From d3922bf62f621a59b5f1e6ddaba23eb877aaade7 Mon Sep 17 00:00:00 2001 From: Gordon Tetlow Date: Wed, 20 May 2026 14:35:50 -0700 Subject: [PATCH] Add EN-26:13 and SA-26:18 through SA-26:24. Approved by: so --- website/data/security/advisories.toml | 28 + website/data/security/errata.toml | 4 + .../FreeBSD-EN-26:13.freebsd-update.asc | 166 ++++++ .../advisories/FreeBSD-SA-26:18.setcred.asc | 170 ++++++ .../advisories/FreeBSD-SA-26:19.file.asc | 173 ++++++ .../advisories/FreeBSD-SA-26:20.fusefs.asc | 164 ++++++ .../advisories/FreeBSD-SA-26:21.ptrace.asc | 163 ++++++ .../advisories/FreeBSD-SA-26:22.libcasper.asc | 155 +++++ .../FreeBSD-SA-26:23.bsdinstall.asc | 155 +++++ .../advisories/FreeBSD-SA-26:24.cap_net.asc | 160 ++++++ .../patches/EN-26:13/freebsd-update.patch | 11 + .../patches/EN-26:13/freebsd-update.patch.asc | 17 + .../patches/SA-26:18/setcred-14.patch | 15 + .../patches/SA-26:18/setcred-14.patch.asc | 17 + .../patches/SA-26:18/setcred-15.patch | 15 + .../patches/SA-26:18/setcred-15.patch.asc | 17 + .../security/patches/SA-26:19/file-14.patch | 203 +++++++ .../patches/SA-26:19/file-14.patch.asc | 17 + .../security/patches/SA-26:19/file-15.patch | 467 +++++++++++++++ .../patches/SA-26:19/file-15.patch.asc | 17 + .../patches/SA-26:20/fusefs-14.3.patch | 146 +++++ .../patches/SA-26:20/fusefs-14.3.patch.asc | 17 + .../patches/SA-26:20/fusefs-14.4.patch | 146 +++++ .../patches/SA-26:20/fusefs-14.4.patch.asc | 17 + .../security/patches/SA-26:20/fusefs-15.patch | 147 +++++ .../patches/SA-26:20/fusefs-15.patch.asc | 17 + .../patches/SA-26:21/ptrace-14.3.patch | 164 ++++++ .../patches/SA-26:21/ptrace-14.3.patch.asc | 17 + .../patches/SA-26:21/ptrace-14.4.patch | 154 +++++ .../patches/SA-26:21/ptrace-14.4.patch.asc | 17 + .../security/patches/SA-26:21/ptrace-15.patch | 154 +++++ .../patches/SA-26:21/ptrace-15.patch.asc | 17 + .../patches/SA-26:22/libcasper-14.patch | 539 ++++++++++++++++++ .../patches/SA-26:22/libcasper-14.patch.asc | 17 + .../patches/SA-26:22/libcasper-15.patch | 538 +++++++++++++++++ .../patches/SA-26:22/libcasper-15.patch.asc | 17 + .../patches/SA-26:23/bsdinstall-14.patch | 102 ++++ .../patches/SA-26:23/bsdinstall-14.patch.asc | 17 + .../patches/SA-26:23/bsdinstall-15.patch | 102 ++++ .../patches/SA-26:23/bsdinstall-15.patch.asc | 17 + .../security/patches/SA-26:24/cap_net.patch | 60 ++ .../patches/SA-26:24/cap_net.patch.asc | 17 + 42 files changed, 4573 insertions(+) create mode 100644 website/static/security/advisories/FreeBSD-EN-26:13.freebsd-update.asc create mode 100644 website/static/security/advisories/FreeBSD-SA-26:18.setcred.asc create mode 100644 website/static/security/advisories/FreeBSD-SA-26:19.file.asc create mode 100644 website/static/security/advisories/FreeBSD-SA-26:20.fusefs.asc create mode 100644 website/static/security/advisories/FreeBSD-SA-26:21.ptrace.asc create mode 100644 website/static/security/advisories/FreeBSD-SA-26:22.libcasper.asc create mode 100644 website/static/security/advisories/FreeBSD-SA-26:23.bsdinstall.asc create mode 100644 website/static/security/advisories/FreeBSD-SA-26:24.cap_net.asc create mode 100644 website/static/security/patches/EN-26:13/freebsd-update.patch create mode 100644 website/static/security/patches/EN-26:13/freebsd-update.patch.asc create mode 100644 website/static/security/patches/SA-26:18/setcred-14.patch create mode 100644 website/static/security/patches/SA-26:18/setcred-14.patch.asc create mode 100644 website/static/security/patches/SA-26:18/setcred-15.patch create mode 100644 website/static/security/patches/SA-26:18/setcred-15.patch.asc create mode 100644 website/static/security/patches/SA-26:19/file-14.patch create mode 100644 website/static/security/patches/SA-26:19/file-14.patch.asc create mode 100644 website/static/security/patches/SA-26:19/file-15.patch create mode 100644 website/static/security/patches/SA-26:19/file-15.patch.asc create mode 100644 website/static/security/patches/SA-26:20/fusefs-14.3.patch create mode 100644 website/static/security/patches/SA-26:20/fusefs-14.3.patch.asc create mode 100644 website/static/security/patches/SA-26:20/fusefs-14.4.patch create mode 100644 website/static/security/patches/SA-26:20/fusefs-14.4.patch.asc create mode 100644 website/static/security/patches/SA-26:20/fusefs-15.patch create mode 100644 website/static/security/patches/SA-26:20/fusefs-15.patch.asc create mode 100644 website/static/security/patches/SA-26:21/ptrace-14.3.patch create mode 100644 website/static/security/patches/SA-26:21/ptrace-14.3.patch.asc create mode 100644 website/static/security/patches/SA-26:21/ptrace-14.4.patch create mode 100644 website/static/security/patches/SA-26:21/ptrace-14.4.patch.asc create mode 100644 website/static/security/patches/SA-26:21/ptrace-15.patch create mode 100644 website/static/security/patches/SA-26:21/ptrace-15.patch.asc create mode 100644 website/static/security/patches/SA-26:22/libcasper-14.patch create mode 100644 website/static/security/patches/SA-26:22/libcasper-14.patch.asc create mode 100644 website/static/security/patches/SA-26:22/libcasper-15.patch create mode 100644 website/static/security/patches/SA-26:22/libcasper-15.patch.asc create mode 100644 website/static/security/patches/SA-26:23/bsdinstall-14.patch create mode 100644 website/static/security/patches/SA-26:23/bsdinstall-14.patch.asc create mode 100644 website/static/security/patches/SA-26:23/bsdinstall-15.patch create mode 100644 website/static/security/patches/SA-26:23/bsdinstall-15.patch.asc create mode 100644 website/static/security/patches/SA-26:24/cap_net.patch create mode 100644 website/static/security/patches/SA-26:24/cap_net.patch.asc diff --git a/website/data/security/advisories.toml b/website/data/security/advisories.toml index 3c30ea9bd5..1a44fe400f 100644 --- a/website/data/security/advisories.toml +++ b/website/data/security/advisories.toml @@ -1,6 +1,34 @@ # Sort advisories by year, month and day # $FreeBSD$ +[[advisories]] +name = "FreeBSD-SA-26:24.cap_net" +date = "2026-05-20" + +[[advisories]] +name = "FreeBSD-SA-26:23.bsdinstall" +date = "2026-05-20" + +[[advisories]] +name = "FreeBSD-SA-26:22.libcasper" +date = "2026-05-20" + +[[advisories]] +name = "FreeBSD-SA-26:21.ptrace" +date = "2026-05-20" + +[[advisories]] +name = "FreeBSD-SA-26:20.fusefs" +date = "2026-05-20" + +[[advisories]] +name = "FreeBSD-SA-26:19.file" +date = "2026-05-20" + +[[advisories]] +name = "FreeBSD-SA-26:18.setcred" +date = "2026-05-20" + [[advisories]] name = "FreeBSD-SA-26:17.libnv" date = "2026-04-29" diff --git a/website/data/security/errata.toml b/website/data/security/errata.toml index 494f54d35d..6cb37b7b15 100644 --- a/website/data/security/errata.toml +++ b/website/data/security/errata.toml @@ -1,6 +1,10 @@ # Sort errata notices by year, month and day # $FreeBSD$ +[[notices]] +name = "FreeBSD-EN-26:13.freebsd-update" +date = "2026-05-20" + [[notices]] name = "FreeBSD-EN-26:12.freebsd-update" date = "2026-05-01" diff --git a/website/static/security/advisories/FreeBSD-EN-26:13.freebsd-update.asc b/website/static/security/advisories/FreeBSD-EN-26:13.freebsd-update.asc new file mode 100644 index 0000000000..6369b75b23 --- /dev/null +++ b/website/static/security/advisories/FreeBSD-EN-26:13.freebsd-update.asc @@ -0,0 +1,166 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-EN-26:13.freebsd-update Errata Notice + The FreeBSD Project + +Topic: freebsd-update attempts to merge a generated file + +Category: core +Module: freebsd-update +Announced: 2026-05-20 +Affects: All supported versions of FreeBSD. +Corrected: 2026-05-19 13:59:37 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:27 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-19 13:59:57 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:39:53 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:31 UTC (releng/14.3, 14.3-RELEASE-p14) + +For general information regarding FreeBSD Errata Notices and Security +Advisories, including descriptions of the fields above, security +branches, and the following sections, please visit +. + +I. Background + +The freebsd-update utility is used both to apply binary updates for security +advisories and errata notices, and to upgrade from one FreeBSD release to +another. + +In the latter scenario, when it detects local changes to a configuration file +which is affected by the upgrade, freebsd-update will perform a three-way +merge and prompt the user to manually resolve any conflicts between local and +incoming changes. + +The certctl utility has been used since FreeBSD 12.0 to manage a hashed +directory of root certificates for use when validating TLS server +certificates. Since FreeBSD 15.0, certctl also maintains a bundle for the +benefit of applications which either do not support the hashed directory +format or need to preload the trust store prior to entering capability mode, +a chroot, or similar. + +II. Problem Description + +When upgrading from FreeBSD 15.0 to FreeBSD 15.1, freebsd-update incorrectly +treats the certificate bundle /etc/ssl/cert.pem as a configuration file. In +most cases, the three-way merge results in conflicts which the user is then +asked to resolve. The bundle is not human-readable, and merging it serves no +purpose since freebsd-update regenerates the entire certificate store at the +end of the upgrade. + +When upgrading from an older FreeBSD release to FreeBSD 15.0 or 15.1, if +/etc/ssl/cert.pem is present (e.g. as provided by the ETCSYMLINK option of +the security/ca_root_nss port, or manually created by an administrator), +freebsd-update will emit a non-fatal error message and pause until the user +acknowledges the message. + +III. Impact + +Users upgrading from 15.0 to 15.1 may be presented with one or more merge +conflicts in thousands of lines of Base64-encoded ASN.1 data. + +Users upgrading from older releases to 15.0 or 15.1 may encounter a non-fatal +error message with no clear resolution, reducing user confidence in the +upgrade process. + +IV. Workaround + +If prompted to resolve conflicts, exit the editor and force freebsd-update +to accept the unmerged file by typing "ACCEPT" (all upper-case, without the +quotes). The bundle will be regenerated at the end of the upgrade process +and the system will be fully functional. + +V. Solution + +Upgrade your system to a supported FreeBSD stable or release / security +branch (releng) dated after the correction date. + +Perform one of the following: + +1) To update your system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base + +2) To update your system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms +which were not installed using base system packages can be updated via the +freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +3) To update your system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +# fetch https://security.FreeBSD.org/patches/EN-26:13/freebsd-update.patch +# fetch https://security.FreeBSD.org/patches/EN-26:13/freebsd-update.patch.asc +# gpg --verify freebsd-update.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ b97f143b6ca9 stable/15-n283610 +releng/15.0/ 2709755d39f5 releng/15.0-n281037 +stable/14/ 7d9c1d3895b3 stable/14-n274144 +releng/14.4/ 081a9e933033 releng/14.4-n273701 +releng/14.3/ a1b3818746e3 releng/14.3-n271501 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKGEbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvgJQP/RY20Qr2cM3gsEsVSt5+ +xXS/yCXu+IZq/ALOzw4RvqzdqvVzlA3U2VgSXpucnkrV0rABc7yxLbmvTVj6GOG7 +yvKXSmV58akQoUbnOtwHZF4x+4A9+Y3BzGIWUrzh014ll4MyhGw/4ekFiu36J0Mg +QBDPkAy+3jrCTE3i2aAF1w1gLYdyIfDwGYQHqpPCsMmGhHuleogGqmhc5pH2J30g +fPRLe8a4njizX5aT15TZvo6U5sQC6tll4DBUqTWh6k49XxSELKQwYgXhqhespI++ +yZ327VPwkVgaYI0C96LCV5SVB811BvFAKXKzjItKOWpJyg6HpB8hiSEubqlWW7zX +vltqLyf8qe15wZPvrs1kgX2kH9ZJXYwJ9W5z5kY8sk/DCYos+bxtEQ47CU5u6/nF +h01i3mAwOdh0/br7Y7hRS4eekNg9XUpu9dakJdhpJjbRylS6I6wK/C/f89L+qmgP +4jq20TCFHQ2riVHxhOG3nSGkP+5CsIUnjg94x/EKK9xA9DZb0D5/Vy+hQYhJ5qza +q5TKkv72vb32LKFKvzXXJbCrRlJr6bmCOMXYRGZwDzKzfd5jrVwzlIfooiaQ28bj +g2egNBCe69H0SboydGi6J4yciBn3TeBHilfPuDLxs2eRZmYFd4wVD4wnigsysL2J +JETeDqmCxDDqbzhIYf3XL7Pt +=zyAg +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:18.setcred.asc b/website/static/security/advisories/FreeBSD-SA-26:18.setcred.asc new file mode 100644 index 0000000000..2b0e4d6640 --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:18.setcred.asc @@ -0,0 +1,170 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:18.setcred Security Advisory + The FreeBSD Project + +Topic: Stack buffer overflow via setcred(2) + +Category: core +Module: setcred +Announced: 2026-05-20 +Credits: Ryan of Calif.io +Credits: Przemyslaw Frasunek +Affects: All supported versions of FreeBSD. +Corrected: 2026-01-06 13:34:30 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:28 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-20 19:37:54 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:39:54 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:32 UTC (releng/14.3, 14.3-RELEASE-p14) +CVE Name: CVE-2026-45250 + +This vulnerability was independently reported by multiple parties prior to +publication. + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +System calls are the programmatic interface through which user-space +processes request services from the operating system kernel, providing a +controlled boundary between unprivileged application code and privileged +kernel operations. + +setcred(2) is a system call which enables a privileged process to atomically +set its full credential set, including the real, effective, and saved user +and group identifiers, as well as the list of supplementary groups. It is +intended for use by programs such as login(1) and PAM(3)-aware authentication +frameworks that must transition a process into a target user context in a +single, race-free operation, replacing the need for multiple discrete calls +to setuid(2), setgid(2), and setgroups(2). + +II. Problem Description + +The setcred(2) system call is only available to privileged users. However, +before the privilege level of the caller is checked, the user-supplied list +of supplementary groups is copied into a fixed-size kernel stack buffer +without first validating its length. If the supplied list exceeds the +capacity of that buffer, a stack buffer overflow occurs. + +III. Impact + +Because the bounds check on the supplementary groups list occurs after the +kernel stack buffer has already been written, an unprivileged local user may +trigger the overflow without holding any special privilege. Successful +exploitation may allow an attacker to execute arbitrary code in the context +of the kernel, allowing an unprivileged local user to gain elevated +privileges on the affected system. + +IV. Workaround + +No workaround is available. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date, +and reboot the system. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms +which were not installed using base system packages can be updated via the +freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 15.x] +# fetch https://security.FreeBSD.org/patches/SA-26:18/setcred-15.patch +# fetch https://security.FreeBSD.org/patches/SA-26:18/setcred-15.patch.asc +# gpg --verify setcred-15.patch.asc + +[FreeBSD 14.x] +# fetch https://security.FreeBSD.org/patches/SA-26:18/setcred-14.patch +# fetch https://security.FreeBSD.org/patches/SA-26:18/setcred-14.patch.asc +# gpg --verify setcred-14.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ b6cba9028457 stable/15-n281743 +releng/15.0/ d98c0a494a42 releng/15.0-n281038 +stable/14/ 8eb0bbbd2e46 stable/14-n274162 +releng/14.4/ 34da5845b8d4 releng/14.4-n273702 +releng/14.3/ bfff5c180193 releng/14.3-n271502 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKGobFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvSpsP/38o7yHdNEMNMPPOBtKZ +2dn/vmcOo1srkhUx0kl2EVBzirSDsTVkWfUq1Txg5JA7/pG3On/YiaAmUMi9jHqy +q0tgkyO/scKGWNDYmFIA9QAXAwwSUZnT+eEwt3IawOzquezD/qr++CCimntSUzsu +IP3oMFYaw9JvMF6Z6tTfcYYA02CF7nRrtIJtrxfWkgyDoMoikHsNW4o2LXJTz4bV +2uk7BuQKbDc3gxoEBYd0bulXBa9DHsrfS59eEnbb8txrBjt21aQGjBY8SJSoFyYh +yZixmadpZ9J4oTBc03hOO2Z2BN5f/QficGIU4t0wj0A8EcsrspFMDRj2xd/5zi86 +VLqiQf6WJbgVyytUe5aYbBPC6eH2TRnMWaOERbocNS6xQKcYpZYqwnVZ77n6tPb4 +wKQd+qKYM74lf0BPCBc60h7yo9e6Qd8puGolyL05qdZVB+c3m0qB000gsyNFytFs +kQSovaXFf4r0DCEuBixE/Ic5ADwl7A4pCIxqwWwJlnrj77XCobNEQJtajkrapXsU +MSLQ20RuRiVNesgyjP9dZCk8enuOl96TwrvdkyqvSJgb0Gw3XEeyCWT4dAE+Fh3A +n8RhQeY6YWWk+DOiuw5Q5v2PyoBNoV8jV2AjeXzhIOQsyWGeSYQ2GeFu6PW3UyzQ +olNjUPjprNwteRkUuGHmE3zQ +=6aG+ +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:19.file.asc b/website/static/security/advisories/FreeBSD-SA-26:19.file.asc new file mode 100644 index 0000000000..ccac947f7d --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:19.file.asc @@ -0,0 +1,173 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:19.file Security Advisory + The FreeBSD Project + +Topic: Kernel use-after-free via file descriptor syscalls + +Category: core +Module: file +Announced: 2026-05-20 +Credits: 75Acol, Lexpl0it, fcgboy, and robinzeng2015 +Credits: Ryan at Calif.io +Affects: All supported versions of FreeBSD. +Corrected: 2026-05-20 19:36:37 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:31 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-20 19:37:57 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:39:57 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:34 UTC (releng/14.3, 14.3-RELEASE-p14) +CVE Name: CVE-2026-45251 + +This vulnerability was independently reported by multiple parties prior to +publication. The reporters' findings prompted a broader review by the +FreeBSD Security Team, which identified additional occurrences of the same +issue in related code. All known exploitable instances are corrected by this +update. + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +FreeBSD implements a number of file descriptor types. Traditionally file +descriptors are used to perform file or network I/O, but other variants +exist such as process descriptors, which enable operations on a particular +process. + +The select(2) and poll(2) system calls allow applications to wait for events +related to the object to which a file descriptor refers. These system calls +are implemented for many different file descriptor types. For instance, a +process descriptor may be used with either system call to wait for the target +process to exit. + +II. Problem Description + +A file descriptor can be closed while a thread is blocked in a poll(2) or +select(2) call waiting for that descriptor. Because the blocked thread does +not hold a reference to the underlying object, this closure may result in the +object being freed while the thread remains blocked. In this situation, the +kernel must remove the blocked thread from the per-object wait queue prior to +freeing the object. + +In the case of some file descriptor types, the kernel failed to unlink +blocked threads from the object before freeing it. When the blocked thread +is subsequently woken, it accesses memory that has already been freed +resulting in a use-after-free vulnerability. + +III. Impact + +The use-after-free vulnerability may be triggered by an unprivileged local +user and can be exploited to obtain superuser privileges. + +IV. Workaround + +No workaround is available. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date, and +reboot the system. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms +which were not installed using base system packages can be updated via the +freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 15.x] +# fetch https://security.FreeBSD.org/patches/SA-26:19/file-15.patch +# fetch https://security.FreeBSD.org/patches/SA-26:19/file-15.patch.asc +# gpg --verify file-15.patch.asc + +[FreeBSD 14.x] +# fetch https://security.FreeBSD.org/patches/SA-26:19/file-14.patch +# fetch https://security.FreeBSD.org/patches/SA-26:19/file-14.patch.asc +# gpg --verify file-14.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ 53a78e582a6f stable/15-n283641 +releng/15.0/ af79f4148450 releng/15.0-n281041 +stable/14/ b90b25c3779e stable/14-n274164 +releng/14.4/ 8d8694c224e2 releng/14.4-n273704 +releng/14.3/ 659818009d15 releng/14.3-n271504 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKG4bFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvA78P/iRlQXxVUpth5tRn2FiC +lseIWOmh3DVI1OjwFQ30VydwnA5rlOqPPTpF2hsT0ee3ExS6pUKITi3735BmkPvT +KvnOKkY9A2DdzXJQ9eZvrVJRN1/VlKx8Us1VmWWRxPHghmcqqTY0wN2lFcsyqcpN +6Wdi51z+X5sLWZZsLsvqAskWiCNqUzBSSWqCTLEW0tBD9AoW2BPQcpAeEmx4MDch +Hk2/pecoUL2T/hu3bjo60CTp3R7E4gPt9wM5Ejf32vwsW0sTNkTmy7HbZCNmYHZw +R764O4i4poDzccTiXxuhXdrIDXmRQwTyB9d6S12OmP8ec8dAQzm9p5xl4HoHhOho +9zTMCiLoU+ApN1H+bXqN9JvmZ9hfxGqdPaJgZRkQ11xRHg8tz48SigON/vxlbYff +ln9EJ+NGEcskrbUAG8cUCJ3/a8A7xLQo07TpvyddeUc6ufk+nFEBzNS3rpaFNy5y +GqFIOzqISRSsE1tf6rrItULQEKWtOMUYvAbrcLRwPAQ1cav+sOv9YlfpW36s1+mc +CyuXDh3pbN5biajjImGO1CYN92mq/Jfz/cRnvQub+78T+4w6yAxj53fBNg97tIOI +b7EISAnbgGj5akQRGJXJ84iuYij9xTPEOCSbfgAqsWXKz6l/bgSoVUhq/e0/dXKA +sr+3pjhi5P7N66SvO+7iEpYI +=iM1b +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:20.fusefs.asc b/website/static/security/advisories/FreeBSD-SA-26:20.fusefs.asc new file mode 100644 index 0000000000..d6516c54e6 --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:20.fusefs.asc @@ -0,0 +1,164 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:20.fusefs Security Advisory + The FreeBSD Project + +Topic: Heap overflow in FUSE_LISTXATTR + +Category: core +Module: fusefs +Announced: 2026-05-20 +Credits: Joshua Rogers of AISLE Research Team +Affects: All supported versions of FreeBSD. +Corrected: 2026-05-20 19:36:38 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:32 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-20 19:37:58 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:39:58 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:36 UTC (releng/14.3, 14.3-RELEASE-p14) +CVE Name: CVE-2026-45252 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +The fusefs file system delegates file system operations to a userspace +daemon. This daemon ordinarily requires root privileges to operate. When +the "vfs.usermount" sysctl is set to 1 (not the default), unprivileged users +are permitted to run such daemons and mount fusefs file systems. + +II. Problem Description + +When a fusefs file system implements extended attributes, the kernel may send +a FUSE_LISTXATTR message to the userspace daemon to retrieve the list of extended +attributes for a given file. The FUSE protocol requires the daemon to return +a packed list of NUL-terminated strings. The fusefs kernel module calls +strlen() on this daemon-supplied buffer without first verifying that the +entire list is NUL-terminated. + +III. Impact + +If a malicious daemon sends a non-NUL-terminated list, the fusefs kernel +module may read beyond the end of one heap-allocated buffer and potentially +write beyond the end of a second buffer. A malicious daemon could disclose +up to 253 bytes of kernel heap memory, or it could inject up to 250 +attacker-controlled bytes into unallocated kernel heap space. + +IV. Workaround + +No workaround is available, but systems that do not load the fusefs kernel +module or set vfs.usermount=1 are unaffected. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date, and +reboot the system. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms +which were not installed using base system packages can be updated via the +freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 15.0] +# fetch https://security.FreeBSD.org/patches/SA-26:20/fusefs-15.patch +# fetch https://security.FreeBSD.org/patches/SA-26:20/fusefs-15.patch.asc +# gpg --verify fusefs-15.patch.asc + +[FreeBSD 14.4] +# fetch https://security.FreeBSD.org/patches/SA-26:20/fusefs-14.4.patch +# fetch https://security.FreeBSD.org/patches/SA-26:20/fusefs-14.4.patch.asc +# gpg --verify fusefs-14.4.patch.asc + +[FreeBSD 14.3] +# fetch https://security.FreeBSD.org/patches/SA-26:20/fusefs-14.3.patch +# fetch https://security.FreeBSD.org/patches/SA-26:20/fusefs-14.3.patch.asc +# gpg --verify fusefs-14.3.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ df3f3fa82775 stable/15-n283642 +releng/15.0/ 0dd8b983db3c releng/15.0-n281042 +stable/14/ 25148c51c8c6 stable/14-n274165 +releng/14.4/ 6a299460f159 releng/14.4-n273705 +releng/14.3/ 53f3bf4ee1ce releng/14.3-n271505 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHIbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvobkP/R3O3bwsnJkhG1NQ6pKh +UFcwpZ8TSAqtccHZRQz2zoKTqu/EeClT7Bdgw/Qa8gbZ7IfZgS8AJaR7e4fgpE96 +AhHU6cbyZrpwvWUatIKgX57032+M1ioMiz9g0KbGg4W4WKe/QHj4yt45F7qRfLNb +BD7Qp7E0XtV+UrNXkhOQQmHyVTpB85tK/e5Yc+vcSgAQ3LWrzwO4zED4f78e3faw +oiLm1oE/Vx0jfrRKsnCECdJS532xlfH6iJ2/2ZXfUthGQmZQe34wOMwYS0EcaGZV +TQoLwsg5qLj4hJOGMCZk4X4TjrkoQquWdsAQetB8tqXIyw7QEgbMIIbhS3mQZ5CW +aEq3wbYMowxCMb/6Dd/R56wDqyGI2Z6GHmUT58M0OSIIISfsD+UHOCW2lrQQ5zrI +o1O/IFAvqsmCN6JQzFgC3KC8BLLZWzxf5Bun6yOls/YA31zOXAen0isnbOvVnGot +42Dy65fENCUQMt+p3eDDLQzxDhlqGAGbiqysBmxwTA5Wqc4furv7O0wmBPwOOGeH +NqlKYsqO9u4kEW2lTCPs7R5+wsc+EACc07kikDQgp1m59JlkMfmXU4Kbcgw9r4GR +9OWtidfTCDGmt9mXzJVKaBurgJ1iqsBfzzLamWo0iDpUMgUP7VA9jVjVbUmtjH1V +qAWdXCXwrbOr+eA50IIPxkal +=HzW3 +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:21.ptrace.asc b/website/static/security/advisories/FreeBSD-SA-26:21.ptrace.asc new file mode 100644 index 0000000000..187aabe5cb --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:21.ptrace.asc @@ -0,0 +1,163 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:21.ptrace Security Advisory + The FreeBSD Project + +Topic: Missing validation in ptrace(PT_SC_REMOTE) + +Category: core +Module: ptrace +Announced: 2026-05-20 +Credits: Yuxiang Yang, Yizhou Zhao, Ao Wang, Xuewei Feng, Qi Li, + and Ke Xu from Tsinghua University using GLM-5.1 from Z.ai +Credits: Ryan at Calif.io +Affects: All supported versions of FreeBSD. +Corrected: 2026-05-20 19:36:40 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:34 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-20 19:37:59 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:39:59 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:37 UTC (releng/14.3, 14.3-RELEASE-p14) +CVE Name: CVE-2026-45253 + +This vulnerability was independently reported by multiple parties prior to +publication. + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +The ptrace(2) system call provides facilities for a debugger to control the +execution of a target process and to obtain status information about it. +Among other capabilities, it permits a debugger to execute arbitrary system +calls in the target process via the PT_SC_REMOTE operation. + +II. Problem Description + +ptrace(PT_SC_REMOTE) failed to properly validate parameters for the syscall(2) +and __syscall(2) meta-system calls. As a result, a user with the ability to +debug a process may trigger arbitrary code execution in the kernel, even if +the target process has no special privileges. + +III. Impact + +The missing validation allows an unprivileged local user to escalate +privileges, potentially gaining full control of the affected system. + +IV. Workaround + +No workaround is available. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date, and +reboot the system. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms +which were not installed using base system packages can be updated via the +freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 15.0] +# fetch https://security.FreeBSD.org/patches/SA-26:21/ptrace-15.patch +# fetch https://security.FreeBSD.org/patches/SA-26:21/ptrace-15.patch.asc +# gpg --verify ptrace-15.patch.asc + +[FreeBSD 14.4] +# fetch https://security.FreeBSD.org/patches/SA-26:21/ptrace-14.4.patch +# fetch https://security.FreeBSD.org/patches/SA-26:21/ptrace-14.4.patch.asc +# gpg --verify ptrace-14.4.patch.asc + +[FreeBSD 14.3] +# fetch https://security.FreeBSD.org/patches/SA-26:21/ptrace-14.3.patch +# fetch https://security.FreeBSD.org/patches/SA-26:21/ptrace-14.3.patch.asc +# gpg --verify ptrace-14.3.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ 3b4afab9add2 stable/15-n283643 +releng/15.0/ fd24dd0b38a8 releng/15.0-n281043 +stable/14/ fac902a3e039 stable/14-n274166 +releng/14.4/ c21d23f0f8be releng/14.4-n273706 +releng/14.3/ 45bd421661c4 releng/14.3-n271506 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHcbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvLd0QAOQGyaTmlTQJTS+EIPMU ++poVU59Fe4L+/+H8LSibnCPBbycH1bv6m9e906s/za0IBLGVq7PhY0U1YtPO5++J +A86nLzgqk4hEU5RWmA3+dnLYrIxOf3fVvSev/XAZe/1eWwcljYRCtqLV+IBmyxeZ +amfYoXliUTuZHO+r+88HVAgDy6efZ3IlnHF9iMlpsF0IFezpgFh4E6tiJk9/pMlz +wuXpHCm34rEjy6bvQaDP9G1zXGszrEatT25d9rKZnHscZCQuRgtpLaOVCuH8oDca ++1PFTfTNJnepH9Ir1nSaYLViZdHfuDK40CafZm54q4669AramrySoxNJlnNHOiMK +DN4aqxMfW5xCEEK+fIJYqTyW2L3WzRJ8tm3bF/zzsMYTsNmclcklzmuMNqsGQls1 +TGIhb+J+e0vkdZOpuJaT65pmGaF2dJeBvwNsIMJgtY3yotUPbDFD1ALNVUwIkKYh +m68XK0Ykw93ySLjbORUVFLP5nv5PvYtubAy37q5tskN6hXLlyX5a0QxIL5T5u0jx +hwDnyl4UAHGmkBM8U0CnaQbixP/yV0p5q+3NtpBurHB74tov593/U1eroydDywRl +Mw2R3k7AFIC5CszwMA6J0l3W2tLq/j7tcTQ/8CNgPpP/TPVntQxQShxB93F+/MdX +n9D4phEb7cKk4Y9QIBKkdbYZ +=egz5 +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:22.libcasper.asc b/website/static/security/advisories/FreeBSD-SA-26:22.libcasper.asc new file mode 100644 index 0000000000..996f09c663 --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:22.libcasper.asc @@ -0,0 +1,155 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:22.libcasper Security Advisory + The FreeBSD Project + +Topic: select(2) file descriptor set overflow causes stack overflow + +Category: core +Module: libcasper +Announced: 2026-05-20 +Credits: Joshua Rogers of AISLE Research Team +Affects: All supported versions of FreeBSD. +Corrected: 2026-05-20 19:36:41 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:35 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-20 19:38:00 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:40:00 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:38 UTC (releng/14.3, 14.3-RELEASE-p14) +CVE Name: CVE-2026-39461 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +libcasper(3) allows Capsicum-sandboxed applications to access system +interfaces that are otherwise unavailable within the sandbox. It is +used by numerous programs in the base system. + +II. Problem Description + +libcasper(3) communicates with helper processes via UNIX domain sockets, and +uses the select(2) system call to wait for data to become available. +However, it does not verify that its socket descriptor fits within +select(2)'s descriptor set size limit of FD_SETSIZE (1024). + +III. Impact + +An attacker able to cause an application using libcasper(3) to allocate large +file descriptors, e.g., by opening many descriptors and executing a program +which is not careful to close them upon startup, may trigger stack +corruption. If the target application runs with setuid root privileges, this +could be used to escalate local privileges. + +IV. Workaround + +No workaround is available. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms, +or the i386 platform on FreeBSD 13, which were not installed using base +system packages, can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 15.x] +# fetch https://security.FreeBSD.org/patches/SA-26:22/libcasper-15.patch +# fetch https://security.FreeBSD.org/patches/SA-26:22/libcasper-15.patch.asc +# gpg --verify libcasper-15.patch.asc + +[FreeBSD 14.x] +# fetch https://security.FreeBSD.org/patches/SA-26:22/libcasper-14.patch +# fetch https://security.FreeBSD.org/patches/SA-26:22/libcasper-14.patch.asc +# gpg --verify libcasper-14.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +Restart the applicable daemons, or reboot the system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ 23929d729d1a stable/15-n283644 +releng/15.0/ e22f3f55c360 releng/15.0-n281044 +stable/14/ 9e74d5e2e5e4 stable/14-n274167 +releng/14.4/ ae34dd1a391f releng/14.4-n273707 +releng/14.3/ cbec31838173 releng/14.3-n271507 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHsbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrveQAP/iyv1O1XI6tSrRictadU +9tBJFE5WlWGPrB8ID/12nLsKaTM5hzbA1G+v8c3So3FaSEl+m7D8BTri4X0XPibQ +5Pp4v67MO+yqsNxOjwyqAizOnD5bk/sEUuBV5JijZuqsAiEWFw5l0dKDU83zt3vu +hyk8/eeKuIxEwDiWQoeE32RM3BupY1ClWp46kiSjvOVzUK04miHQjgFFnVqkBuI7 +DeanTjzCw3g+RQNTRKVGE2LYRLFHka6m4Z5RYT7beFOLdlD58T7lvQLl3l3f2QSR +hXcq5RxAhf4omPkm432fIdd4nev4gti3rxJC76NM2rIHGeSlRd4O7MHreNwNkU2O +8Rv8IWMCM20zZCtbov7q8XbTqKp8JXSJ/8g15iZuZ4wk+THnpRy7dsRe5eYQvVbB +J/zBKB9xMXGp69+88uZHDsSSoS841pkZ61+MlxeK4xC3MO6tlTO0Hannhmy8WCb4 +U5GimvX3EcvhGeBWRvPTdPJY9EcrDPDU2djaiFzPZZ7rrUjR8YJ685fyj161nnb+ +ibubcwiz7ygQu8b9T0rc1AV5ZTAC/QAlRarDpRNx2Ynh/FlZ89n+N5LnSHwGXc/v +/P+ob/5AqdLfyofw5pcx/FVuAiK4bjqDGGYuZw1tplg/L7AV3k87zIMYdCgr3e95 +PyQCsFAG014gMVETPGHKm6/7 +=ypPx +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:23.bsdinstall.asc b/website/static/security/advisories/FreeBSD-SA-26:23.bsdinstall.asc new file mode 100644 index 0000000000..4da2ce8d17 --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:23.bsdinstall.asc @@ -0,0 +1,155 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:23.bsdinstall Security Advisory + The FreeBSD Project + +Topic: Remote code execution via installer Wi-Fi access point scans + +Category: core +Module: bsdinstall +Announced: 2026-05-20 +Credits: Austin Ralls +Affects: All supported versions of FreeBSD. +Corrected: 2026-05-20 19:36:43 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:37 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-20 19:38:03 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:40:02 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:40 UTC (releng/14.3, 14.3-RELEASE-p14) +CVE Name: CVE-2026-45255 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +bsdinstall and bsdconfig are utilities that provide an interactive +configuration mechanism for FreeBSD. Among other functionality, they can be +used to configure FreeBSD to automatically join a Wi-Fi network. + +II. Problem Description + +When bsdinstall or bsdconfig are prompted to scan for nearby Wi-Fi networks, +they build up a list of network names and use bsddialog(1) to prompt the user +to select a network. This is implemented using a shell script, and the code +which handled network names was not careful to prevent expansion by the +shell. As a result, a suitably crafted network name can be used to execute +commands via a subshell. + +III. Impact + +The problem can be exploited to execute code as root on the system running +bsdinstall or bsdconfig. The attacker would need to create an access point +with a specially crafted name and be within range of a Wi-Fi scan. Note that +bsdinstall and bsdconfig are vulnerable as soon as the user prompts them to +scan for nearby networks; they do not need to actually select the malicious +network. + +IV. Workaround + +Avoid using bsdinstall or bsdconfig to scan for Wi-Fi networks, and instead +configure Wi-Fi manually. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms +which were not installed using base system packages can be updated via the +freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 15.x] +# fetch https://security.FreeBSD.org/patches/SA-26:23/bsdinstall-15.patch +# fetch https://security.FreeBSD.org/patches/SA-26:23/bsdinstall-15.patch.asc +# gpg --verify bsdinstall-15.patch.asc + +[FreeBSD 14.x] +# fetch https://security.FreeBSD.org/patches/SA-26:23/bsdinstall-14.patch +# fetch https://security.FreeBSD.org/patches/SA-26:23/bsdinstall-14.patch.asc +# gpg --verify bsdinstall-14.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ 6f5674b97fd6 stable/15-n283646 +releng/15.0/ b89f48ade920 releng/15.0-n281046 +stable/14/ f15df0adbcd2 stable/14-n274170 +releng/14.4/ dd50cc216e4d releng/14.4-n273709 +releng/14.3/ 9cb0be8381f7 releng/14.3-n271509 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKH8bFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvTloP/0369bPHpZf0yt9C2VEk +NyOeFq+58zQGrz+RRrXA6Vg2xNdaD3fcjpVzAoqzscuE2T7VqZkpi6cS+cbzEE40 +NXX+d7qPgd5udqJR4gL8+90KWj7yQ9Wl0tnbV8wTLE6km/Dma+MXuDJrIqUl8Tsb +q9hXGPfeymptS2vkR1Nj3VxEhDg0CCQz3bGD1sln7Oj63amX8HkHO9MwW8zHTyGj +pcMqEF2sN3Zz0WyyaBf5XS9G0EP0BpicDIcF1NiwYbPi0rlA/nU/zjACfao7lEJk +/XCq/iBKQsOiicvNGhoms/ku4YLNQv/L40FSJFNm8wmUsJD4fh6ll2+5Rm88666e +gJUcBiLEzlKFogiel4JLqXMBaAZseV6Py8B+puAYh2eFCa/3aF6w2QppMj4jIHCL +xEC/XUoBXN+34riiOCkPuSPqmgktvw7oZOBuk5DpV6qt7kdkInZ9i4HQnR13dhlF +vLW88oyuO+2dUn+LiLaHi6f7gxkHcgyOOa/N60D95E9+d6Aop9otyMxNRFbSiQ7I +x13B4j9ONtdAwL0uYJ+HPNHIfGTBHtpFzt62JfKdWqbSa5oVQrU5aq6wfMjMVupI +sYCq+XNTN0MVr4iHowDPqwuEi0+RBoPOQPIXFZRfJr4uTdeim5dzX6fjfe95KIps +3nJWEVEXVF/FiFWL3C+Lk3Cv +=Y3q0 +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:24.cap_net.asc b/website/static/security/advisories/FreeBSD-SA-26:24.cap_net.asc new file mode 100644 index 0000000000..8d7866d034 --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:24.cap_net.asc @@ -0,0 +1,160 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:24.cap_net Security Advisory + The FreeBSD Project + +Topic: Incorrect libcap_net limitation list manipulation + +Category: core +Module: libcap_net +Announced: 2026-05-20 +Credits: Joshua Rogers of AISLE Research Team +Affects: All supported versions of FreeBSD. +Corrected: 2026-05-19 23:03:59 UTC (stable/15, 15.0-STABLE) + 2026-05-20 19:39:38 UTC (releng/15.0, 15.0-RELEASE-p9) + 2026-05-19 23:04:13 UTC (stable/14, 14.4-STABLE) + 2026-05-20 19:40:03 UTC (releng/14.4, 14.4-RELEASE-p5) + 2026-05-20 19:40:41 UTC (releng/14.3, 14.3-RELEASE-p14) +CVE Name: CVE-2026-45254 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +libcasper(3) allows Capsicum-sandboxed applications to define and use system +interfaces which are otherwise not available in a capability sandbox, through +implementing special services. One of these services, libcap_net, enables +networking capabilities within the restricted environment. + +Casper services allow the application to define fine-grained limits on each +operation handled by the service. Each service maintains a specific list of +permitted operations. Certain operations can be further restricted by +specifying an explicit list of allowed names. For example, libcap_net allows +the application to limit the addresses to which the application may bind or +connect. If it attempts to use libcap_net to bind or connect to addresses +outside the allowed list, the operation will fail. + +In keeping with Capsicum's capability model, once a set of limits is applied, +subsequent adjustments may only narrow the set of permitted operations to a +subset of the current one. + +II. Problem Description + +In the case of the cap_net service, when a key present in the old limit was +omitted from the new limit, the missing key was treated as "allow any" +instead of being rejected. + +III. Impact + +In certain scenarios, an application that had previously restricted a subset +of network operations could ask for a new limit that extended the permissions +of the process. + +IV. Workaround + +No workaround is available. Note that no FreeBSD base system software is +affected by this issue. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms +which were not installed using base system packages can be updated via the +freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +# fetch https://security.FreeBSD.org/patches/SA-26:24/cap_net.patch +# fetch https://security.FreeBSD.org/patches/SA-26:24/cap_net.patch.asc +# gpg --verify cap_net.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile the operating system using buildworld and installworld as +described in . + +Restart all daemons that use the library, or reboot the system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ 7eb3fd691d64 stable/15-n283630 +releng/15.0/ f69df16fcc20 releng/15.0-n281047 +stable/14/ b79faca1c596 stable/14-n274156 +releng/14.4/ f977328c7277 releng/14.4-n273710 +releng/14.3/ b3baecf08405 releng/14.3-n271510 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQJPBAEBCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKIMbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvCBgQANie9vi7Gg5seuAZqAjF +JozB+Gs6qaHzHu1CcsDSjbm3Sx5l7p5vbdTR/qsdj9WJbORSI7l5CgB175s8Dbcn +PrBYNHCa/+lQEMjRkxGrQF9+qr0W48jrARBgauqqzrYTXHAtLGG1e4S6s83w0IJP +wZ/3zoEOb7dzcfvxOXSSa+BGcIirmzctg886IH1+EvQKluARzAMxFhNTMwbMMRe7 +k0duMSU7KIlh2C23aMLUPlj1Su52gbiSMz3fDgl4i1cbo3xnQPQNWZnnlu8u+ZCB +2pXQoNag7AHTzaOMvOtIyYKXfR9OLdPa4Ii6D38s6WTUb5q0GmS6G7ISYOAnTM+3 +TEvH2uhOpq8bySWb6TEx1ppedyIwZ+awocQ9XUeDvarJGCCTlBQ3kV08TMKKZxPA +/DOWHJ9KSgKku9sKaLpbTNacpDmkipIEgKZdicifA9KpvH7frBlvwVsTzERVm/qy +SVySVCqSE5fpYo6FN3Mo0GfN3EnBU2aYpPRx3RHvzKHTbQSU7iaNOu+Iki6GJuiH +HTQ6oaWHmNAkotNN5tAdmDXrm9wnMncCbMT1JHrtEDanJWgKWEovhK1mw6LhHlg1 +K+bvyTB6LyZYnOZXhb9540tXfyrmjdTzM/jNMZL1Z5AYjy1FfDdTxH460gIsy6PU +f4TRsebl2L+EThYrx6pjoj5D +=K5/f +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/EN-26:13/freebsd-update.patch b/website/static/security/patches/EN-26:13/freebsd-update.patch new file mode 100644 index 0000000000..317583071a --- /dev/null +++ b/website/static/security/patches/EN-26:13/freebsd-update.patch @@ -0,0 +1,11 @@ +--- usr.sbin/freebsd-update/freebsd-update.sh.orig ++++ usr.sbin/freebsd-update/freebsd-update.sh +@@ -2576,7 +2576,7 @@ + + # Some files need special treatment. + case ${F} in +- /etc/spwd.db | /etc/pwd.db | /etc/login.conf.db) ++ /etc/spwd.db | /etc/pwd.db | /etc/login.conf.db | /etc/ssl/cert.pem) + # Don't merge these -- we're rebuild them + # after updates are installed. + cp merge/old/${F} merge/new/${F} diff --git a/website/static/security/patches/EN-26:13/freebsd-update.patch.asc b/website/static/security/patches/EN-26:13/freebsd-update.patch.asc new file mode 100644 index 0000000000..af91392b93 --- /dev/null +++ b/website/static/security/patches/EN-26:13/freebsd-update.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKGkbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvFPUP/RdMliKc4ZoFZcXJtLCH +fUAAsi+ypPYpy+/8dJs8QGmkUD/ypzcZCHp6drAV6dnjkJL9J3HexUnFPnMAErOV +yQYqyL8bZfxhbbvlYEPT8eFmpARdMMT648lUmtiR8Eoh/YcX1hjsVRDmMNMdq9hn +XCwffFhvgtjxvaVePJn8K6WnN+Yze4JxO5qSFxcAPP9MlKID0Y+BMlXnXfaJn+c/ +F/GCjJLObn0N58BKwB5gyOAGZVP8HyUvOB+LSL/Sw6c+OcOCPnMT6BQeN2dzlPlo +Pw3ccZu81ZNOmYIR+f3TTSCngTGgmKQafvO7TQUdKbpaDMtwL6VeZqarlAgascNb +UccstnGYZ4RnlCsjdh7+vv4CgGVQzDrvVPiQFxYHZpzyc1sXht8Ecl4iImepsBmN +O7k0SlDGoBJ+0GLJDIDa5FMTPUuroyLYTrBDu0MRvLrhrfYrOlnakFkNb/8V0uYC +7PmBtc78u/KDtvzvBwYy8ycufkweRsMuNqCDSXI7LvS304iOCLfv/U0F+HWDJ/e2 +bFtiK8pcmXE44YP0xmU0YBPadQSOO4yrJV5WDuH8dTzZNE752oeGtlSjKpR1zwsC +ahT6tGsCdCy2+v6Oy/5ekGD7EofIZ9/z33rr9xGwtWuuYPtX3nnY4Q1Z3p5DOWJ9 +4IetBflMI9TI84oAcIB5WCTe +=Zmj1 +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:18/setcred-14.patch b/website/static/security/patches/SA-26:18/setcred-14.patch new file mode 100644 index 0000000000..82444a9083 --- /dev/null +++ b/website/static/security/patches/SA-26:18/setcred-14.patch @@ -0,0 +1,15 @@ +--- sys/kern/kern_prot.c.orig ++++ sys/kern/kern_prot.c +@@ -527,10 +527,10 @@ + */ + *groups = wcred->sc_supp_groups_nb < CRED_SMALLGROUPS_NB ? + smallgroups : malloc((wcred->sc_supp_groups_nb + 1) * +- sizeof(*groups), M_TEMP, M_WAITOK); ++ sizeof(gid_t), M_TEMP, M_WAITOK); + + error = copyin(wcred->sc_supp_groups, *groups + 1, +- wcred->sc_supp_groups_nb * sizeof(*groups)); ++ wcred->sc_supp_groups_nb * sizeof(gid_t)); + if (error != 0) + return (error); + wcred->sc_supp_groups = *groups + 1; diff --git a/website/static/security/patches/SA-26:18/setcred-14.patch.asc b/website/static/security/patches/SA-26:18/setcred-14.patch.asc new file mode 100644 index 0000000000..fb4b8b1fd6 --- /dev/null +++ b/website/static/security/patches/SA-26:18/setcred-14.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKGwbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvyUAQAOW9vodMeIny/sYwja/h +i5tiMAYzjX6fT81v8VRRMBu32VRwCp9Y93nhuDlCmrEOr0BUw1makjSX1LKTXU1U +9Qe22KInT51Ri79z2Q4R8IynYSXACqvBcFXOtrtybbC2Vi41wJKUz7DUXZrVS4+N +5uBqJ/jYI3/Yxf04vragPa8Zv6ZdXlmkwcMia5HvFgITKT7FfItHxJC9df3UHj5a +Hi1BA+vQgmJKd8a19rwSJtquMLh1tKDL8M1FQ4fkXi0uKvnSuEy5B4JBm9TbyNnl +yP/22eVLVNmEuTiS5uhsuOchtKv/hBHYLarcciBlta6Foiw7YyqiXUgX2NwhNSUa +lSJR8b87Zrt79JhbyC2tfJ1KUHHVWEalOKgjgg40Zrbqs1AfAD8iNzfGo4hWSM3X +6o7CuVYsu4KmMVWP4qCYrjkdyHt++Cs3c9a+s2gLdP3egZAjnuuOtD9Pg6P8ZzZw +PeOI4MyGjKN2EDTa5/X6jKCl79ygU2vkhhbcvhBu53T8UUyLp7RmCw173y/bjm9K +nbN7pVtcLj8nUKKqzxZv5ZiRdZcoj7J/EWhvUb9Uz7EYqc9Qx1VEAl2bWUBGttBS +ReQkMQiC2ilfNowLKvSlnporTH30UW5JlaBzkMG1go2CBVQNEV3umX4nXTQiYlSp +qKQvsqfNgxBdCTDq6FsNCu65 +=BocE +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:18/setcred-15.patch b/website/static/security/patches/SA-26:18/setcred-15.patch new file mode 100644 index 0000000000..9cb65a3574 --- /dev/null +++ b/website/static/security/patches/SA-26:18/setcred-15.patch @@ -0,0 +1,15 @@ +--- sys/kern/kern_prot.c.orig ++++ sys/kern/kern_prot.c +@@ -554,10 +554,10 @@ + */ + *groups = wcred->sc_supp_groups_nb <= CRED_SMALLGROUPS_NB ? + smallgroups : malloc(wcred->sc_supp_groups_nb * +- sizeof(*groups), M_TEMP, M_WAITOK); ++ sizeof(gid_t), M_TEMP, M_WAITOK); + + error = copyin(wcred->sc_supp_groups, *groups, +- wcred->sc_supp_groups_nb * sizeof(*groups)); ++ wcred->sc_supp_groups_nb * sizeof(gid_t)); + if (error != 0) + return (error); + wcred->sc_supp_groups = *groups; diff --git a/website/static/security/patches/SA-26:18/setcred-15.patch.asc b/website/static/security/patches/SA-26:18/setcred-15.patch.asc new file mode 100644 index 0000000000..7dad72c15d --- /dev/null +++ b/website/static/security/patches/SA-26:18/setcred-15.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKG0bFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvIiQQAOUHGIICFi6oBcTzbnl3 +yFA/Gm3j9S2zjHcfN3TSOTbuuBNxx2sy7JLdDsG45VRGVdK1sS3gtQv65mey4rXD +U5tTbkIJaGnPYFfwoYBppFGcQtjhzgqt2YWatkOr28GPv5EKRY0NfPwqObaiMJy/ +L2StoZFwlFGe104dNmqLyDZ5/lDEWAXZgCaZRElk7odOT7wjnZJEcJKqIw1FDrXP +FrrZPZBbt4hha0KdyFY2A/ddM6Mnve14LoLW10nCFOpQH1+DMORSMoGdXuoFDWDX +K4nL5scWSbJnkQY5PBmJo6kN2mGMkMLCwpYbAyr6WXeS8WDF9bE8/I6C4EFa4JsG +D4aynWbE6VpJ35thyKd4p4+YI7VXuLtYVKdlJE0GGNV92qBb2pwCAtjVqfAogDMj +B1DuG+fcUPEtNS/aBn1A1BdM7UPTNDrp5X6L35e5MqVBU8ScWCgi0zQiscY3LvmR +BigmHWVeUcStLM+cCSAedhwKhXvJkpsoRVqzvLkIba+mL77YJh10s8zTzPWrlFV0 +k3G3U5yw/vhRIvkhoe6V5k4aw0lcNY4DDWai8xTjgd4T8hM+k7syjw8Q3uHBESyv +dag0q/OHLrSA/DLUhOjnHtfDaKjNAsfkW1QJINbGaZbbWrL8WB4jUZ1Lu7SPNHxj +It9S1CwDOvNRusx0xDnkUQUU +=i9RG +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:19/file-14.patch b/website/static/security/patches/SA-26:19/file-14.patch new file mode 100644 index 0000000000..327f123923 --- /dev/null +++ b/website/static/security/patches/SA-26:19/file-14.patch @@ -0,0 +1,203 @@ +--- sys/dev/netmap/netmap_freebsd.c.orig ++++ sys/dev/netmap/netmap_freebsd.c +@@ -119,6 +119,7 @@ + taskqueue_drain(si->ntfytq, &si->ntfytask); + taskqueue_free(si->ntfytq); + si->ntfytq = NULL; ++ seldrain(&si->si); + knlist_delete(&si->si.si_note, curthread, /*islocked=*/0); + knlist_destroy(&si->si.si_note); + /* now we don't need the mutex anymore */ +--- sys/kern/sys_procdesc.c.orig ++++ sys/kern/sys_procdesc.c +@@ -273,6 +273,7 @@ + KASSERT((pd->pd_flags & PDF_CLOSED), + ("procdesc_free: !PDF_CLOSED")); + ++ seldrain(&pd->pd_selinfo); + knlist_destroy(&pd->pd_selinfo.si_note); + PROCDESC_LOCK_DESTROY(pd); + free(pd, M_PROCDESC); +@@ -315,10 +316,7 @@ + procdesc_free(pd); + return (1); + } +- if (pd->pd_flags & PDF_SELECTED) { +- pd->pd_flags &= ~PDF_SELECTED; +- selwakeup(&pd->pd_selinfo); +- } ++ selwakeup(&pd->pd_selinfo); + KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT); + PROCDESC_UNLOCK(pd); + return (0); +@@ -433,10 +431,8 @@ + PROCDESC_LOCK(pd); + if (pd->pd_flags & PDF_EXITED) + revents |= POLLHUP; +- if (revents == 0) { ++ else + selrecord(td, &pd->pd_selinfo); +- pd->pd_flags |= PDF_SELECTED; +- } + PROCDESC_UNLOCK(pd); + return (revents); + } +--- sys/sys/procdesc.h.orig ++++ sys/sys/procdesc.h +@@ -86,7 +86,6 @@ + * Flags for the pd_flags field. + */ + #define PDF_CLOSED 0x00000001 /* Descriptor has closed. */ +-#define PDF_SELECTED 0x00000002 /* Issue selwakeup(). */ + #define PDF_EXITED 0x00000004 /* Process exited. */ + #define PDF_DAEMON 0x00000008 /* Don't exit when procdesc closes. */ + +--- tests/sys/kern/Makefile.orig ++++ tests/sys/kern/Makefile +@@ -27,6 +27,7 @@ + .endif + ATF_TESTS_C+= ktrace_test + ATF_TESTS_C+= module_test ++ATF_TESTS_C+= procdesc + ATF_TESTS_C+= ptrace_test + TEST_METADATA.ptrace_test+= timeout="15" + ATF_TESTS_C+= reaper +@@ -85,6 +86,7 @@ + LIBADD.kcov+= pthread + CFLAGS.ktls_test+= -DOPENSSL_API_COMPAT=0x10100000L + LIBADD.ktls_test+= crypto util ++LIBADD.procdesc+= pthread + LIBADD.socket_msg_waitall+= pthread + LIBADD.socket_splice+= pthread + LIBADD.sendfile_helper+= pthread +--- /dev/null ++++ tests/sys/kern/procdesc.c +@@ -0,0 +1,128 @@ ++/*- ++ * SPDX-License-Identifier: BSD-2-Clause ++ * ++ * Copyright (c) 2026 ConnectWise ++ * Copyright (c) 2026 Mark Johnston ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Tests for procdesc(4) that aren't specific to any one syscall */ ++ ++static void * ++poll_procdesc(void *arg) ++{ ++ struct pollfd pfd; ++ ++ pfd.fd = *(int *)arg; ++ pfd.events = POLLHUP; ++ (void)poll(&pfd, 1, 5000); ++ return ((void *)(uintptr_t)pfd.revents); ++} ++ ++/* ++ * Regression test to exercise the case where a procdesc is closed while a ++ * thread is poll()ing it. ++ */ ++ATF_TC_WITHOUT_HEAD(poll_close_race); ++ATF_TC_BODY(poll_close_race, tc) ++{ ++ pthread_t thr; ++ pid_t pid; ++ uintptr_t revents; ++ int error, pd; ++ ++ pid = pdfork(&pd, PD_DAEMON); ++ ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno)); ++ if (pid == 0) { ++ pause(); ++ _exit(0); ++ } ++ ++ error = pthread_create(&thr, NULL, poll_procdesc, &pd); ++ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); ++ ++ /* Wait for the thread to block in poll(2). */ ++ usleep(250000); ++ ++ ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno)); ++ ++ error = pthread_join(thr, (void *)&revents); ++ ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); ++ ATF_REQUIRE_EQ(revents, POLLNVAL); ++} ++ ++/* ++ * Verify that poll(2) of a procdesc returns POLLHUP when the process exits. ++ */ ++ATF_TC_WITHOUT_HEAD(poll_exit_wakeup); ++ATF_TC_BODY(poll_exit_wakeup, tc) ++{ ++ pthread_t thr; ++ uintptr_t revents; ++ pid_t pid; ++ int error, pd; ++ ++ pid = pdfork(&pd, PD_DAEMON); ++ ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno)); ++ if (pid == 0) { ++ pause(); ++ _exit(0); ++ } ++ ++ error = pthread_create(&thr, NULL, poll_procdesc, &pd); ++ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); ++ ++ /* Wait for the thread to block in poll(2). */ ++ usleep(250000); ++ ++ ATF_REQUIRE_MSG(pdkill(pd, SIGKILL) == 0, ++ "pdkill: %s", strerror(errno)); ++ ++ error = pthread_join(thr, (void *)&revents); ++ ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); ++ ATF_REQUIRE_EQ(revents, POLLHUP); ++ ++ ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno)); ++} ++ ++ATF_TP_ADD_TCS(tp) ++{ ++ ATF_TP_ADD_TC(tp, poll_close_race); ++ ATF_TP_ADD_TC(tp, poll_exit_wakeup); ++ ++ return (atf_no_error()); ++} diff --git a/website/static/security/patches/SA-26:19/file-14.patch.asc b/website/static/security/patches/SA-26:19/file-14.patch.asc new file mode 100644 index 0000000000..3ed9fafc3a --- /dev/null +++ b/website/static/security/patches/SA-26:19/file-14.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKG8bFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvNpoP/jb1grG0Pi0UGPNjwt/V +nrNT+blyuF/QgBj59Qs8ukTjZ9pkPCYtRFGPRvE046gLGgZxtRflqSchT7MHFi11 +EOe1QX74PFqHYNuTlBndaidapx3J6TOe+KbrriG7yWsaYbjpI7iiu+ebsU4arU8L +60N/EAiES6Eu8T7uQysOWXNbI0Tp0zehwK/W7Z9H2iiGtggU25MFMgcdFj+onwH+ +qKznW5/2xHiv3/P4k83RMkbSKb0oLAvUvTIGuMSTaLtr1sGkkajrKmxJiiGYZuGK +Zfw3wEWBZdTs5QM51CAnkqQaDEwxmUsOn70onXeeiTjcpzr7aTwVqSzd6NgJ+xkv +rr1gbfBDRgJW3Uk3/wOwAUGk4a6E3kDW3u2lc6hPD3TfTjrrBlE/XaT0Lkc//Trz +up2ebnjRaai9fsMjhA62k2H+hNXsioAzOe/t3aAEjTcCHKqLtyJ0cGEhAmfJvxE6 +c7h6q17hxNnT6fhfqqdfZ4c+JUBA3jiio/RQVL1/x+NzJiZxjRnzct28tuN9Y8am +BepYRa6pWSet8s7J8L5KNREmBHEgwLwqybKXCEJpjoQn9cQXTTb5M8eJkIa+V86q +nMDZ6TONl1OHeqiH7uhB47W38+jXGWeFMSromI5+spoKLax3m5zlJwkULMJJi8Vk +UAdh/UQz3TX+Bw129dVDQES+ +=J8vU +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:19/file-15.patch b/website/static/security/patches/SA-26:19/file-15.patch new file mode 100644 index 0000000000..53ebcbf036 --- /dev/null +++ b/website/static/security/patches/SA-26:19/file-15.patch @@ -0,0 +1,467 @@ +--- sys/dev/netmap/netmap_freebsd.c.orig ++++ sys/dev/netmap/netmap_freebsd.c +@@ -119,6 +119,7 @@ + taskqueue_drain(si->ntfytq, &si->ntfytask); + taskqueue_free(si->ntfytq); + si->ntfytq = NULL; ++ seldrain(&si->si); + knlist_delete(&si->si.si_note, curthread, /*islocked=*/0); + knlist_destroy(&si->si.si_note); + /* now we don't need the mutex anymore */ +--- sys/kern/kern_jaildesc.c.orig ++++ sys/kern/kern_jaildesc.c +@@ -197,10 +197,7 @@ + JAILDESC_LOCK(jd); + if (hint == NOTE_JAIL_REMOVE) { + jd->jd_flags |= JDF_REMOVED; +- if (jd->jd_flags & JDF_SELECTED) { +- jd->jd_flags &= ~JDF_SELECTED; +- selwakeup(&jd->jd_selinfo); +- } ++ selwakeup(&jd->jd_selinfo); + } + KNOTE_LOCKED(&jd->jd_selinfo.si_note, hint); + JAILDESC_UNLOCK(jd); +@@ -257,6 +254,7 @@ + } + prison_free(pr); + } ++ seldrain(&jd->jd_selinfo); + knlist_destroy(&jd->jd_selinfo.si_note); + JAILDESC_LOCK_DESTROY(jd); + free(jd, M_JAILDESC); +@@ -276,10 +274,8 @@ + JAILDESC_LOCK(jd); + if (jd->jd_flags & JDF_REMOVED) + revents |= POLLHUP; +- if (revents == 0) { ++ else + selrecord(td, &jd->jd_selinfo); +- jd->jd_flags |= JDF_SELECTED; +- } + JAILDESC_UNLOCK(jd); + return (revents); + } +--- sys/kern/sys_procdesc.c.orig ++++ sys/kern/sys_procdesc.c +@@ -270,6 +270,7 @@ + KASSERT((pd->pd_flags & PDF_CLOSED), + ("procdesc_free: !PDF_CLOSED")); + ++ seldrain(&pd->pd_selinfo); + knlist_destroy(&pd->pd_selinfo.si_note); + PROCDESC_LOCK_DESTROY(pd); + free(pd, M_PROCDESC); +@@ -312,10 +313,7 @@ + procdesc_free(pd); + return (1); + } +- if (pd->pd_flags & PDF_SELECTED) { +- pd->pd_flags &= ~PDF_SELECTED; +- selwakeup(&pd->pd_selinfo); +- } ++ selwakeup(&pd->pd_selinfo); + KNOTE_LOCKED(&pd->pd_selinfo.si_note, NOTE_EXIT); + PROCDESC_UNLOCK(pd); + return (0); +@@ -430,10 +428,8 @@ + PROCDESC_LOCK(pd); + if (pd->pd_flags & PDF_EXITED) + revents |= POLLHUP; +- if (revents == 0) { ++ else + selrecord(td, &pd->pd_selinfo); +- pd->pd_flags |= PDF_SELECTED; +- } + PROCDESC_UNLOCK(pd); + return (revents); + } +--- sys/sys/jaildesc.h.orig ++++ sys/sys/jaildesc.h +@@ -71,7 +71,6 @@ + /* + * Flags for the jd_flags field + */ +-#define JDF_SELECTED 0x00000001 /* issue selwakeup() */ + #define JDF_REMOVED 0x00000002 /* jail was removed */ + #define JDF_OWNING 0x00000004 /* closing descriptor removes jail */ + +--- sys/sys/procdesc.h.orig ++++ sys/sys/procdesc.h +@@ -86,7 +86,6 @@ + * Flags for the pd_flags field. + */ + #define PDF_CLOSED 0x00000001 /* Descriptor has closed. */ +-#define PDF_SELECTED 0x00000002 /* Issue selwakeup(). */ + #define PDF_EXITED 0x00000004 /* Process exited. */ + #define PDF_DAEMON 0x00000008 /* Don't exit when procdesc closes. */ + +--- tests/sys/kern/Makefile.orig ++++ tests/sys/kern/Makefile +@@ -22,6 +22,7 @@ + ATF_TESTS_C+= fdgrowtable_test + ATF_TESTS_C+= getdirentries_test + ATF_TESTS_C+= jail_lookup_root ++ATF_TESTS_C+= jaildesc + ATF_TESTS_C+= inotify_test + ATF_TESTS_C+= kill_zombie + .if ${MK_OPENSSL} != "no" +@@ -31,6 +32,7 @@ + ATF_TESTS_C+= listener_wakeup + ATF_TESTS_C+= module_test + ATF_TESTS_C+= prace ++ATF_TESTS_C+= procdesc + ATF_TESTS_C+= ptrace_test + TEST_METADATA.ptrace_test+= timeout="15" + ATF_TESTS_C+= reaper +@@ -84,6 +86,7 @@ + + LIBADD.copy_file_range+= md + LIBADD.jail_lookup_root+= jail util ++LIBADD.jaildesc+= pthread + CFLAGS.sys_getrandom+= -I${SRCTOP}/sys/contrib/zstd/lib + LIBADD.sys_getrandom+= zstd + LIBADD.sys_getrandom+= c +@@ -95,6 +98,7 @@ + CFLAGS.ktls_test+= -DOPENSSL_API_COMPAT=0x10100000L + LIBADD.ktls_test+= crypto util + LIBADD.listener_wakeup+= pthread ++LIBADD.procdesc+= pthread + LIBADD.shutdown_dgram+= pthread + LIBADD.socket_msg_waitall+= pthread + LIBADD.socket_splice+= pthread +--- /dev/null ++++ tests/sys/kern/jaildesc.c +@@ -0,0 +1,201 @@ ++/* ++ * Copyright (c) 2026 Mark Johnston ++ * ++ * SPDX-License-Identifier: BSD-2-Clause ++ */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * Create a persistent jail and return an owning descriptor for it. ++ * The jail is removed when the returned descriptor is closed. ++ */ ++static int ++create_jail(const char *name) ++{ ++ struct iovec iov[8]; ++ int desc, jid, n; ++ ++ desc = -1; ++ n = 0; ++ iov[n].iov_base = __DECONST(void *, "name"); ++ iov[n++].iov_len = strlen("name") + 1; ++ iov[n].iov_base = __DECONST(void *, name); ++ iov[n++].iov_len = strlen(name) + 1; ++ iov[n].iov_base = __DECONST(void *, "path"); ++ iov[n++].iov_len = strlen("path") + 1; ++ iov[n].iov_base = __DECONST(void *, "/"); ++ iov[n++].iov_len = strlen("/") + 1; ++ iov[n].iov_base = __DECONST(void *, "persist"); ++ iov[n++].iov_len = strlen("persist") + 1; ++ iov[n].iov_base = NULL; ++ iov[n++].iov_len = 0; ++ iov[n].iov_base = __DECONST(void *, "desc"); ++ iov[n++].iov_len = strlen("desc") + 1; ++ iov[n].iov_base = &desc; ++ iov[n++].iov_len = sizeof(desc); ++ jid = jail_set(iov, n, JAIL_CREATE | JAIL_OWN_DESC); ++ ATF_REQUIRE_MSG(jid >= 0, "jail_set: %s", strerror(errno)); ++ return (desc); ++} ++ ++static void * ++poll_jaildesc(void *arg) ++{ ++ struct pollfd pfd; ++ ++ pfd.fd = *(int *)arg; ++ pfd.events = POLLHUP; ++ (void)poll(&pfd, 1, 5000); ++ return ((void *)(uintptr_t)pfd.revents); ++} ++ ++/* ++ * Regression test for the case where a jail descriptor is closed while a ++ * thread is blocking in poll(2) on it. ++ */ ++ATF_TC(poll_close_race); ++ATF_TC_HEAD(poll_close_race, tc) ++{ ++ atf_tc_set_md_var(tc, "require.user", "root"); ++} ++ATF_TC_BODY(poll_close_race, tc) ++{ ++ pthread_t thr; ++ uintptr_t revents; ++ int error, jd; ++ ++ jd = create_jail("jaildesc_poll_close_race"); ++ ++ error = pthread_create(&thr, NULL, poll_jaildesc, &jd); ++ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); ++ ++ /* Wait for the thread to block in poll(2). */ ++ usleep(250000); ++ ++ ATF_REQUIRE_MSG(close(jd) == 0, "close: %s", strerror(errno)); ++ ++ error = pthread_join(thr, (void *)&revents); ++ ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); ++ ATF_REQUIRE_EQ(revents, POLLNVAL); ++} ++ ++/* ++ * Verify that poll(2) of a jail descriptor returns POLLHUP when the jail ++ * is removed. ++ */ ++ATF_TC(poll_remove_wakeup); ++ATF_TC_HEAD(poll_remove_wakeup, tc) ++{ ++ atf_tc_set_md_var(tc, "require.user", "root"); ++} ++ATF_TC_BODY(poll_remove_wakeup, tc) ++{ ++ pthread_t thr; ++ uintptr_t revents; ++ int error, jd; ++ ++ jd = create_jail("jaildesc_poll_remove_wakeup"); ++ ++ error = pthread_create(&thr, NULL, poll_jaildesc, &jd); ++ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); ++ ++ /* Wait for the thread to block in poll(2). */ ++ usleep(250000); ++ ++ ATF_REQUIRE_MSG(jail_remove_jd(jd) == 0, ++ "jail_remove_jd: %s", strerror(errno)); ++ ++ error = pthread_join(thr, (void *)&revents); ++ ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); ++ ATF_REQUIRE_EQ(revents, POLLHUP); ++ ++ ATF_REQUIRE_MSG(close(jd) == 0, "close: %s", strerror(errno)); ++} ++ ++static int ++get_jaildesc(const char *name) ++{ ++ struct iovec iov[4]; ++ char namebuf[MAXHOSTNAMELEN]; ++ int desc, jid, n; ++ ++ strlcpy(namebuf, name, sizeof(namebuf)); ++ desc = -1; ++ n = 0; ++ iov[n].iov_base = __DECONST(void *, "name"); ++ iov[n++].iov_len = strlen("name") + 1; ++ iov[n].iov_base = namebuf; ++ iov[n++].iov_len = sizeof(namebuf); ++ iov[n].iov_base = __DECONST(void *, "desc"); ++ iov[n++].iov_len = strlen("desc") + 1; ++ iov[n].iov_base = &desc; ++ iov[n++].iov_len = sizeof(desc); ++ jid = jail_get(iov, n, JAIL_GET_DESC); ++ ATF_REQUIRE_MSG(jid >= 0, "jail_get: %s", strerror(errno)); ++ return (desc); ++} ++ ++/* ++ * Regression test for the same use-after-free as poll_close_race, but with a ++ * non-owning JAIL_GET_DESC descriptor obtained without root privileges. ++ */ ++ATF_TC(poll_close_race_get_desc); ++ATF_TC_HEAD(poll_close_race_get_desc, tc) ++{ ++ atf_tc_set_md_var(tc, "require.user", "root"); ++} ++ATF_TC_BODY(poll_close_race_get_desc, tc) ++{ ++ struct passwd *pw; ++ pthread_t thr; ++ uintptr_t revents; ++ int error, jd, owning_jd; ++ ++ /* Create the jail as root; keep the owning descriptor for cleanup. */ ++ owning_jd = create_jail("jaildesc_poll_close_get_desc"); ++ ++ /* ++ * Drop root privileges. jail_get(2) with JAIL_GET_DESC does not ++ * require PRIV_JAIL_REMOVE, so a non-root process in the host prison ++ * can obtain a read-only descriptor for any visible jail. ++ */ ++ pw = getpwnam("nobody"); ++ ATF_REQUIRE_MSG(pw != NULL, "getpwnam: %s", strerror(errno)); ++ ATF_REQUIRE_MSG(setuid(pw->pw_uid) == 0, "setuid: %s", strerror(errno)); ++ ++ jd = get_jaildesc("jaildesc_poll_close_get_desc"); ++ ++ error = pthread_create(&thr, NULL, poll_jaildesc, &jd); ++ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); ++ ++ /* Wait for the thread to block in poll(2). */ ++ usleep(250000); ++ ++ ATF_REQUIRE_MSG(close(jd) == 0, "close: %s", strerror(errno)); ++ ++ error = pthread_join(thr, (void *)&revents); ++ ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); ++ ATF_REQUIRE_EQ(revents, POLLNVAL); ++ ++ ATF_REQUIRE_MSG(close(owning_jd) == 0, "close: %s", strerror(errno)); ++} ++ ++ATF_TP_ADD_TCS(tp) ++{ ++ ATF_TP_ADD_TC(tp, poll_close_race); ++ ATF_TP_ADD_TC(tp, poll_remove_wakeup); ++ ATF_TP_ADD_TC(tp, poll_close_race_get_desc); ++ ++ return (atf_no_error()); ++} +--- /dev/null ++++ tests/sys/kern/procdesc.c +@@ -0,0 +1,128 @@ ++/*- ++ * SPDX-License-Identifier: BSD-2-Clause ++ * ++ * Copyright (c) 2026 ConnectWise ++ * Copyright (c) 2026 Mark Johnston ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Tests for procdesc(4) that aren't specific to any one syscall */ ++ ++static void * ++poll_procdesc(void *arg) ++{ ++ struct pollfd pfd; ++ ++ pfd.fd = *(int *)arg; ++ pfd.events = POLLHUP; ++ (void)poll(&pfd, 1, 5000); ++ return ((void *)(uintptr_t)pfd.revents); ++} ++ ++/* ++ * Regression test to exercise the case where a procdesc is closed while a ++ * thread is poll()ing it. ++ */ ++ATF_TC_WITHOUT_HEAD(poll_close_race); ++ATF_TC_BODY(poll_close_race, tc) ++{ ++ pthread_t thr; ++ pid_t pid; ++ uintptr_t revents; ++ int error, pd; ++ ++ pid = pdfork(&pd, PD_DAEMON); ++ ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno)); ++ if (pid == 0) { ++ pause(); ++ _exit(0); ++ } ++ ++ error = pthread_create(&thr, NULL, poll_procdesc, &pd); ++ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); ++ ++ /* Wait for the thread to block in poll(2). */ ++ usleep(250000); ++ ++ ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno)); ++ ++ error = pthread_join(thr, (void *)&revents); ++ ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); ++ ATF_REQUIRE_EQ(revents, POLLNVAL); ++} ++ ++/* ++ * Verify that poll(2) of a procdesc returns POLLHUP when the process exits. ++ */ ++ATF_TC_WITHOUT_HEAD(poll_exit_wakeup); ++ATF_TC_BODY(poll_exit_wakeup, tc) ++{ ++ pthread_t thr; ++ uintptr_t revents; ++ pid_t pid; ++ int error, pd; ++ ++ pid = pdfork(&pd, PD_DAEMON); ++ ATF_REQUIRE_MSG(pid >= 0, "pdfork: %s", strerror(errno)); ++ if (pid == 0) { ++ pause(); ++ _exit(0); ++ } ++ ++ error = pthread_create(&thr, NULL, poll_procdesc, &pd); ++ ATF_REQUIRE_MSG(error == 0, "pthread_create: %s", strerror(error)); ++ ++ /* Wait for the thread to block in poll(2). */ ++ usleep(250000); ++ ++ ATF_REQUIRE_MSG(pdkill(pd, SIGKILL) == 0, ++ "pdkill: %s", strerror(errno)); ++ ++ error = pthread_join(thr, (void *)&revents); ++ ATF_REQUIRE_MSG(error == 0, "pthread_join: %s", strerror(error)); ++ ATF_REQUIRE_EQ(revents, POLLHUP); ++ ++ ATF_REQUIRE_MSG(close(pd) == 0, "close: %s", strerror(errno)); ++} ++ ++ATF_TP_ADD_TCS(tp) ++{ ++ ATF_TP_ADD_TC(tp, poll_close_race); ++ ATF_TP_ADD_TC(tp, poll_exit_wakeup); ++ ++ return (atf_no_error()); ++} diff --git a/website/static/security/patches/SA-26:19/file-15.patch.asc b/website/static/security/patches/SA-26:19/file-15.patch.asc new file mode 100644 index 0000000000..d15ac5c435 --- /dev/null +++ b/website/static/security/patches/SA-26:19/file-15.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHAbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvUPwQAL4Td5qsO6YRMxxoyd9Y +h59xkv7KCPmLOXRLgAf+Aqxof0lqVGbxOx2e9ULzyL2InRvKEsbFCMthdYyWcR0f +CRTLqPFWq6sAAd2s1ensgTMMn89Ei7ZJoqjPFF7Cl9EBdPRvROf+8aDLD7BXhxO4 +jwoQY0b0FR/IhhP7sfMUEy3Lg2D6E68rrFV6EvKj0CQPentHY3GFSKYh6RcOmBYP +V1xRl27vTQOfrjIoPPSKTKyVulfjPb7k2ZNsrNozEwdG541ZGddafk+V59siyBxZ +zt7WBNPPdqSZUOrdWEfy0WS7ejzVk+EtZejNDT5IyJciZfc+2/EGHJDHD4/13UDJ +lUC4a5cuvSm6bf8qk1lnz1ISfPxayYK3T53lU/axasIlqOdj3jFwWl+EyMDQLmfq +lxMskYbDobbuxmRstpET9uTQB+uAu2yg2+eej/FGzdT5p+R/54dS6kdeuU2JNFXD +KJrlll92vMCm1U9gzW5rzzUb78xXWLl4k07PHvauaqActWXZyDPOUGH7a/Ik57BG +2kO/EzPJZvhpYbhM7Zidfot+b9X+gO4v9YsFy02XdZhRgblIlzyvtDkl2l7drlC7 +j8fzQiEEHJxnMwhj5bxpE028cRg8XszVXgAYGHeEJMqc2mbkMh5yAVgUZX10GZtc +lJGd4WA1yrr618fLIvXGz+re +=3h0D +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:20/fusefs-14.3.patch b/website/static/security/patches/SA-26:20/fusefs-14.3.patch new file mode 100644 index 0000000000..35b68cec8f --- /dev/null +++ b/website/static/security/patches/SA-26:20/fusefs-14.3.patch @@ -0,0 +1,146 @@ +--- sys/fs/fuse/fuse_ipc.h.orig ++++ sys/fs/fuse/fuse_ipc.h +@@ -238,6 +238,7 @@ + #define FSESS_WARN_WB_CACHE_INCOHERENT 0x400000 /* WB cache incoherent */ + #define FSESS_WARN_ILLEGAL_INODE 0x800000 /* Illegal inode for new file */ + #define FSESS_WARN_READLINK_EMBEDDED_NUL 0x1000000 /* corrupt READLINK output */ ++#define FSESS_WARN_LSEXTATTR_NUL 0x20000000 /* Non nul-terminated xattr list */ + #define FSESS_MNTOPTS_MASK ( \ + FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \ + FSESS_DEFAULT_PERMISSIONS | FSESS_INTR) +--- sys/fs/fuse/fuse_vnops.c.orig ++++ sys/fs/fuse/fuse_vnops.c +@@ -2806,8 +2806,8 @@ + * bsd_list, bsd_list_len - output list compatible with bsd vfs + */ + static int +-fuse_xattrlist_convert(char *prefix, const char *list, int list_len, +- char *bsd_list, int *bsd_list_len) ++fuse_xattrlist_convert(struct fuse_data *data, char *prefix, const char *list, ++ int list_len, char *bsd_list, int *bsd_list_len) + { + int len, pos, dist_to_next, prefix_len; + +@@ -2816,7 +2816,13 @@ + prefix_len = strlen(prefix); + + while (pos < list_len && list[pos] != '\0') { +- dist_to_next = strlen(&list[pos]) + 1; ++ dist_to_next = strnlen(&list[pos], list_len - pos - 1) + 1; ++ if (list[pos + dist_to_next - 1] != '\0') { ++ fuse_warn(data, FSESS_WARN_LSEXTATTR_NUL, ++ "The FUSE server returned a non nul-terminated " ++ "LISTXATTR response."); ++ return (EIO); ++ } + if (bcmp(&list[pos], prefix, prefix_len) == 0 && + list[pos + prefix_len] == extattr_namespace_separator) { + len = dist_to_next - +@@ -2872,6 +2878,7 @@ + struct fuse_listxattr_in *list_xattr_in; + struct fuse_listxattr_out *list_xattr_out; + struct mount *mp = vnode_mount(vp); ++ struct fuse_data *data = fuse_get_mpdata(mp); + struct thread *td = ap->a_td; + struct ucred *cred = ap->a_cred; + char *prefix; +@@ -2949,8 +2956,6 @@ + linux_list = fdi.answ; + /* FUSE doesn't allow the server to return more data than requested */ + if (fdi.iosize > linux_list_len) { +- struct fuse_data *data = fuse_get_mpdata(mp); +- + fuse_warn(data, FSESS_WARN_LSEXTATTR_LONG, + "server returned " + "more extended attribute data than requested; " +@@ -2967,7 +2972,7 @@ + * FreeBSD's format before giving it to the user. + */ + bsd_list = malloc(linux_list_len, M_TEMP, M_WAITOK); +- err = fuse_xattrlist_convert(prefix, linux_list, linux_list_len, ++ err = fuse_xattrlist_convert(data, prefix, linux_list, linux_list_len, + bsd_list, &bsd_list_len); + if (err != 0) + goto out; +--- tests/sys/fs/fusefs/xattr.cc.orig ++++ tests/sys/fs/fusefs/xattr.cc +@@ -448,6 +448,79 @@ + ASSERT_TRUE(WIFSIGNALED(status)); + } + ++/* ++ * A buggy or malicious server returns a list that isn't nul-terminated. The ++ * kernel should handle it gracefully. ++ */ ++TEST_F(Listxattr, not_nul_terminated) ++{ ++ uint64_t ino = 42; ++ int ns = EXTATTR_NAMESPACE_USER; ++ char *data; ++ const char expected[4] = {3, 'f', 'o', 'o'}; ++ const char first[255] = "user.foo\0system.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; ++ const uint8_t badlist[9] = {'u', 's', 'e', 'r', '.', 'f', 'o', 'o', 'd'}; ++ Sequence seq; ++ ++ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) ++ .WillRepeatedly(Invoke( ++ ReturnImmediate([=](auto in __unused, auto& out) { ++ SET_OUT_HEADER_LEN(out, entry); ++ out.body.entry.attr.mode = S_IFREG | 0644; ++ out.body.entry.nodeid = ino; ++ out.body.entry.attr.nlink = 1; ++ out.body.entry.attr_valid = UINT64_MAX; ++ out.body.entry.entry_valid = UINT64_MAX; ++ }))); ++ ++ /* ++ * On the first LISTXATTRS call, return a big attribute just to fill ++ * the heap with non-NUL data. ++ */ ++ expect_listxattr(ino, 0, ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ out.body.listxattr.size = sizeof(first); ++ SET_OUT_HEADER_LEN(out, listxattr); ++ }), &seq ++ ); ++ expect_listxattr(ino, sizeof(first), ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ memcpy((void*)out.body.bytes, first, sizeof(first)); ++ out.header.len = sizeof(fuse_out_header) + sizeof(first); ++ }), &seq ++ ); ++ /* ++ * On the second LISTXATTRS call, return a malformed list with no NUL ++ * termination. The heap might still be full of the data from the ++ * first call. ++ */ ++ expect_listxattr(ino, 0, ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ out.body.listxattr.size = sizeof(badlist); ++ SET_OUT_HEADER_LEN(out, listxattr); ++ }), &seq ++ ); ++ expect_listxattr(ino, sizeof(badlist), ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ memset((void*)out.body.bytes, 'x', sizeof(first)); ++ memcpy((void*)out.body.bytes, badlist, sizeof(badlist)); ++ out.header.len = sizeof(fuse_out_header) + sizeof(badlist); ++ }), &seq ++ ); ++ ++ data = new char[1024]; ++ ++ ASSERT_EQ(static_cast(sizeof(expected)), ++ extattr_list_file(FULLPATH, ns, data, sizeof(data))) ++ << strerror(errno); ++ /* ++ * Receiving this malformed list, the kernel should log it to dmesg and ++ * report an IO error to the caller. ++ */ ++ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, data, sizeof(data))); ++ EXPECT_EQ(EIO, errno); ++} ++ + /* + * Get the size of the list that it would take to list no extended attributes + */ diff --git a/website/static/security/patches/SA-26:20/fusefs-14.3.patch.asc b/website/static/security/patches/SA-26:20/fusefs-14.3.patch.asc new file mode 100644 index 0000000000..0f3c9741eb --- /dev/null +++ b/website/static/security/patches/SA-26:20/fusefs-14.3.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHMbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvlI0QALm6fFHg0EXsCNa/fEx6 +oSYsueeAZgVd4T+gXdQ2Qj/Ne6LImFJiRiG2m9yhIEVCZA2l54r5UP5IpGag+g0S +h1H8JEs2VYrc/Z7+E2xD2e3bKhwyJ5DDcoHbGALkFFaVvGDibC79yqzmBHmkBdZr +W5DBi6YuMlDqoZVMZps00qNRnUjbLWNZXafEiuNBf1suUv14hhyI/DD+SPEXWvDG +IXXeEO5zA7IxecZSUHZak9DQL234c/gR/+tkgVKwgY+42e9AqcTIOFTDgyZFJ56V +OgJ5BFsFWO0lFxYPmKRvUPs2DnsvEkN+L9meT5G8BxCC3QybeBRuFoi4/5mLTNAf +gEU6Fg/XIs/cHnRxq2nfqKigdvULAEeqqUZCIZLnPLxx5DKr42PCv5ExalV+UbTW +826x6NQ5CNM/MncBpQJwfdwVLc3gOo+jx96I1t9Fe1RMzdho0wj2/rd9gjElkAv7 +ffDtKraDy/m2559Bmu9dLYBokeeFMtLvvGgoHMAyum3QgottdLOkqquvGETM1voP +2dWNuynDmzf+hFIVN5LgL21TaJbYFvS0MZNmYKGyu6Lqx+9KvYyDdy/T0UGWflU0 +mSbDcCflqUfxdqo50lNzaFhn9KLAA5KhO3DcggeCEVS1ylxN+d85j4qPkROF4I6E +DEYgzjfPApOkOUqpNXFCvtye +=Bk3h +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:20/fusefs-14.4.patch b/website/static/security/patches/SA-26:20/fusefs-14.4.patch new file mode 100644 index 0000000000..a598821095 --- /dev/null +++ b/website/static/security/patches/SA-26:20/fusefs-14.4.patch @@ -0,0 +1,146 @@ +--- sys/fs/fuse/fuse_ipc.h.orig ++++ sys/fs/fuse/fuse_ipc.h +@@ -240,6 +240,7 @@ + #define FSESS_WARN_READLINK_EMBEDDED_NUL 0x1000000 /* corrupt READLINK output */ + #define FSESS_WARN_DOT_LOOKUP 0x2000000 /* Inconsistent . LOOKUP response */ + #define FSESS_WARN_INODE_MISMATCH 0x4000000 /* ino != nodeid */ ++#define FSESS_WARN_LSEXTATTR_NUL 0x20000000 /* Non nul-terminated xattr list */ + #define FSESS_MNTOPTS_MASK ( \ + FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \ + FSESS_DEFAULT_PERMISSIONS | FSESS_INTR) +--- sys/fs/fuse/fuse_vnops.c.orig ++++ sys/fs/fuse/fuse_vnops.c +@@ -2794,8 +2794,8 @@ + * bsd_list, bsd_list_len - output list compatible with bsd vfs + */ + static int +-fuse_xattrlist_convert(char *prefix, const char *list, int list_len, +- char *bsd_list, int *bsd_list_len) ++fuse_xattrlist_convert(struct fuse_data *data, char *prefix, const char *list, ++ int list_len, char *bsd_list, int *bsd_list_len) + { + int len, pos, dist_to_next, prefix_len; + +@@ -2804,7 +2804,13 @@ + prefix_len = strlen(prefix); + + while (pos < list_len && list[pos] != '\0') { +- dist_to_next = strlen(&list[pos]) + 1; ++ dist_to_next = strnlen(&list[pos], list_len - pos - 1) + 1; ++ if (list[pos + dist_to_next - 1] != '\0') { ++ fuse_warn(data, FSESS_WARN_LSEXTATTR_NUL, ++ "The FUSE server returned a non nul-terminated " ++ "LISTXATTR response."); ++ return (EIO); ++ } + if (bcmp(&list[pos], prefix, prefix_len) == 0 && + list[pos + prefix_len] == extattr_namespace_separator) { + len = dist_to_next - +@@ -2860,6 +2866,7 @@ + struct fuse_listxattr_in *list_xattr_in; + struct fuse_listxattr_out *list_xattr_out; + struct mount *mp = vnode_mount(vp); ++ struct fuse_data *data = fuse_get_mpdata(mp); + struct thread *td = ap->a_td; + struct ucred *cred = ap->a_cred; + char *prefix; +@@ -2937,8 +2944,6 @@ + linux_list = fdi.answ; + /* FUSE doesn't allow the server to return more data than requested */ + if (fdi.iosize > linux_list_len) { +- struct fuse_data *data = fuse_get_mpdata(mp); +- + fuse_warn(data, FSESS_WARN_LSEXTATTR_LONG, + "server returned " + "more extended attribute data than requested; " +@@ -2955,7 +2960,7 @@ + * FreeBSD's format before giving it to the user. + */ + bsd_list = malloc(linux_list_len, M_TEMP, M_WAITOK); +- err = fuse_xattrlist_convert(prefix, linux_list, linux_list_len, ++ err = fuse_xattrlist_convert(data, prefix, linux_list, linux_list_len, + bsd_list, &bsd_list_len); + if (err != 0) + goto out; +--- tests/sys/fs/fusefs/xattr.cc.orig ++++ tests/sys/fs/fusefs/xattr.cc +@@ -448,6 +448,79 @@ + ASSERT_TRUE(WIFSIGNALED(status)); + } + ++/* ++ * A buggy or malicious server returns a list that isn't nul-terminated. The ++ * kernel should handle it gracefully. ++ */ ++TEST_F(Listxattr, not_nul_terminated) ++{ ++ uint64_t ino = 42; ++ int ns = EXTATTR_NAMESPACE_USER; ++ char *data; ++ const char expected[4] = {3, 'f', 'o', 'o'}; ++ const char first[255] = "user.foo\0system.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; ++ const uint8_t badlist[9] = {'u', 's', 'e', 'r', '.', 'f', 'o', 'o', 'd'}; ++ Sequence seq; ++ ++ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) ++ .WillRepeatedly(Invoke( ++ ReturnImmediate([=](auto in __unused, auto& out) { ++ SET_OUT_HEADER_LEN(out, entry); ++ out.body.entry.attr.mode = S_IFREG | 0644; ++ out.body.entry.nodeid = ino; ++ out.body.entry.attr.nlink = 1; ++ out.body.entry.attr_valid = UINT64_MAX; ++ out.body.entry.entry_valid = UINT64_MAX; ++ }))); ++ ++ /* ++ * On the first LISTXATTRS call, return a big attribute just to fill ++ * the heap with non-NUL data. ++ */ ++ expect_listxattr(ino, 0, ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ out.body.listxattr.size = sizeof(first); ++ SET_OUT_HEADER_LEN(out, listxattr); ++ }), &seq ++ ); ++ expect_listxattr(ino, sizeof(first), ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ memcpy((void*)out.body.bytes, first, sizeof(first)); ++ out.header.len = sizeof(fuse_out_header) + sizeof(first); ++ }), &seq ++ ); ++ /* ++ * On the second LISTXATTRS call, return a malformed list with no NUL ++ * termination. The heap might still be full of the data from the ++ * first call. ++ */ ++ expect_listxattr(ino, 0, ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ out.body.listxattr.size = sizeof(badlist); ++ SET_OUT_HEADER_LEN(out, listxattr); ++ }), &seq ++ ); ++ expect_listxattr(ino, sizeof(badlist), ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ memset((void*)out.body.bytes, 'x', sizeof(first)); ++ memcpy((void*)out.body.bytes, badlist, sizeof(badlist)); ++ out.header.len = sizeof(fuse_out_header) + sizeof(badlist); ++ }), &seq ++ ); ++ ++ data = new char[1024]; ++ ++ ASSERT_EQ(static_cast(sizeof(expected)), ++ extattr_list_file(FULLPATH, ns, data, sizeof(data))) ++ << strerror(errno); ++ /* ++ * Receiving this malformed list, the kernel should log it to dmesg and ++ * report an IO error to the caller. ++ */ ++ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, data, sizeof(data))); ++ EXPECT_EQ(EIO, errno); ++} ++ + /* + * Get the size of the list that it would take to list no extended attributes + */ diff --git a/website/static/security/patches/SA-26:20/fusefs-14.4.patch.asc b/website/static/security/patches/SA-26:20/fusefs-14.4.patch.asc new file mode 100644 index 0000000000..2c328bce9d --- /dev/null +++ b/website/static/security/patches/SA-26:20/fusefs-14.4.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHQbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvTEYP/ji9A2XSR2utdOfiMp7d +I3Bhy66qAxYmC3xcVDwQLMe5z/7BK6lgJFfLp6Lq6cFAvKYy+cwY9iR85fAcGK60 +cInncCgFpgl3apNzBQrSg28kD2v1bIM80oaeM/uBJc2WIjKPeu5Z7DdniHiiUxae +jknvhojwpCkd3KCktqrpW6IBHI1TtXuHJWpDPBWr9KbxEcTWUku4gfnXkLvq8yoi +9YrMnWIK0zP322BbpEgujQengsoT2dYOQgj2YqXegLgrEiXWn/OdcmqSLSTKTl4W +5Zdmf90hZbvUMD4jnumXAEoCG++oopec0uFkTPuFLuXTqZhSFeTAT+9P6vepyc6i +EpoGEaSJjDlAdbpf/usp69ReROr6DHje67bMUcTpDWQXlv3fho8UirCMhOU8qAdI +ZQS6ogJgTxz29sEfgD+raaiQYte7p3pFIA41RwXpN9g87hyMGk41GRxZMpyRtN44 +W8OUOUf1bwGpNcyu9J/z8WZ7eNT4iwuRw13IeQ7jUcz1bIt7GlnV8TiyFCc+ZsvW +PzWx15zuWO751AzwnuUJkOR7H+xCZT48aOj5WvegdAIBpqMSz3R2veecyGsEjfRE +pe9z06/2dNR9ZPCxW+1yA9iBZr46D+wWXAD4TCNajHDXIRUBu18blhITQmTbHqeO +Ok5rj3IHx1CRL4kNgWDjtzHe +=ZCVv +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:20/fusefs-15.patch b/website/static/security/patches/SA-26:20/fusefs-15.patch new file mode 100644 index 0000000000..ff6e838f45 --- /dev/null +++ b/website/static/security/patches/SA-26:20/fusefs-15.patch @@ -0,0 +1,147 @@ +--- sys/fs/fuse/fuse_ipc.h.orig ++++ sys/fs/fuse/fuse_ipc.h +@@ -240,6 +240,7 @@ + #define FSESS_WARN_READLINK_EMBEDDED_NUL 0x1000000 /* corrupt READLINK output */ + #define FSESS_WARN_DOT_LOOKUP 0x2000000 /* Inconsistent . LOOKUP response */ + #define FSESS_WARN_INODE_MISMATCH 0x4000000 /* ino != nodeid */ ++#define FSESS_WARN_LSEXTATTR_NUL 0x20000000 /* Non nul-terminated xattr list */ + #define FSESS_MNTOPTS_MASK ( \ + FSESS_DAEMON_CAN_SPY | FSESS_PUSH_SYMLINKS_IN | \ + FSESS_DEFAULT_PERMISSIONS | FSESS_INTR) +--- sys/fs/fuse/fuse_vnops.c.orig ++++ sys/fs/fuse/fuse_vnops.c +@@ -2841,8 +2841,8 @@ + * bsd_list, bsd_list_len - output list compatible with bsd vfs + */ + static int +-fuse_xattrlist_convert(char *prefix, const char *list, int list_len, +- char *bsd_list, int *bsd_list_len) ++fuse_xattrlist_convert(struct fuse_data *data, char *prefix, const char *list, ++ int list_len, char *bsd_list, int *bsd_list_len) + { + int len, pos, dist_to_next, prefix_len; + +@@ -2851,7 +2851,14 @@ + prefix_len = strlen(prefix); + + while (pos < list_len && list[pos] != '\0') { +- dist_to_next = strlen(&list[pos]) + 1; ++ dist_to_next = strnlen(&list[pos], list_len - pos - 1) + 1; ++ if (list[pos + dist_to_next - 1] != '\0') { ++ fuse_warn(data, FSESS_WARN_LSEXTATTR_NUL, ++ "The FUSE server returned a non nul-terminated " ++ "LISTXATTR response."); ++ return (EXTERROR(EIO, ++ "The FUSE server returned a malformed list")); ++ } + if (bcmp(&list[pos], prefix, prefix_len) == 0 && + list[pos + prefix_len] == extattr_namespace_separator) { + len = dist_to_next - +@@ -2907,6 +2914,7 @@ + struct fuse_listxattr_in *list_xattr_in; + struct fuse_listxattr_out *list_xattr_out; + struct mount *mp = vnode_mount(vp); ++ struct fuse_data *data = fuse_get_mpdata(mp); + struct thread *td = ap->a_td; + struct ucred *cred = ap->a_cred; + char *prefix; +@@ -2987,8 +2995,6 @@ + linux_list = fdi.answ; + /* FUSE doesn't allow the server to return more data than requested */ + if (fdi.iosize > linux_list_len) { +- struct fuse_data *data = fuse_get_mpdata(mp); +- + fuse_warn(data, FSESS_WARN_LSEXTATTR_LONG, + "server returned " + "more extended attribute data than requested; " +@@ -3005,7 +3011,7 @@ + * FreeBSD's format before giving it to the user. + */ + bsd_list = malloc(linux_list_len, M_TEMP, M_WAITOK); +- err = fuse_xattrlist_convert(prefix, linux_list, linux_list_len, ++ err = fuse_xattrlist_convert(data, prefix, linux_list, linux_list_len, + bsd_list, &bsd_list_len); + if (err != 0) + goto out; +--- tests/sys/fs/fusefs/xattr.cc.orig ++++ tests/sys/fs/fusefs/xattr.cc +@@ -492,6 +492,79 @@ + ASSERT_TRUE(WIFSIGNALED(status)); + } + ++/* ++ * A buggy or malicious server returns a list that isn't nul-terminated. The ++ * kernel should handle it gracefully. ++ */ ++TEST_F(Listxattr, not_nul_terminated) ++{ ++ uint64_t ino = 42; ++ int ns = EXTATTR_NAMESPACE_USER; ++ char *data; ++ const char expected[4] = {3, 'f', 'o', 'o'}; ++ const char first[255] = "user.foo\0system.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; ++ const uint8_t badlist[9] = {'u', 's', 'e', 'r', '.', 'f', 'o', 'o', 'd'}; ++ Sequence seq; ++ ++ EXPECT_LOOKUP(FUSE_ROOT_ID, RELPATH) ++ .WillRepeatedly(Invoke( ++ ReturnImmediate([=](auto in __unused, auto& out) { ++ SET_OUT_HEADER_LEN(out, entry); ++ out.body.entry.attr.mode = S_IFREG | 0644; ++ out.body.entry.nodeid = ino; ++ out.body.entry.attr.nlink = 1; ++ out.body.entry.attr_valid = UINT64_MAX; ++ out.body.entry.entry_valid = UINT64_MAX; ++ }))); ++ ++ /* ++ * On the first LISTXATTRS call, return a big attribute just to fill ++ * the heap with non-NUL data. ++ */ ++ expect_listxattr(ino, 0, ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ out.body.listxattr.size = sizeof(first); ++ SET_OUT_HEADER_LEN(out, listxattr); ++ }), &seq ++ ); ++ expect_listxattr(ino, sizeof(first), ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ memcpy((void*)out.body.bytes, first, sizeof(first)); ++ out.header.len = sizeof(fuse_out_header) + sizeof(first); ++ }), &seq ++ ); ++ /* ++ * On the second LISTXATTRS call, return a malformed list with no NUL ++ * termination. The heap might still be full of the data from the ++ * first call. ++ */ ++ expect_listxattr(ino, 0, ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ out.body.listxattr.size = sizeof(badlist); ++ SET_OUT_HEADER_LEN(out, listxattr); ++ }), &seq ++ ); ++ expect_listxattr(ino, sizeof(badlist), ++ ReturnImmediate([&](auto in __unused, auto& out) { ++ memset((void*)out.body.bytes, 'x', sizeof(first)); ++ memcpy((void*)out.body.bytes, badlist, sizeof(badlist)); ++ out.header.len = sizeof(fuse_out_header) + sizeof(badlist); ++ }), &seq ++ ); ++ ++ data = new char[1024]; ++ ++ ASSERT_EQ(static_cast(sizeof(expected)), ++ extattr_list_file(FULLPATH, ns, data, sizeof(data))) ++ << strerror(errno); ++ /* ++ * Receiving this malformed list, the kernel should log it to dmesg and ++ * report an IO error to the caller. ++ */ ++ ASSERT_EQ(-1, extattr_list_file(FULLPATH, ns, data, sizeof(data))); ++ EXPECT_EQ(EIO, errno); ++} ++ + /* + * Get the size of the list that it would take to list no extended attributes + */ diff --git a/website/static/security/patches/SA-26:20/fusefs-15.patch.asc b/website/static/security/patches/SA-26:20/fusefs-15.patch.asc new file mode 100644 index 0000000000..9a7b7393e8 --- /dev/null +++ b/website/static/security/patches/SA-26:20/fusefs-15.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHUbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrv0+cP/0P7o4+JOhW/CAe6DdQI +90ZMElw9Q/ceD4P63RTby9oFCIG1l+hEMfCeH9CoaRXuTelIrX8g+O5o+9ED3oPc +FpPj/baTFXoq8ZnxoT18ZtGqsF/pjpZlEZx4vFLILjXMnmun9WC7Bw2X51gUxh46 +XoilE/ofXmTebRRn/cslae3DTOBezMPO59QVg+qvdQEzoPxTF6YWtGW+775ZiSEo +i0jxBvTqNe17QKZnjMO5Mr7ZlCAmG10z8ygmWOXbtIyaSSq8I1UKzUBhnq4UUC1E +BW7YtbYBu/ZIG9Wyg4apcN7Rb4krg2J/5wWaZnJYxLNEWM1wTjR1Mg/AFsV/1mav +ueJBEkGRfGcTH/UbCi4nMBTizN18dATdkbQSuffU+BeTLYjylS15nramtI5BJcVa +M2XHMVFHNvQB0bVyRwTXvwXggwSXDnIPeB/k3w+3usyY/xQ66cf5KfaNFZ/MJcRv +hukTFXwRFt5q6Q0Emnt4whti+gn4HQNnfFHJONdXXPU2X05CGGHtg5/qTdm8wLKm +iO8ggc3i/15gAdaPKwkWMwQyifwQ4kNYPttwqkrTz+kyCuhzftHuWNJ+ZRnh+99a +pPNuTMZd9ZOTkjX0DebEbPZD9CZ4qiwquOMnUJ07b6xTa02BHk63D0SDIVl4vV9R +9u08BlmYuNc2zTAFit1MychY +=gqpB +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:21/ptrace-14.3.patch b/website/static/security/patches/SA-26:21/ptrace-14.3.patch new file mode 100644 index 0000000000..1c357dd9e0 --- /dev/null +++ b/website/static/security/patches/SA-26:21/ptrace-14.3.patch @@ -0,0 +1,164 @@ +--- sys/kern/kern_sig.c.orig ++++ sys/kern/kern_sig.c +@@ -2677,23 +2677,26 @@ + struct sysentvec *sv; + struct sysent *se; + register_t rv_saved[2]; ++ unsigned int sc; + int error, nerror; +- int sc; + bool audited, sy_thr_static; + +- sv = p->p_sysent; +- if (sv->sv_table == NULL || sv->sv_size < tsr->ts_sa.code) { +- tsr->ts_ret.sr_error = ENOSYS; +- return; +- } +- + sc = tsr->ts_sa.code; + if (sc == SYS_syscall || sc == SYS___syscall) { ++ if (tsr->ts_nargs == 0) { ++ tsr->ts_ret.sr_error = EINVAL; ++ return; ++ } + sc = tsr->ts_sa.args[0]; + memmove(&tsr->ts_sa.args[0], &tsr->ts_sa.args[1], + sizeof(register_t) * (tsr->ts_nargs - 1)); + } + ++ sv = p->p_sysent; ++ if (sv->sv_table == NULL || sc >= sv->sv_size) { ++ tsr->ts_ret.sr_error = ENOSYS; ++ return; ++ } + tsr->ts_sa.callp = se = &sv->sv_table[sc]; + + VM_CNT_INC(v_syscall); +--- tests/sys/kern/ptrace_test.c.orig ++++ tests/sys/kern/ptrace_test.c +@@ -4363,6 +4363,25 @@ + REQUIRE_EQ(close(pd), 0); + } + ++static void ++pt_sc_remote(pid_t pid, struct ptrace_sc_remote *pscr, int error, ++ syscallarg_t ret) ++{ ++ pid_t wpid; ++ int status; ++ ++ ATF_REQUIRE(ptrace(PT_SC_REMOTE, pid, (caddr_t)pscr, sizeof(*pscr)) != ++ -1); ++ ATF_REQUIRE_EQ(pscr->pscr_ret.sr_error, error); ++ if (error == 0) ++ ATF_REQUIRE_EQ(pscr->pscr_ret.sr_retval[0], ret); ++ ++ wpid = waitpid(pid, &status, 0); ++ REQUIRE_EQ(wpid, pid); ++ ATF_REQUIRE(WIFSTOPPED(status)); ++ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ++} ++ + /* + * Try using PT_SC_REMOTE to get the PID of a traced child process. + */ +@@ -4370,8 +4389,7 @@ + ATF_TC_BODY(ptrace__PT_SC_REMOTE_getpid, tc) + { + struct ptrace_sc_remote pscr; +- pid_t fpid, wpid; +- int status; ++ pid_t fpid; + + ATF_REQUIRE((fpid = fork()) != -1); + if (fpid == 0) { +@@ -4384,35 +4402,62 @@ + pscr.pscr_syscall = SYS_getpid; + pscr.pscr_nargs = 0; + pscr.pscr_args = NULL; +- ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) != +- -1); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0, +- "remote getpid failed with error %d", pscr.pscr_ret.sr_error); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == fpid, +- "unexpected return value %jd instead of %d", +- (intmax_t)pscr.pscr_ret.sr_retval[0], fpid); +- +- wpid = waitpid(fpid, &status, 0); +- REQUIRE_EQ(wpid, fpid); +- ATF_REQUIRE(WIFSTOPPED(status)); +- REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ++ pt_sc_remote(fpid, &pscr, 0, fpid); + + pscr.pscr_syscall = SYS_getppid; + pscr.pscr_nargs = 0; + pscr.pscr_args = NULL; +- ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) != +- -1); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0, +- "remote getppid failed with error %d", pscr.pscr_ret.sr_error); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == getpid(), +- "unexpected return value %jd instead of %d", +- (intmax_t)pscr.pscr_ret.sr_retval[0], fpid); ++ pt_sc_remote(fpid, &pscr, 0, getpid()); ++ ++ ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1); ++} ++ ++ATF_TC_WITHOUT_HEAD(ptrace__PT_SC_REMOTE_syscall_validation); ++ATF_TC_BODY(ptrace__PT_SC_REMOTE_syscall_validation, tc) ++{ ++ struct ptrace_sc_remote pscr; ++ quad_t code; ++ int status; ++ pid_t fpid, wpid; ++ ++ code = SYS_MAXSYSCALL; ++ ++ ATF_REQUIRE((fpid = fork()) != -1); ++ if (fpid == 0) { ++ trace_me(); ++ exit(0); ++ } + + wpid = waitpid(fpid, &status, 0); + REQUIRE_EQ(wpid, fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); + ++ pscr.pscr_syscall = SYS_MAXSYSCALL; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ ++ pscr.pscr_syscall = SYS_syscall; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, EINVAL, 0); ++ ++ pscr.pscr_syscall = SYS_syscall; ++ pscr.pscr_nargs = 1; ++ pscr.pscr_args = (syscallarg_t *)&code; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ ++ pscr.pscr_syscall = SYS___syscall; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, EINVAL, 0); ++ ++ pscr.pscr_syscall = SYS___syscall; ++ pscr.pscr_nargs = 1; ++ pscr.pscr_args = (syscallarg_t *)&code; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ + ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1); + } + +@@ -4529,6 +4574,7 @@ + ATF_TP_ADD_TC(tp, ptrace__procdesc_wait_child); + ATF_TP_ADD_TC(tp, ptrace__procdesc_reparent_wait_child); + ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_getpid); ++ ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_syscall_validation); + ATF_TP_ADD_TC(tp, ptrace__reap_kill_stopped); + + return (atf_no_error()); diff --git a/website/static/security/patches/SA-26:21/ptrace-14.3.patch.asc b/website/static/security/patches/SA-26:21/ptrace-14.3.patch.asc new file mode 100644 index 0000000000..9f47bb999a --- /dev/null +++ b/website/static/security/patches/SA-26:21/ptrace-14.3.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHgbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvlAMP/10MF70q2uwHdVYqJ8R8 +LNy+2BJDPiPerGbCnbjqtD8ciDAvMRMD6413kMyOrfpMQePSUwyz2y7ByxJPOeca +xkzoCebhuwpf6iXZJQa86OyTR64hDBbGLl4SF9r8Kmd9icSm79AQXmYxRoXeqsJX +x/DHY2q08M2nclIAggF6c10zLvtPdOmJNVTvMcFeEt+gsV6RwRIbvk+5nYLMIBlU ++SQ9M/Q0mYuAlEUZAqtJOPC2iOoetyO6arBr76RhBOB6nxced60QrKZLY/sRdBaE +TTpdkWV5O/E8lj8IbSwzZx8saHo/BS5mT9yJivbDrFSQ6oLds0SZEtGOkXWFLfCR +/aLnp6vypCIWcJ/BkS5hfqP3sAw4nTuGBD9EtG4tdcqV+ys2NFT6vIOo2oJTKuys +JcYmGs1lP0a9iT2DM3SOW1C6CIU/mDmpeZ1uJv11a9ZUo4KSQA7WJhm4NpSke5AP +7+omM3heH+RWzgbysrPywHGIygzWFzhWi6lrE/ys8OC7mHizR+B3DAAhAmdAKa7k +AQMeIIghg5NphPYO8/tBml1QJ3q6oHp4ohkPIppmiI3M3nUxJH3yb/S9klnM3CM4 +pwCYTQn/ph8519OFr4Hjn/RylZvjmdoIthwImZlX2xfyMhZoWFlFRAg+MzR784Yy +FDCL4ifSXzPjx9xqeryZ61fk +=vTiT +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:21/ptrace-14.4.patch b/website/static/security/patches/SA-26:21/ptrace-14.4.patch new file mode 100644 index 0000000000..0dd7cdc3a3 --- /dev/null +++ b/website/static/security/patches/SA-26:21/ptrace-14.4.patch @@ -0,0 +1,154 @@ +--- sys/kern/kern_sig.c.orig ++++ sys/kern/kern_sig.c +@@ -2692,23 +2692,26 @@ + struct sysentvec *sv; + struct sysent *se; + register_t rv_saved[2]; ++ unsigned int sc; + int error, nerror; +- int sc; + bool audited, sy_thr_static; + +- sv = p->p_sysent; +- if (sv->sv_table == NULL || sv->sv_size < tsr->ts_sa.code) { +- tsr->ts_ret.sr_error = ENOSYS; +- return; +- } +- + sc = tsr->ts_sa.code; + if (sc == SYS_syscall || sc == SYS___syscall) { ++ if (tsr->ts_nargs == 0) { ++ tsr->ts_ret.sr_error = EINVAL; ++ return; ++ } + sc = tsr->ts_sa.args[0]; + memmove(&tsr->ts_sa.args[0], &tsr->ts_sa.args[1], + sizeof(register_t) * (tsr->ts_nargs - 1)); + } + ++ sv = p->p_sysent; ++ if (sv->sv_table == NULL || sc >= sv->sv_size) { ++ tsr->ts_ret.sr_error = ENOSYS; ++ return; ++ } + tsr->ts_sa.callp = se = &sv->sv_table[sc]; + + VM_CNT_INC(v_syscall); +--- tests/sys/kern/ptrace_test.c.orig ++++ tests/sys/kern/ptrace_test.c +@@ -4363,6 +4363,25 @@ + REQUIRE_EQ(close(pd), 0); + } + ++static void ++pt_sc_remote(pid_t pid, struct ptrace_sc_remote *pscr, int error, ++ syscallarg_t ret) ++{ ++ pid_t wpid; ++ int status; ++ ++ ATF_REQUIRE(ptrace(PT_SC_REMOTE, pid, (caddr_t)pscr, sizeof(*pscr)) != ++ -1); ++ ATF_REQUIRE_EQ(pscr->pscr_ret.sr_error, error); ++ if (error == 0) ++ ATF_REQUIRE_EQ(pscr->pscr_ret.sr_retval[0], ret); ++ ++ wpid = waitpid(pid, &status, 0); ++ REQUIRE_EQ(wpid, pid); ++ ATF_REQUIRE(WIFSTOPPED(status)); ++ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ++} ++ + /* + * Try using PT_SC_REMOTE to get the PID of a traced child process. + */ +@@ -4387,35 +4406,62 @@ + pscr.pscr_syscall = SYS_getpid; + pscr.pscr_nargs = 0; + pscr.pscr_args = NULL; +- ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) != +- -1); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0, +- "remote getpid failed with error %d", pscr.pscr_ret.sr_error); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == fpid, +- "unexpected return value %jd instead of %d", +- (intmax_t)pscr.pscr_ret.sr_retval[0], fpid); +- +- wpid = waitpid(fpid, &status, 0); +- REQUIRE_EQ(wpid, fpid); +- ATF_REQUIRE(WIFSTOPPED(status)); +- REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ++ pt_sc_remote(fpid, &pscr, 0, fpid); + + pscr.pscr_syscall = SYS_getppid; + pscr.pscr_nargs = 0; + pscr.pscr_args = NULL; +- ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) != +- -1); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0, +- "remote getppid failed with error %d", pscr.pscr_ret.sr_error); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == getpid(), +- "unexpected return value %jd instead of %d", +- (intmax_t)pscr.pscr_ret.sr_retval[0], fpid); ++ pt_sc_remote(fpid, &pscr, 0, getpid()); ++ ++ ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1); ++} ++ ++ATF_TC_WITHOUT_HEAD(ptrace__PT_SC_REMOTE_syscall_validation); ++ATF_TC_BODY(ptrace__PT_SC_REMOTE_syscall_validation, tc) ++{ ++ struct ptrace_sc_remote pscr; ++ quad_t code; ++ int status; ++ pid_t fpid, wpid; ++ ++ code = SYS_MAXSYSCALL; ++ ++ ATF_REQUIRE((fpid = fork()) != -1); ++ if (fpid == 0) { ++ trace_me(); ++ exit(0); ++ } + + wpid = waitpid(fpid, &status, 0); + REQUIRE_EQ(wpid, fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); + ++ pscr.pscr_syscall = SYS_MAXSYSCALL; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ ++ pscr.pscr_syscall = SYS_syscall; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, EINVAL, 0); ++ ++ pscr.pscr_syscall = SYS_syscall; ++ pscr.pscr_nargs = 1; ++ pscr.pscr_args = (syscallarg_t *)&code; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ ++ pscr.pscr_syscall = SYS___syscall; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, EINVAL, 0); ++ ++ pscr.pscr_syscall = SYS___syscall; ++ pscr.pscr_nargs = 1; ++ pscr.pscr_args = (syscallarg_t *)&code; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ + ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1); + } + +@@ -4658,6 +4704,7 @@ + ATF_TP_ADD_TC(tp, ptrace__procdesc_wait_child); + ATF_TP_ADD_TC(tp, ptrace__procdesc_reparent_wait_child); + ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_getpid); ++ ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_syscall_validation); + ATF_TP_ADD_TC(tp, ptrace__reap_kill_stopped); + ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_no_EINTR); + ATF_TP_ADD_TC(tp, ptrace__PT_DETACH_continued); diff --git a/website/static/security/patches/SA-26:21/ptrace-14.4.patch.asc b/website/static/security/patches/SA-26:21/ptrace-14.4.patch.asc new file mode 100644 index 0000000000..7b82a1c802 --- /dev/null +++ b/website/static/security/patches/SA-26:21/ptrace-14.4.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHkbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrviIQP/0J+7WsDP2sa3qN/bPca +m6rVHn83Jo8wnPSaUwUHqiWWCsXZBKSWpMcUneQoknmqArC7l2SNPAhXv3EH01fi +ot63ojivc6/Fi0LPhnLC0XSvOePelm9EAkIuYny106q4mWDO0AQIKLks8AMWyJF7 +nEqUzVkXegoasWYmcS+9s0ddS+mVZdNm9Ajd+7gohX2T1/SK3FgIdJMZaDLx4cUV +3B+44iS557PZLTpEjBSESxVM9132HFY16Fz3HUI6i+Cohrnip9KmSAFbutqmKARa +08xfFDGeuLgkxZJFoiBmF9o3QxiutRlTeGl3l2A2WIKkMuIf4UtEMMaD+9KrMIpP +idi0Sa9utRVc1TKRg2hjb2121b7ZAM3SaCvBbunBmV8Kv051sVRcoElG4QsmZgxf +4rwpSBLQdtIBTjvSwhzTwnPJLcb6Lehb2ntZ55NDelSru/3DODEX7oKqKfst3Bgv +QM+L/UYmedbb9qCe58tCgXOE9GEXn8pL7URUT7zv0zf6nX+Tc4viS9TO8lG65wU6 +c8/QnDt9kxts6/Nzfe5JAnSMxPLExuc7Cka6n625nB0L/q81rCvWISwUpJFa49in +mbeuih5Jggpn/kZ+ifFQIpK59rpHShzumTdIWMZubJOSALjpAXFynvC1si/k/W6/ +xxi7tyKq1Tb/SRsXWk4ChxNZ +=c4Yi +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:21/ptrace-15.patch b/website/static/security/patches/SA-26:21/ptrace-15.patch new file mode 100644 index 0000000000..97f0d5f84c --- /dev/null +++ b/website/static/security/patches/SA-26:21/ptrace-15.patch @@ -0,0 +1,154 @@ +--- sys/kern/kern_sig.c.orig ++++ sys/kern/kern_sig.c +@@ -2678,23 +2678,26 @@ + struct sysentvec *sv; + struct sysent *se; + register_t rv_saved[2]; ++ unsigned int sc; + int error, nerror; +- int sc; + bool audited, sy_thr_static; + +- sv = p->p_sysent; +- if (sv->sv_table == NULL || sv->sv_size < tsr->ts_sa.code) { +- tsr->ts_ret.sr_error = ENOSYS; +- return; +- } +- + sc = tsr->ts_sa.code; + if (sc == SYS_syscall || sc == SYS___syscall) { ++ if (tsr->ts_nargs == 0) { ++ tsr->ts_ret.sr_error = EINVAL; ++ return; ++ } + sc = tsr->ts_sa.args[0]; + memmove(&tsr->ts_sa.args[0], &tsr->ts_sa.args[1], + sizeof(register_t) * (tsr->ts_nargs - 1)); + } + ++ sv = p->p_sysent; ++ if (sv->sv_table == NULL || sc >= sv->sv_size) { ++ tsr->ts_ret.sr_error = ENOSYS; ++ return; ++ } + tsr->ts_sa.callp = se = &sv->sv_table[sc]; + + VM_CNT_INC(v_syscall); +--- tests/sys/kern/ptrace_test.c.orig ++++ tests/sys/kern/ptrace_test.c +@@ -4362,6 +4362,25 @@ + REQUIRE_EQ(close(pd), 0); + } + ++static void ++pt_sc_remote(pid_t pid, struct ptrace_sc_remote *pscr, int error, ++ syscallarg_t ret) ++{ ++ pid_t wpid; ++ int status; ++ ++ ATF_REQUIRE(ptrace(PT_SC_REMOTE, pid, (caddr_t)pscr, sizeof(*pscr)) != ++ -1); ++ ATF_REQUIRE_EQ(pscr->pscr_ret.sr_error, error); ++ if (error == 0) ++ ATF_REQUIRE_EQ(pscr->pscr_ret.sr_retval[0], ret); ++ ++ wpid = waitpid(pid, &status, 0); ++ REQUIRE_EQ(wpid, pid); ++ ATF_REQUIRE(WIFSTOPPED(status)); ++ REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ++} ++ + /* + * Try using PT_SC_REMOTE to get the PID of a traced child process. + */ +@@ -4386,35 +4405,62 @@ + pscr.pscr_syscall = SYS_getpid; + pscr.pscr_nargs = 0; + pscr.pscr_args = NULL; +- ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) != +- -1); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0, +- "remote getpid failed with error %d", pscr.pscr_ret.sr_error); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == fpid, +- "unexpected return value %jd instead of %d", +- (intmax_t)pscr.pscr_ret.sr_retval[0], fpid); +- +- wpid = waitpid(fpid, &status, 0); +- REQUIRE_EQ(wpid, fpid); +- ATF_REQUIRE(WIFSTOPPED(status)); +- REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); ++ pt_sc_remote(fpid, &pscr, 0, fpid); + + pscr.pscr_syscall = SYS_getppid; + pscr.pscr_nargs = 0; + pscr.pscr_args = NULL; +- ATF_REQUIRE(ptrace(PT_SC_REMOTE, fpid, (caddr_t)&pscr, sizeof(pscr)) != +- -1); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_error == 0, +- "remote getppid failed with error %d", pscr.pscr_ret.sr_error); +- ATF_REQUIRE_MSG(pscr.pscr_ret.sr_retval[0] == getpid(), +- "unexpected return value %jd instead of %d", +- (intmax_t)pscr.pscr_ret.sr_retval[0], fpid); ++ pt_sc_remote(fpid, &pscr, 0, getpid()); ++ ++ ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1); ++} ++ ++ATF_TC_WITHOUT_HEAD(ptrace__PT_SC_REMOTE_syscall_validation); ++ATF_TC_BODY(ptrace__PT_SC_REMOTE_syscall_validation, tc) ++{ ++ struct ptrace_sc_remote pscr; ++ quad_t code; ++ int status; ++ pid_t fpid, wpid; ++ ++ code = SYS_MAXSYSCALL; ++ ++ ATF_REQUIRE((fpid = fork()) != -1); ++ if (fpid == 0) { ++ trace_me(); ++ exit(0); ++ } + + wpid = waitpid(fpid, &status, 0); + REQUIRE_EQ(wpid, fpid); + ATF_REQUIRE(WIFSTOPPED(status)); + REQUIRE_EQ(WSTOPSIG(status), SIGSTOP); + ++ pscr.pscr_syscall = SYS_MAXSYSCALL; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ ++ pscr.pscr_syscall = SYS_syscall; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, EINVAL, 0); ++ ++ pscr.pscr_syscall = SYS_syscall; ++ pscr.pscr_nargs = 1; ++ pscr.pscr_args = (syscallarg_t *)&code; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ ++ pscr.pscr_syscall = SYS___syscall; ++ pscr.pscr_nargs = 0; ++ pscr.pscr_args = NULL; ++ pt_sc_remote(fpid, &pscr, EINVAL, 0); ++ ++ pscr.pscr_syscall = SYS___syscall; ++ pscr.pscr_nargs = 1; ++ pscr.pscr_args = (syscallarg_t *)&code; ++ pt_sc_remote(fpid, &pscr, ENOSYS, 0); ++ + ATF_REQUIRE(ptrace(PT_DETACH, fpid, (caddr_t)1, 0) != -1); + } + +@@ -4657,6 +4703,7 @@ + ATF_TP_ADD_TC(tp, ptrace__procdesc_wait_child); + ATF_TP_ADD_TC(tp, ptrace__procdesc_reparent_wait_child); + ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_getpid); ++ ATF_TP_ADD_TC(tp, ptrace__PT_SC_REMOTE_syscall_validation); + ATF_TP_ADD_TC(tp, ptrace__reap_kill_stopped); + ATF_TP_ADD_TC(tp, ptrace__PT_ATTACH_no_EINTR); + ATF_TP_ADD_TC(tp, ptrace__PT_DETACH_continued); diff --git a/website/static/security/patches/SA-26:21/ptrace-15.patch.asc b/website/static/security/patches/SA-26:21/ptrace-15.patch.asc new file mode 100644 index 0000000000..609bfc84b7 --- /dev/null +++ b/website/static/security/patches/SA-26:21/ptrace-15.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKHobFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrv+mIP/jrd2IsNHPhHsbnSjg16 +fbLtDSzIBAA5NT8ELxD21EvUVrQlIIakjdxmBs9GyssUDQMWdhXJvVUg+Buk0WPo +U6sLJU8KfA1r8R3BVvwQVUilIOZd3rbf2p8v+SnilKxLtHThm0N8N05og4k9J4wj +sbN5cXapiaLeQlrGD9e7qDQRTrxO9XG4qxvipUtjgGqgOPxhfAvPxylieWrQwr80 +V+wpX/gbGLr8psmwYk/4XNkd4tHI4OguNzyOaCgMfcb16AueQBxzgkexlZ1GIxgY +VF8LMr8w/Qfh2ukjTtgvpKJ8G7TDqoZTxqs+c3mxuW66AxX20x8i+WRlo4X0VTjm +lTd77Fejoyff6DI8yIDmTaCblX2OLyZFsoi8cY3TRkK6BVDvW08bSE3ztZZwJO4e +oFVZsONII8GBTRf2yjhOxLaJpheRsff75AqSwTpy8w9XkqQ70n7K8OUsWmHTN2VK +ZpCMGIu6tX7ShxnigVrA3o0O30fg3VuvVqnsbyA7jDyjzUjpywwd12zcQ+0eZz8U +1i2XH/hmZ+0dxArVB19QQ/I2vBSQvqYdRxwmitVOWpxZLWWr6FDLMS8BumHXn2IN +oNbDdiClPyW7+CpJzfrxA5kcnZsBF/aBi/xRZtV/xof4M5LhYDYzbdDWJYgEmW4p +JSVYiYuiRnJmeWi01UJYGWr6 +=I125 +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:22/libcasper-14.patch b/website/static/security/patches/SA-26:22/libcasper-14.patch new file mode 100644 index 0000000000..7cddb28b48 --- /dev/null +++ b/website/static/security/patches/SA-26:22/libcasper-14.patch @@ -0,0 +1,539 @@ +--- lib/libcasper/libcasper/libcasper_impl.h.orig ++++ lib/libcasper/libcasper/libcasper_impl.h +@@ -54,6 +54,8 @@ + void service_start(struct service *service, int sock, int procfd); + const char *service_name(struct service *service); + int service_get_channel_flags(struct service *service); ++bool service_have_connections(void); ++bool service_poll_dispatch(void); + + /* Private service connection functions. */ + struct service_connection *service_connection_add(struct service *service, +@@ -64,10 +66,6 @@ + int service_connection_clone( + struct service *service, + struct service_connection *sconn); +-struct service_connection *service_connection_first( +- struct service *service); +-struct service_connection *service_connection_next( +- struct service_connection *sconn); + cap_channel_t *service_connection_get_chan( + const struct service_connection *sconn); + int service_connection_get_sock( +--- lib/libcasper/libcasper/libcasper_service.c.orig ++++ lib/libcasper/libcasper/libcasper_service.c +@@ -223,10 +223,6 @@ + void + casper_main_loop(int fd) + { +- fd_set fds; +- struct casper_service *casserv; +- struct service_connection *sconn, *sconntmp; +- int sock, maxfd, ret; + + if (zygote_init() < 0) + _exit(1); +@@ -236,55 +232,10 @@ + */ + service_register_core(fd); + +- for (;;) { +- FD_ZERO(&fds); +- FD_SET(fd, &fds); +- maxfd = -1; +- TAILQ_FOREACH(casserv, &casper_services, cs_next) { +- /* We handle only core services. */ +- if (!CSERVICE_IS_CORE(casserv)) +- continue; +- for (sconn = service_connection_first(casserv->cs_service); +- sconn != NULL; +- sconn = service_connection_next(sconn)) { +- sock = service_connection_get_sock(sconn); +- FD_SET(sock, &fds); +- maxfd = sock > maxfd ? sock : maxfd; +- } +- } +- if (maxfd == -1) { +- /* Nothing to do. */ +- _exit(0); +- } +- maxfd++; +- +- +- assert(maxfd <= (int)FD_SETSIZE); +- ret = select(maxfd, &fds, NULL, NULL, NULL); +- assert(ret == -1 || ret > 0); /* select() cannot timeout */ +- if (ret == -1) { +- if (errno == EINTR) +- continue; ++ while (service_have_connections()) { ++ if (!service_poll_dispatch()) + _exit(1); +- } +- +- TAILQ_FOREACH(casserv, &casper_services, cs_next) { +- /* We handle only core services. */ +- if (!CSERVICE_IS_CORE(casserv)) +- continue; +- for (sconn = service_connection_first(casserv->cs_service); +- sconn != NULL; sconn = sconntmp) { +- /* +- * Prepare for connection to be removed from +- * the list on failure. +- */ +- sconntmp = service_connection_next(sconn); +- sock = service_connection_get_sock(sconn); +- if (FD_ISSET(sock, &fds)) { +- service_message(casserv->cs_service, +- sconn); +- } +- } +- } + } ++ ++ _exit(0); + } +--- lib/libcasper/libcasper/service.c.orig ++++ lib/libcasper/libcasper/service.c +@@ -30,8 +30,7 @@ + * SUCH DAMAGE. + */ + +-#include +-#include ++#include + #include + #include + #include +@@ -42,6 +41,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -72,7 +72,8 @@ + int sc_magic; + cap_channel_t *sc_chan; + nvlist_t *sc_limits; +- TAILQ_ENTRY(service_connection) sc_next; ++ struct service *sc_service; ++ size_t sc_pollidx; + }; + + #define SERVICE_MAGIC 0x5e91ce +@@ -82,9 +83,90 @@ + uint64_t s_flags; + service_limit_func_t *s_limit; + service_command_func_t *s_command; +- TAILQ_HEAD(, service_connection) s_connections; + }; + ++#define POLLSET_CHUNK 8 ++static struct pollfd *pollset_pfds; ++static struct service_connection **pollset_conns; ++static size_t pollset_cap; ++static size_t pollset_size; ++ ++static int ++pollset_add(struct service_connection *sconn, int sock) ++{ ++ size_t i, newcap; ++ void *p; ++ ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_pfds[i].fd < 0) ++ break; ++ } ++ if (i == pollset_size) { ++ newcap = roundup2(pollset_size + 1, POLLSET_CHUNK); ++ if (newcap > pollset_cap) { ++ p = reallocarray(pollset_pfds, newcap, ++ sizeof(*pollset_pfds)); ++ if (p == NULL) ++ return (-1); ++ pollset_pfds = p; ++ p = reallocarray(pollset_conns, newcap, ++ sizeof(*pollset_conns)); ++ if (p == NULL) ++ return (-1); ++ pollset_conns = p; ++ pollset_cap = newcap; ++ } ++ pollset_size++; ++ } ++ pollset_pfds[i].fd = sock; ++ pollset_pfds[i].events = POLLIN; ++ pollset_pfds[i].revents = 0; ++ pollset_conns[i] = sconn; ++ sconn->sc_pollidx = i; ++ return (0); ++} ++ ++static void ++pollset_remove(struct service_connection *sconn) ++{ ++ ++ pollset_pfds[sconn->sc_pollidx].fd = -1; ++ pollset_conns[sconn->sc_pollidx] = NULL; ++} ++ ++bool ++service_have_connections(void) ++{ ++ size_t i; ++ ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_pfds[i].fd >= 0) ++ return (true); ++ } ++ return (false); ++} ++ ++bool ++service_poll_dispatch(void) ++{ ++ size_t i; ++ int ret; ++ ++ do { ++ ret = poll(pollset_pfds, pollset_size, -1); ++ } while (ret == -1 && errno == EINTR); ++ if (ret == -1) ++ return (false); ++ ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_pfds[i].revents == 0) ++ continue; ++ service_message(pollset_conns[i]->sc_service, ++ pollset_conns[i]); ++ } ++ return (true); ++} ++ + struct service * + service_alloc(const char *name, service_limit_func_t *limitfunc, + service_command_func_t *commandfunc, uint64_t flags) +@@ -102,7 +184,6 @@ + service->s_limit = limitfunc; + service->s_command = commandfunc; + service->s_flags = flags; +- TAILQ_INIT(&service->s_connections); + service->s_magic = SERVICE_MAGIC; + + return (service); +@@ -111,13 +192,16 @@ + void + service_free(struct service *service) + { +- struct service_connection *sconn; ++ size_t i; + + assert(service->s_magic == SERVICE_MAGIC); + + service->s_magic = 0; +- while ((sconn = service_connection_first(service)) != NULL) +- service_connection_remove(service, sconn); ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_conns[i] != NULL && ++ pollset_conns[i]->sc_service == service) ++ service_connection_remove(service, pollset_conns[i]); ++ } + free(service->s_name); + free(service); + } +@@ -154,8 +238,16 @@ + return (NULL); + } + } ++ sconn->sc_service = service; ++ if (pollset_add(sconn, sock) == -1) { ++ serrno = errno; ++ nvlist_destroy(sconn->sc_limits); ++ (void)cap_unwrap(sconn->sc_chan, NULL); ++ free(sconn); ++ errno = serrno; ++ return (NULL); ++ } + sconn->sc_magic = SERVICE_CONNECTION_MAGIC; +- TAILQ_INSERT_TAIL(&service->s_connections, sconn, sc_next); + return (sconn); + } + +@@ -167,7 +259,7 @@ + assert(service->s_magic == SERVICE_MAGIC); + assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + +- TAILQ_REMOVE(&service->s_connections, sconn, sc_next); ++ pollset_remove(sconn); + sconn->sc_magic = 0; + nvlist_destroy(sconn->sc_limits); + cap_close(sconn->sc_chan); +@@ -197,31 +289,6 @@ + return (sock[1]); + } + +-struct service_connection * +-service_connection_first(struct service *service) +-{ +- struct service_connection *sconn; +- +- assert(service->s_magic == SERVICE_MAGIC); +- +- sconn = TAILQ_FIRST(&service->s_connections); +- assert(sconn == NULL || +- sconn->sc_magic == SERVICE_CONNECTION_MAGIC); +- return (sconn); +-} +- +-struct service_connection * +-service_connection_next(struct service_connection *sconn) +-{ +- +- assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); +- +- sconn = TAILQ_NEXT(sconn, sc_next); +- assert(sconn == NULL || +- sconn->sc_magic == SERVICE_CONNECTION_MAGIC); +- return (sconn); +-} +- + cap_channel_t * + service_connection_get_chan(const struct service_connection *sconn) + { +@@ -330,14 +397,6 @@ + nvlist_destroy(nvlout); + } + +-static int +-fd_add(fd_set *fdsp, int maxfd, int fd) +-{ +- +- FD_SET(fd, fdsp); +- return (fd > maxfd ? fd : maxfd); +-} +- + const char * + service_name(struct service *service) + { +@@ -418,9 +477,6 @@ + void + service_start(struct service *service, int sock, int procfd) + { +- struct service_connection *sconn, *sconntmp; +- fd_set fds; +- int maxfd, nfds; + + assert(service != NULL); + assert(service->s_magic == SERVICE_MAGIC); +@@ -430,43 +486,9 @@ + if (service_connection_add(service, sock, NULL) == NULL) + _exit(1); + +- for (;;) { +- FD_ZERO(&fds); +- maxfd = -1; +- for (sconn = service_connection_first(service); sconn != NULL; +- sconn = service_connection_next(sconn)) { +- maxfd = fd_add(&fds, maxfd, +- service_connection_get_sock(sconn)); +- } +- +- assert(maxfd >= 0); +- assert(maxfd + 1 <= (int)FD_SETSIZE); +- nfds = select(maxfd + 1, &fds, NULL, NULL, NULL); +- if (nfds < 0) { +- if (errno != EINTR) +- _exit(1); +- continue; +- } else if (nfds == 0) { +- /* Timeout. */ +- abort(); +- } +- +- for (sconn = service_connection_first(service); sconn != NULL; +- sconn = sconntmp) { +- /* +- * Prepare for connection to be removed from the list +- * on failure. +- */ +- sconntmp = service_connection_next(sconn); +- if (FD_ISSET(service_connection_get_sock(sconn), &fds)) +- service_message(service, sconn); +- } +- if (service_connection_first(service) == NULL) { +- /* +- * No connections left, exiting. +- */ +- break; +- } ++ while (service_have_connections()) { ++ if (!service_poll_dispatch()) ++ _exit(1); + } + + _exit(0); +--- lib/libcasper/tests/Makefile.orig ++++ lib/libcasper/tests/Makefile +@@ -1,5 +1,13 @@ ++.include + +-.PATH: ${SRCTOP}/tests +-KYUAFILE= yes ++PACKAGE= tests ++ ++ATF_TESTS_C= cap_main_test ++ ++.if ${MK_CASPER} != "no" ++LIBADD+= casper ++CFLAGS+= -DWITH_CASPER ++.endif ++LIBADD+= nv + + .include +--- /dev/null ++++ lib/libcasper/tests/cap_main_test.c +@@ -0,0 +1,142 @@ ++/*- ++ * SPDX-License-Identifier: BSD-2-Clause ++ * ++ * Copyright (c) 2026 Mariusz Zaborski ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#define NCONNECTIONS (FD_SETSIZE + 64) ++#define FD_HEADROOM 64 ++ ++/* Test that file descriptors past FD_SETSIZE (1024) work. */ ++ATF_TC_WITHOUT_HEAD(many_connections); ++ATF_TC_BODY(many_connections, tc) ++{ ++ struct rlimit rl; ++ cap_channel_t *chan; ++ cap_channel_t **clones; ++ size_t i; ++ ++ if (getrlimit(RLIMIT_NOFILE, &rl) != 0) ++ atf_tc_skip("getrlimit: %s", strerror(errno)); ++ if (rl.rlim_max < NCONNECTIONS + FD_HEADROOM) ++ atf_tc_skip("RLIMIT_NOFILE hard cap %ju below required %d", ++ (uintmax_t)rl.rlim_max, NCONNECTIONS + FD_HEADROOM); ++ rl.rlim_cur = rl.rlim_max; ++ ATF_REQUIRE_MSG(setrlimit(RLIMIT_NOFILE, &rl) == 0, ++ "setrlimit: %s", strerror(errno)); ++ ++ chan = cap_init(); ++ ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno)); ++ ++ clones = calloc(NCONNECTIONS, sizeof(*clones)); ++ ATF_REQUIRE(clones != NULL); ++ ++ /* ++ * Every cap_clone(3) adds one more connection to the helper. ++ * After this loop the helper is watching more fds than an ++ * fd_set can hold. ++ */ ++ for (i = 0; i < NCONNECTIONS; i++) { ++ clones[i] = cap_clone(chan); ++ ATF_REQUIRE_MSG(clones[i] != NULL, ++ "cap_clone failed at %zu/%d: %s", ++ i, NCONNECTIONS, strerror(errno)); ++ } ++ ++ for (i = 0; i < NCONNECTIONS; i++) ++ cap_close(clones[i]); ++ free(clones); ++ cap_close(chan); ++} ++ ++#define CHURN_CONNECTIONS 50 ++#define CHURN_CLOSE_STEP 5 ++ ++/* Test that gaps in the file descriptor list do not break casper. */ ++ATF_TC_WITHOUT_HEAD(connection_churn); ++ATF_TC_BODY(connection_churn, tc) ++{ ++ cap_channel_t *chan, *survivor, *extra; ++ cap_channel_t *clones[CHURN_CONNECTIONS]; ++ size_t i, survivor_idx; ++ ++ chan = cap_init(); ++ ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno)); ++ ++ for (i = 0; i < CHURN_CONNECTIONS; i++) { ++ clones[i] = cap_clone(chan); ++ ATF_REQUIRE_MSG(clones[i] != NULL, ++ "cap_clone failed at %zu: %s", i, strerror(errno)); ++ } ++ ++ /* ++ * Close every Nth clone. ++ */ ++ for (i = 0; i < CHURN_CONNECTIONS; i += CHURN_CLOSE_STEP) { ++ cap_close(clones[i]); ++ clones[i] = NULL; ++ } ++ ++ /* ++ * Force a poll() cycle: the helper handles POLLIN on chan and ++ * POLLHUP on the closed clones in the same walk. ++ */ ++ extra = cap_clone(chan); ++ ATF_REQUIRE_MSG(extra != NULL, "cap_clone after churn failed: %s", ++ strerror(errno)); ++ ++ /* A surviving clone must still round-trip. */ ++ survivor_idx = 1; ++ survivor = cap_clone(clones[survivor_idx]); ++ ATF_REQUIRE_MSG(survivor != NULL, ++ "cap_clone on survivor failed: %s", strerror(errno)); ++ ++ cap_close(survivor); ++ cap_close(extra); ++ for (i = 0; i < CHURN_CONNECTIONS; i++) { ++ if (clones[i] != NULL) ++ cap_close(clones[i]); ++ } ++ cap_close(chan); ++} ++ ++ATF_TP_ADD_TCS(tp) ++{ ++ ++ ATF_TP_ADD_TC(tp, many_connections); ++ ATF_TP_ADD_TC(tp, connection_churn); ++ return (atf_no_error()); ++} diff --git a/website/static/security/patches/SA-26:22/libcasper-14.patch.asc b/website/static/security/patches/SA-26:22/libcasper-14.patch.asc new file mode 100644 index 0000000000..1754ed5247 --- /dev/null +++ b/website/static/security/patches/SA-26:22/libcasper-14.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKH0bFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvE1wQAIFN2hAU+76Vf5P6J1w9 +ERxUl9XWB+9e+KCvpJzVprwpM6sLm3/04snX5J3dk3/GJ7ngYEmwO0K8fqF8pqTI +N040UkRk0pIdNPmfzrGZO7vbPhdJeLgiCozzS4x8NLVY297S7pD7chLqw42noJlu +qZ+RLbaBz+5jeyLnO5y4x40GYuUrQPkD3ksRMr9MXDrN/VJZp48mEBc5fpTe+dTG +Ina5PB33C70V/Hu7KtPYCvEMhT139ZY/X6zBXJWkFac+yy5Iw5XpQGgtxmiU53ti +Rfjg1h3lC/mQD1lIvdDBXxhwASqbiqKLBjtl2j+HJbXTw6F1STHr3fl2stPcRu5O +e4cTEfULoraVKhvUa2/rVc3O9UiWT6ttJeqiLSFLMPje+EzsRRPv7BURIgjCX4ZO +XbaAS0lPr49tqhDssSN8V4EUaRe4cJm1/bB+1xydpEKmm/5lbvQyA3GMyKau4jEH +85/E6d6oi0LOK4o5V89tSo0kNiozWHe/uHcZqLCS0FNHtjAhrky+EynKlBD8aQED +IA+O1RJnFENxG8R6aC290KCDupYG3LBxXavYxm8RA5fIStvQX2srpRoUuCAEh9Dr +Nwv+nHZZK4yRssf04uwurf/3SUvuOt06fCkzkQxdFByxgjEVFw2LukYaIExtVYH8 +czK6p++bNvfTzQNAx+Mx/4zS +=hxFw +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:22/libcasper-15.patch b/website/static/security/patches/SA-26:22/libcasper-15.patch new file mode 100644 index 0000000000..fa89b524a3 --- /dev/null +++ b/website/static/security/patches/SA-26:22/libcasper-15.patch @@ -0,0 +1,538 @@ +--- lib/libcasper/libcasper/libcasper_impl.h.orig ++++ lib/libcasper/libcasper/libcasper_impl.h +@@ -54,6 +54,8 @@ + void service_start(struct service *service, int sock, int procfd); + const char *service_name(struct service *service); + int service_get_channel_flags(struct service *service); ++bool service_have_connections(void); ++bool service_poll_dispatch(void); + + /* Private service connection functions. */ + struct service_connection *service_connection_add(struct service *service, +@@ -64,10 +66,6 @@ + int service_connection_clone( + struct service *service, + struct service_connection *sconn); +-struct service_connection *service_connection_first( +- struct service *service); +-struct service_connection *service_connection_next( +- struct service_connection *sconn); + cap_channel_t *service_connection_get_chan( + const struct service_connection *sconn); + int service_connection_get_sock( +--- lib/libcasper/libcasper/libcasper_service.c.orig ++++ lib/libcasper/libcasper/libcasper_service.c +@@ -222,10 +222,6 @@ + void + casper_main_loop(int fd) + { +- fd_set fds; +- struct casper_service *casserv; +- struct service_connection *sconn, *sconntmp; +- int sock, maxfd, ret; + + if (zygote_init() < 0) + _exit(1); +@@ -235,55 +231,10 @@ + */ + service_register_core(fd); + +- for (;;) { +- FD_ZERO(&fds); +- FD_SET(fd, &fds); +- maxfd = -1; +- TAILQ_FOREACH(casserv, &casper_services, cs_next) { +- /* We handle only core services. */ +- if (!CSERVICE_IS_CORE(casserv)) +- continue; +- for (sconn = service_connection_first(casserv->cs_service); +- sconn != NULL; +- sconn = service_connection_next(sconn)) { +- sock = service_connection_get_sock(sconn); +- FD_SET(sock, &fds); +- maxfd = sock > maxfd ? sock : maxfd; +- } +- } +- if (maxfd == -1) { +- /* Nothing to do. */ +- _exit(0); +- } +- maxfd++; +- +- +- assert(maxfd <= (int)FD_SETSIZE); +- ret = select(maxfd, &fds, NULL, NULL, NULL); +- assert(ret == -1 || ret > 0); /* select() cannot timeout */ +- if (ret == -1) { +- if (errno == EINTR) +- continue; ++ while (service_have_connections()) { ++ if (!service_poll_dispatch()) + _exit(1); +- } +- +- TAILQ_FOREACH(casserv, &casper_services, cs_next) { +- /* We handle only core services. */ +- if (!CSERVICE_IS_CORE(casserv)) +- continue; +- for (sconn = service_connection_first(casserv->cs_service); +- sconn != NULL; sconn = sconntmp) { +- /* +- * Prepare for connection to be removed from +- * the list on failure. +- */ +- sconntmp = service_connection_next(sconn); +- sock = service_connection_get_sock(sconn); +- if (FD_ISSET(sock, &fds)) { +- service_message(casserv->cs_service, +- sconn); +- } +- } +- } + } ++ ++ _exit(0); + } +--- lib/libcasper/libcasper/service.c.orig ++++ lib/libcasper/libcasper/service.c +@@ -30,7 +30,7 @@ + * SUCH DAMAGE. + */ + +-#include ++#include + #include + #include + #include +@@ -41,6 +41,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -71,7 +72,8 @@ + int sc_magic; + cap_channel_t *sc_chan; + nvlist_t *sc_limits; +- TAILQ_ENTRY(service_connection) sc_next; ++ struct service *sc_service; ++ size_t sc_pollidx; + }; + + #define SERVICE_MAGIC 0x5e91ce +@@ -81,9 +83,90 @@ + uint64_t s_flags; + service_limit_func_t *s_limit; + service_command_func_t *s_command; +- TAILQ_HEAD(, service_connection) s_connections; + }; + ++#define POLLSET_CHUNK 8 ++static struct pollfd *pollset_pfds; ++static struct service_connection **pollset_conns; ++static size_t pollset_cap; ++static size_t pollset_size; ++ ++static int ++pollset_add(struct service_connection *sconn, int sock) ++{ ++ size_t i, newcap; ++ void *p; ++ ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_pfds[i].fd < 0) ++ break; ++ } ++ if (i == pollset_size) { ++ newcap = roundup2(pollset_size + 1, POLLSET_CHUNK); ++ if (newcap > pollset_cap) { ++ p = reallocarray(pollset_pfds, newcap, ++ sizeof(*pollset_pfds)); ++ if (p == NULL) ++ return (-1); ++ pollset_pfds = p; ++ p = reallocarray(pollset_conns, newcap, ++ sizeof(*pollset_conns)); ++ if (p == NULL) ++ return (-1); ++ pollset_conns = p; ++ pollset_cap = newcap; ++ } ++ pollset_size++; ++ } ++ pollset_pfds[i].fd = sock; ++ pollset_pfds[i].events = POLLIN; ++ pollset_pfds[i].revents = 0; ++ pollset_conns[i] = sconn; ++ sconn->sc_pollidx = i; ++ return (0); ++} ++ ++static void ++pollset_remove(struct service_connection *sconn) ++{ ++ ++ pollset_pfds[sconn->sc_pollidx].fd = -1; ++ pollset_conns[sconn->sc_pollidx] = NULL; ++} ++ ++bool ++service_have_connections(void) ++{ ++ size_t i; ++ ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_pfds[i].fd >= 0) ++ return (true); ++ } ++ return (false); ++} ++ ++bool ++service_poll_dispatch(void) ++{ ++ size_t i; ++ int ret; ++ ++ do { ++ ret = poll(pollset_pfds, pollset_size, -1); ++ } while (ret == -1 && errno == EINTR); ++ if (ret == -1) ++ return (false); ++ ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_pfds[i].revents == 0) ++ continue; ++ service_message(pollset_conns[i]->sc_service, ++ pollset_conns[i]); ++ } ++ return (true); ++} ++ + struct service * + service_alloc(const char *name, service_limit_func_t *limitfunc, + service_command_func_t *commandfunc, uint64_t flags) +@@ -101,7 +184,6 @@ + service->s_limit = limitfunc; + service->s_command = commandfunc; + service->s_flags = flags; +- TAILQ_INIT(&service->s_connections); + service->s_magic = SERVICE_MAGIC; + + return (service); +@@ -110,13 +192,16 @@ + void + service_free(struct service *service) + { +- struct service_connection *sconn; ++ size_t i; + + assert(service->s_magic == SERVICE_MAGIC); + + service->s_magic = 0; +- while ((sconn = service_connection_first(service)) != NULL) +- service_connection_remove(service, sconn); ++ for (i = 0; i < pollset_size; i++) { ++ if (pollset_conns[i] != NULL && ++ pollset_conns[i]->sc_service == service) ++ service_connection_remove(service, pollset_conns[i]); ++ } + free(service->s_name); + free(service); + } +@@ -153,8 +238,16 @@ + return (NULL); + } + } ++ sconn->sc_service = service; ++ if (pollset_add(sconn, sock) == -1) { ++ serrno = errno; ++ nvlist_destroy(sconn->sc_limits); ++ (void)cap_unwrap(sconn->sc_chan, NULL); ++ free(sconn); ++ errno = serrno; ++ return (NULL); ++ } + sconn->sc_magic = SERVICE_CONNECTION_MAGIC; +- TAILQ_INSERT_TAIL(&service->s_connections, sconn, sc_next); + return (sconn); + } + +@@ -166,7 +259,7 @@ + assert(service->s_magic == SERVICE_MAGIC); + assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); + +- TAILQ_REMOVE(&service->s_connections, sconn, sc_next); ++ pollset_remove(sconn); + sconn->sc_magic = 0; + nvlist_destroy(sconn->sc_limits); + cap_close(sconn->sc_chan); +@@ -196,31 +289,6 @@ + return (sock[1]); + } + +-struct service_connection * +-service_connection_first(struct service *service) +-{ +- struct service_connection *sconn; +- +- assert(service->s_magic == SERVICE_MAGIC); +- +- sconn = TAILQ_FIRST(&service->s_connections); +- assert(sconn == NULL || +- sconn->sc_magic == SERVICE_CONNECTION_MAGIC); +- return (sconn); +-} +- +-struct service_connection * +-service_connection_next(struct service_connection *sconn) +-{ +- +- assert(sconn->sc_magic == SERVICE_CONNECTION_MAGIC); +- +- sconn = TAILQ_NEXT(sconn, sc_next); +- assert(sconn == NULL || +- sconn->sc_magic == SERVICE_CONNECTION_MAGIC); +- return (sconn); +-} +- + cap_channel_t * + service_connection_get_chan(const struct service_connection *sconn) + { +@@ -329,14 +397,6 @@ + nvlist_destroy(nvlout); + } + +-static int +-fd_add(fd_set *fdsp, int maxfd, int fd) +-{ +- +- FD_SET(fd, fdsp); +- return (fd > maxfd ? fd : maxfd); +-} +- + const char * + service_name(struct service *service) + { +@@ -417,9 +477,6 @@ + void + service_start(struct service *service, int sock, int procfd) + { +- struct service_connection *sconn, *sconntmp; +- fd_set fds; +- int maxfd, nfds; + + assert(service != NULL); + assert(service->s_magic == SERVICE_MAGIC); +@@ -429,43 +486,9 @@ + if (service_connection_add(service, sock, NULL) == NULL) + _exit(1); + +- for (;;) { +- FD_ZERO(&fds); +- maxfd = -1; +- for (sconn = service_connection_first(service); sconn != NULL; +- sconn = service_connection_next(sconn)) { +- maxfd = fd_add(&fds, maxfd, +- service_connection_get_sock(sconn)); +- } +- +- assert(maxfd >= 0); +- assert(maxfd + 1 <= (int)FD_SETSIZE); +- nfds = select(maxfd + 1, &fds, NULL, NULL, NULL); +- if (nfds < 0) { +- if (errno != EINTR) +- _exit(1); +- continue; +- } else if (nfds == 0) { +- /* Timeout. */ +- abort(); +- } +- +- for (sconn = service_connection_first(service); sconn != NULL; +- sconn = sconntmp) { +- /* +- * Prepare for connection to be removed from the list +- * on failure. +- */ +- sconntmp = service_connection_next(sconn); +- if (FD_ISSET(service_connection_get_sock(sconn), &fds)) +- service_message(service, sconn); +- } +- if (service_connection_first(service) == NULL) { +- /* +- * No connections left, exiting. +- */ +- break; +- } ++ while (service_have_connections()) { ++ if (!service_poll_dispatch()) ++ _exit(1); + } + + _exit(0); +--- lib/libcasper/tests/Makefile.orig ++++ lib/libcasper/tests/Makefile +@@ -1,6 +1,13 @@ +-.PATH: ${SRCTOP}/tests ++.include + + PACKAGE= tests +-KYUAFILE= yes ++ ++ATF_TESTS_C= cap_main_test ++ ++.if ${MK_CASPER} != "no" ++LIBADD+= casper ++CFLAGS+= -DWITH_CASPER ++.endif ++LIBADD+= nv + + .include +--- /dev/null ++++ lib/libcasper/tests/cap_main_test.c +@@ -0,0 +1,142 @@ ++/*- ++ * SPDX-License-Identifier: BSD-2-Clause ++ * ++ * Copyright (c) 2026 Mariusz Zaborski ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++ ++#define NCONNECTIONS (FD_SETSIZE + 64) ++#define FD_HEADROOM 64 ++ ++/* Test that file descriptors past FD_SETSIZE (1024) work. */ ++ATF_TC_WITHOUT_HEAD(many_connections); ++ATF_TC_BODY(many_connections, tc) ++{ ++ struct rlimit rl; ++ cap_channel_t *chan; ++ cap_channel_t **clones; ++ size_t i; ++ ++ if (getrlimit(RLIMIT_NOFILE, &rl) != 0) ++ atf_tc_skip("getrlimit: %s", strerror(errno)); ++ if (rl.rlim_max < NCONNECTIONS + FD_HEADROOM) ++ atf_tc_skip("RLIMIT_NOFILE hard cap %ju below required %d", ++ (uintmax_t)rl.rlim_max, NCONNECTIONS + FD_HEADROOM); ++ rl.rlim_cur = rl.rlim_max; ++ ATF_REQUIRE_MSG(setrlimit(RLIMIT_NOFILE, &rl) == 0, ++ "setrlimit: %s", strerror(errno)); ++ ++ chan = cap_init(); ++ ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno)); ++ ++ clones = calloc(NCONNECTIONS, sizeof(*clones)); ++ ATF_REQUIRE(clones != NULL); ++ ++ /* ++ * Every cap_clone(3) adds one more connection to the helper. ++ * After this loop the helper is watching more fds than an ++ * fd_set can hold. ++ */ ++ for (i = 0; i < NCONNECTIONS; i++) { ++ clones[i] = cap_clone(chan); ++ ATF_REQUIRE_MSG(clones[i] != NULL, ++ "cap_clone failed at %zu/%d: %s", ++ i, NCONNECTIONS, strerror(errno)); ++ } ++ ++ for (i = 0; i < NCONNECTIONS; i++) ++ cap_close(clones[i]); ++ free(clones); ++ cap_close(chan); ++} ++ ++#define CHURN_CONNECTIONS 50 ++#define CHURN_CLOSE_STEP 5 ++ ++/* Test that gaps in the file descriptor list do not break casper. */ ++ATF_TC_WITHOUT_HEAD(connection_churn); ++ATF_TC_BODY(connection_churn, tc) ++{ ++ cap_channel_t *chan, *survivor, *extra; ++ cap_channel_t *clones[CHURN_CONNECTIONS]; ++ size_t i, survivor_idx; ++ ++ chan = cap_init(); ++ ATF_REQUIRE_MSG(chan != NULL, "cap_init failed: %s", strerror(errno)); ++ ++ for (i = 0; i < CHURN_CONNECTIONS; i++) { ++ clones[i] = cap_clone(chan); ++ ATF_REQUIRE_MSG(clones[i] != NULL, ++ "cap_clone failed at %zu: %s", i, strerror(errno)); ++ } ++ ++ /* ++ * Close every Nth clone. ++ */ ++ for (i = 0; i < CHURN_CONNECTIONS; i += CHURN_CLOSE_STEP) { ++ cap_close(clones[i]); ++ clones[i] = NULL; ++ } ++ ++ /* ++ * Force a poll() cycle: the helper handles POLLIN on chan and ++ * POLLHUP on the closed clones in the same walk. ++ */ ++ extra = cap_clone(chan); ++ ATF_REQUIRE_MSG(extra != NULL, "cap_clone after churn failed: %s", ++ strerror(errno)); ++ ++ /* A surviving clone must still round-trip. */ ++ survivor_idx = 1; ++ survivor = cap_clone(clones[survivor_idx]); ++ ATF_REQUIRE_MSG(survivor != NULL, ++ "cap_clone on survivor failed: %s", strerror(errno)); ++ ++ cap_close(survivor); ++ cap_close(extra); ++ for (i = 0; i < CHURN_CONNECTIONS; i++) { ++ if (clones[i] != NULL) ++ cap_close(clones[i]); ++ } ++ cap_close(chan); ++} ++ ++ATF_TP_ADD_TCS(tp) ++{ ++ ++ ATF_TP_ADD_TC(tp, many_connections); ++ ATF_TP_ADD_TC(tp, connection_churn); ++ return (atf_no_error()); ++} diff --git a/website/static/security/patches/SA-26:22/libcasper-15.patch.asc b/website/static/security/patches/SA-26:22/libcasper-15.patch.asc new file mode 100644 index 0000000000..ba65d33054 --- /dev/null +++ b/website/static/security/patches/SA-26:22/libcasper-15.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKH4bFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrv3kEQAMJU7f++QUOGUqTiANS4 +69poIhn4KaawwwzRRblcMRVYVhBZoT92YJUOauXILbNsqQZjQ5K4kdW/mgBk3ivw +v7P8y3Bc9t/ZWX4Z9tBB5KmmDhMKdShWTHaHZiCYliJGW+Yzaki/zPBJ+R3vPETd +Cm/X8rvxjMh4tM9HTqaNCrT+DfjAYxS/CCi+mUaNKiP6tLSPAUWYfc2WtBY85OvS +sRVxFBpFPtVwxuVM0XhAIBrPq9fIxILdT3bLi7Q85epLjstoXirCzEjpqhnYE/6m +huyAZCe54qWt+ZhdiRwsLUV/Asom3cLlkuBv0c4QK4tfpZaQqyysAB8+QrNLZ7L8 +V+SnBu8RwEWlsviIf0aWkVnM/YKyoTPs3Wqm7yuWgMz0DuiVuNUs99wLWwDmeAwX +2XA86LsLpi5MPgd7oX2Py4XUKtkiVS7zuKRYW/i5DqSCarUm+wV3v/5EoYwNHVmD +P+Idbe9XkwwYYT+MIHmxYAi2E/jmCvp2RA724K/Iy10Nv0vKz4bbf69P9RPfSnrM +d+H1A6o4LEo5tGS+gcln9rTyg3PME1fXGri1vLT8JJauTQfn1OV0KX2LsZKHAq1i +O4P6M6iUuqupwEAOiqZ39olo241exOzsyP7mXPIwYtS1xi/npWEHrwz2rHEPvD2X +XoXsfguRWHH1Gu7LatZ+XlFm +=B6VF +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:23/bsdinstall-14.patch b/website/static/security/patches/SA-26:23/bsdinstall-14.patch new file mode 100644 index 0000000000..ed81ef5e70 --- /dev/null +++ b/website/static/security/patches/SA-26:23/bsdinstall-14.patch @@ -0,0 +1,102 @@ +--- usr.sbin/bsdconfig/share/media/wlan.subr.orig ++++ usr.sbin/bsdconfig/share/media/wlan.subr +@@ -813,6 +813,7 @@ + [ $nmatches -le ${#DIALOG_MENU_TAGS} ] || break + f_substr -v tag "$DIALOG_MENU_TAGS" $nmatches 1 + ++ f_shell_escape "$wssid" wssid + f_wireless_describe WIRELESS_$n help + menu_list1="$menu_list1 + '$tag $wssid' '$wbssid' '$help' +@@ -1076,6 +1077,7 @@ + while [ $n -lt $nunique ]; do + n=$(( $n + 1 )) + menuitem_$n get ssid ssid ++ f_shell_escape "$ssid" ssid + + menuitem_$n get nconfigs nconfigs + desc="$nconfigs $msg_configured_lc" +@@ -1184,6 +1186,7 @@ + while [ $n -lt $nunique ]; do + n=$(( $n + 1 )) + menuitem_$n get ssid ssid ++ f_shell_escape "$ssid" ssid + + desc= + if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then +--- usr.sbin/bsdinstall/scripts/wlanconfig.orig ++++ usr.sbin/bsdinstall/scripts/wlanconfig +@@ -147,6 +147,34 @@ + country_set "$regdomain" "$country" + } + ++dialog_network_select() ++{ ++ local ssid flags height width rows prompt ++ ++ # Avoid using eval on untrusted data. ++ set -- ++ while IFS=$'\t' read -r ssid flags; do ++ [ -n "$ssid" ] || continue ++ set -- "$@" "$ssid" "$flags" ++ done <&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ++} ++ + ############################################################ MAIN + + : > "$BSDINSTALL_TMPETC/wpa_supplicant.conf" +@@ -213,27 +241,14 @@ + + f_eval_catch -dk SCAN_RESULTS wlanconfig wpa_cli "wpa_cli scan_results" + NETWORKS=$( echo "$SCAN_RESULTS" | awk -F '\t' ' +- /..:..:..:..:..:../ && $5 { printf "\"%s\"\t\"%s\"\n", $5, $4 } ++ /..:..:..:..:..:../ && $5 { print $5 "\t" $4 } + ' | sort | uniq ) + + if [ ! "$NETWORKS" ]; then + f_dialog_title "$msg_error" + f_yesno "No wireless networks were found. Rescan?" && continue + else +- f_dialog_title "Network Selection" +- prompt="Select a wireless network to connect to." +- f_dialog_menu_size height width rows "$DIALOG_TITLE" \ +- "$DIALOG_BACKTITLE" "$prompt" "" $menu_list +- NETWORK=$( eval $DIALOG \ +- --title \"\$DIALOG_TITLE\" \ +- --backtitle \"\$DIALOG_BACKTITLE\" \ +- --extra-button \ +- --extra-label \"Rescan\" \ +- --menu \"\$prompt\" \ +- $height $width $rows \ +- $NETWORKS \ +- 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD +- ) ++ NETWORK=$( dialog_network_select ) + fi + retval=$? + f_dialog_data_sanitize NETWORK +@@ -270,7 +285,7 @@ + done + + [ "$ENCRYPTION" ] || ENCRYPTION=$( echo "$NETWORKS" | +- awk -F '\t' "/^\"$NETWORK\"\t/ { print \$2 }" ) ++ awk -F '\t' "/^$NETWORK\t/ { print \$2 }" ) + + if echo "$ENCRYPTION" | grep -q PSK; then + PASS=$( $DIALOG \ diff --git a/website/static/security/patches/SA-26:23/bsdinstall-14.patch.asc b/website/static/security/patches/SA-26:23/bsdinstall-14.patch.asc new file mode 100644 index 0000000000..1a58120aa4 --- /dev/null +++ b/website/static/security/patches/SA-26:23/bsdinstall-14.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKIAbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvVmgP/ikDqOYGMuEuPfDArmAt +px7cSmVbijPdRRJLcYvboUfRsT/5PtYIJiVTRdr6kG/BH9xz28XNwzm6IfyUbsEO +XwlgRkincO4zXJq6F6ZKJSiHJaM1Px6IkccIRLcZYILzVfULagoQ+OVxkMSntiCR +KRSiqeoG5qBswUbU3jxtlfpKv3Ayk+H4+u7ELPi/zxLXLOuZWgjm8/oF/KJx4CmQ +eqnBaWxd5zswLG+jZ7PZ3wc2s5qw0b2xbOHPdjxyikvkaqgc38cbjJQBKHzfCrpi +4KBkqs9tZY9QotNzZDznfgnJS3skymRJs9uDrhQwO/StC+iu4WuscU/73OWt/x2Z +QKJ3WKyJVfVvSeoQqF0h98ynWeUvA/7cqpBnaJCZXlQaRrMrlz8bV83HiE3968hP +AUKdLGEdUWiaZI2sFsVvE/s7794UCQrpd4piODqhcr5VOX9Gp06h1ShCZREIpd0v +lfgen3d8PNcH7sXFFAMerlvqZbL/YZuaWPk/izOHP0dNC04OCM4+3SMO3IFYBc3l +VcTKTu5GcwmURn687E/YoL/2x6Edikz7xmm33lRqTAs5Qn8ohsTs0b4igpZuRQSo +VTjOiwO78akyfYM8AiASwu0qLlgh6nO1n/wBHRIAmFztChWBGyBQTsRvYb1uTA8J +67ktHpJ1y1tnRuCV0ar7RoJL +=hjx8 +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:23/bsdinstall-15.patch b/website/static/security/patches/SA-26:23/bsdinstall-15.patch new file mode 100644 index 0000000000..69d23b373b --- /dev/null +++ b/website/static/security/patches/SA-26:23/bsdinstall-15.patch @@ -0,0 +1,102 @@ +--- usr.sbin/bsdconfig/share/media/wlan.subr.orig ++++ usr.sbin/bsdconfig/share/media/wlan.subr +@@ -813,6 +813,7 @@ + [ $nmatches -le ${#DIALOG_MENU_TAGS} ] || break + f_substr -v tag "$DIALOG_MENU_TAGS" $nmatches 1 + ++ f_shell_escape "$wssid" wssid + f_wireless_describe WIRELESS_$n help + menu_list1="$menu_list1 + '$tag $wssid' '$wbssid' '$help' +@@ -1076,6 +1077,7 @@ + while [ $n -lt $nunique ]; do + n=$(( $n + 1 )) + menuitem_$n get ssid ssid ++ f_shell_escape "$ssid" ssid + + menuitem_$n get nconfigs nconfigs + desc="$nconfigs $msg_configured_lc" +@@ -1184,6 +1186,7 @@ + while [ $n -lt $nunique ]; do + n=$(( $n + 1 )) + menuitem_$n get ssid ssid ++ f_shell_escape "$ssid" ssid + + desc= + if [ "$DIALOG_MENU_WLAN_SHOW_ALL" ]; then +--- usr.sbin/bsdinstall/scripts/wlanconfig.orig ++++ usr.sbin/bsdinstall/scripts/wlanconfig +@@ -147,6 +147,34 @@ + country_set "$regdomain" "$country" + } + ++dialog_network_select() ++{ ++ local ssid flags height width rows prompt ++ ++ # Avoid using eval on untrusted data. ++ set -- ++ while IFS=$'\t' read -r ssid flags; do ++ [ -n "$ssid" ] || continue ++ set -- "$@" "$ssid" "$flags" ++ done <&1 >&$DIALOG_TERMINAL_PASSTHRU_FD ++} ++ + ############################################################ MAIN + + : > "$BSDINSTALL_TMPETC/wpa_supplicant.conf" +@@ -213,27 +241,14 @@ + + f_eval_catch -dk SCAN_RESULTS wlanconfig wpa_cli "wpa_cli scan_results" + NETWORKS=$( echo "$SCAN_RESULTS" | awk -F '\t' ' +- /..:..:..:..:..:../ && $5 { printf "\"%s\"\t\"%s\"\n", $5, $4 } ++ /..:..:..:..:..:../ && $5 { print $5 "\t" $4 } + ' | sort | uniq ) + + if [ ! "$NETWORKS" ]; then + f_dialog_title "$msg_error" + f_yesno "No wireless networks were found. Rescan?" && continue + else +- f_dialog_title "Network Selection" +- prompt="Select a wireless network to connect to." +- f_dialog_menu_size height width rows "$DIALOG_TITLE" \ +- "$DIALOG_BACKTITLE" "$prompt" "" $NETWORKS +- NETWORK=$( eval $DIALOG \ +- --title \"\$DIALOG_TITLE\" \ +- --backtitle \"\$DIALOG_BACKTITLE\" \ +- --extra-button \ +- --extra-label \"Rescan\" \ +- --menu \"\$prompt\" \ +- $height $width $rows \ +- $NETWORKS \ +- 2>&1 >&$DIALOG_TERMINAL_PASSTHRU_FD +- ) ++ NETWORK=$( dialog_network_select ) + fi + retval=$? + f_dialog_data_sanitize NETWORK +@@ -270,7 +285,7 @@ + done + + [ "$ENCRYPTION" ] || ENCRYPTION=$( echo "$NETWORKS" | +- awk -F '\t' "/^\"$NETWORK\"\t/ { print \$2 }" ) ++ awk -F '\t' "/^$NETWORK\t/ { print \$2 }" ) + + if echo "$ENCRYPTION" | grep -q PSK; then + PASS=$( $DIALOG \ diff --git a/website/static/security/patches/SA-26:23/bsdinstall-15.patch.asc b/website/static/security/patches/SA-26:23/bsdinstall-15.patch.asc new file mode 100644 index 0000000000..d015b06731 --- /dev/null +++ b/website/static/security/patches/SA-26:23/bsdinstall-15.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKIEbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrv1e0P/RqYWzbb1NdABp3BvIay +D0WebRVJuItsRDdalt0cXumP76UGTa1KVKawVE6OSkzM3cQ0hktBHFd16uDlLH0n +M3uO61BCz0hPJcalO4PAxT1QO1SOA5bVY1FWB0wvSL1mj9OS35YCbbaxFqlwcTrJ +CDjBXyC4CAD4drbOMmfLKLHCkB7gb1TKgN4gpXsMEyhkeOrBBwKg94p6xdqYfxdI +yn6kX54BKcDy0fgVnWgtVufpNLqz5wNlShw6TsNM07Mu91yFX5n9jv1jaQet1+Jd +QGi1n0r9evuun5BbBzeX2GU6Hq7xwRUYueTEyzPJGn8TBzzjLacEUDGuDA4hDRhI +Y9F/wQJdVJN2Pa5e3CtNXc8VQ1V/W92vAkPAzqPEJDsXWa/qp14bS4GpdXWlwFHj +ucvI00hC/6kmlhTpa4RjtMUG22qkOeIABoy6ah23qTICVQr29E9nMgLS8wQWDR52 +rgM6iX6sQ3mOAleFIqT12kaiT8nE7IJnMTRTjoKBkG6iaePuIdFNkrrQAML04TzO +TMdHsE4qjgFhI7in2LxVuJIlGLqGDt7uj6M36px2vYvVPu6s8758SpYeiIXGfHnR +L7Ihv4P8M48tBIp4yGp10HwYwEBY+CON7NsRCk/gANTBAnPfiHWsjZaH4ZR6f8mg +nQqftY90HhsoCa1C/SRm/HKa +=mgO8 +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:24/cap_net.patch b/website/static/security/patches/SA-26:24/cap_net.patch new file mode 100644 index 0000000000..d2fdd5c208 --- /dev/null +++ b/website/static/security/patches/SA-26:24/cap_net.patch @@ -0,0 +1,60 @@ +--- lib/libcasper/services/cap_net/cap_net.c.orig ++++ lib/libcasper/services/cap_net/cap_net.c +@@ -1122,12 +1122,37 @@ + return (0); + } + ++/* ++ * If the old sublimit restricted a subkey, the new one must too; ++ * a missing subkey means "allow any" at request time. ++ */ ++static bool ++verify_subkeys_present(const nvlist_t *oldfunclimits, ++ const nvlist_t *newfunclimit) ++{ ++ void *cookie; ++ const char *name; ++ ++ if (oldfunclimits == NULL) ++ return (true); ++ ++ cookie = NULL; ++ while ((name = nvlist_next(oldfunclimits, NULL, &cookie)) != NULL) { ++ if (!nvlist_exists(newfunclimit, name)) ++ return (false); ++ } ++ return (true); ++} ++ + static bool + verify_only_sa_newlimts(const nvlist_t *oldfunclimits, + const nvlist_t *newfunclimit) + { + void *cookie; + ++ if (!verify_subkeys_present(oldfunclimits, newfunclimit)) ++ return (false); ++ + cookie = NULL; + while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) { + void *sacookie; +@@ -1200,6 +1225,9 @@ + LIMIT_NV_ADDR2NAME, NULL); + } + ++ if (!verify_subkeys_present(oldfunclimits, newfunclimit)) ++ return (false); ++ + cookie = NULL; + while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) { + if (strcmp(cnvlist_name(cookie), "sockaddr") == 0) { +@@ -1258,6 +1286,9 @@ + LIMIT_NV_NAME2ADDR, NULL); + } + ++ if (!verify_subkeys_present(oldfunclimits, newfunclimit)) ++ return (false); ++ + cookie = NULL; + while (nvlist_next(newfunclimit, NULL, &cookie) != NULL) { + if (strcmp(cnvlist_name(cookie), "hosts") == 0) { diff --git a/website/static/security/patches/SA-26:24/cap_net.patch.asc b/website/static/security/patches/SA-26:24/cap_net.patch.asc new file mode 100644 index 0000000000..ad87a51dd7 --- /dev/null +++ b/website/static/security/patches/SA-26:24/cap_net.patch.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- + +iQJPBAABCgA5FiEEthUnfoEIffdcgYM7bljekB8AGu8FAmoOKIQbFIAAAAAABAAO +bWFudTIsMi41KzEuMTIsMCwzAAoJEG5Y3pAfABrvlq4P/272F0oQ3tEr9MPfrErE +L74yVBNk7EDAJqniPUlFL7sRqpnFem0K2TBSj55j/nGbONYDkBTppM0cN8zsrbyq +iZkF1c6qPPZtq1brDxY5ZQQokUS3+XcOyW13d7ImT8cdh0o+WpfAtEPFYQa/yrM1 +IkQMmkiWMp+/XX6C3wYjYAFlbztx0aRrh7f54FUk6uQL58cdbWH2ZuH74cMkv6AW +0fIBRkwpfqpjTN90BRZq/2ePIVw7e2rw6aqNqAyOURIQAIYY8sucEK5r2YosieMU +SJIpB1ejW6S6egAvuHXx4OD6Xqw4TouCo9GnaPf+PQvER8eQvvUJAfHfJeAjj5+I +5QmGDXz47Zn9fSLiSKrzk4rkGuHHcNGyCbwuYtRXoBlLoRrWousP59KYO1kR63gv +1p7fPitzqHXCfdohN8xkv2RtSWTU9jUXb6h5rEyI5SdBB6kX0w4BbwW5qXwmGWtM +282ey6jNVCIi+xeJ1xwSeHcSuHHj6AhtOANxM2I1xNMhmCMcAX65cGCg0EVYhO6X +9BQXyVJXdojVUNOP3NV1OjbXvSFHHaIJ0iBJOJ38GUl377AXW+KW0cp5yggbZMhD +J3dRdAqTZO9FObwJF86Zd1xyuqySIqVnnycBTboNwoKkRDvRib8vwwQCBCoWSMwj +ge/Ki9+fl13Q/5uoW/dWyf4J +=ci2w +-----END PGP SIGNATURE-----