1
0
mirror of https://git.FreeBSD.org/doc.git synced 2026-06-02 19:35:07 +00:00

Add EN-26:13 and SA-26:18 through SA-26:24.

Approved by:	so
This commit is contained in:
Gordon Tetlow
2026-05-20 14:35:50 -07:00
parent b6f8bb5509
commit d3922bf62f
42 changed files with 4573 additions and 0 deletions
+28
View File
@@ -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"
+4
View File
@@ -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"
@@ -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
<URL:https://security.FreeBSD.org/>.
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 <URL:https://www.FreeBSD.org/handbook/makeworld.html>.
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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://security.FreeBSD.org/advisories/FreeBSD-EN-26:13.freebsd-update.asc>
-----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-----
@@ -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 <URL:https://security.FreeBSD.org/>.
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
<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> 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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://www.cve.org/CVERecord?id=CVE-2026-45250>
The latest revision of this advisory is available at
<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:18.setcred.asc>
-----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-----
@@ -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 <URL:https://security.FreeBSD.org/>.
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
<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> 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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://www.cve.org/CVERecord?id=CVE-2026-45251>
The latest revision of this advisory is available at
<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:19.file.asc>
-----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-----
@@ -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 <URL:https://security.FreeBSD.org/>.
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
<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> 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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://www.cve.org/CVERecord?id=CVE-2026-45252>
The latest revision of this advisory is available at
<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:20.fusefs.asc>
-----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-----
@@ -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 <URL:https://security.FreeBSD.org/>.
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
<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> 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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://www.cve.org/CVERecord?id=CVE-2026-45253>
The latest revision of this advisory is available at
<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:21.ptrace.asc>
-----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-----
@@ -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 <URL:https://security.FreeBSD.org/>.
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 <URL:https://www.FreeBSD.org/handbook/makeworld.html>.
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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://www.cve.org/CVERecord?id=CVE-2026-39461>
The latest revision of this advisory is available at
<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:22.libcasper.asc>
-----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-----
@@ -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 <URL:https://security.FreeBSD.org/>.
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 <URL:https://www.FreeBSD.org/handbook/makeworld.html>.
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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://www.cve.org/CVERecord?id=CVE-2026-45255>
The latest revision of this advisory is available at
<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:23.bsdinstall.asc>
-----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-----
@@ -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 <URL:https://security.FreeBSD.org/>.
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 <URL:https://www.FreeBSD.org/handbook/makeworld.html>.
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 <commit hash>
Or visit the following URL, replacing NNNNNN with the hash:
<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
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
<URL:https://www.cve.org/CVERecord?id=CVE-2026-45254>
The latest revision of this advisory is available at
<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:24.cap_net.asc>
-----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-----
@@ -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}
@@ -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-----
@@ -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;
@@ -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-----
@@ -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;
@@ -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-----
@@ -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 <markj@FreeBSD.org>
+ *
+ * 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 <sys/types.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/procdesc.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/* 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());
+}
@@ -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-----
@@ -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 <markj@FreeBSD.org>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/uio.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <poll.h>
+#include <pthread.h>
+#include <pwd.h>
+#include <string.h>
+#include <unistd.h>
+
+/*
+ * 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 <markj@FreeBSD.org>
+ *
+ * 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 <sys/types.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/procdesc.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include <atf-c.h>
+
+/* 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());
+}
@@ -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-----
@@ -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<ssize_t>(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
*/
@@ -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-----
@@ -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<ssize_t>(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
*/
@@ -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-----
@@ -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<ssize_t>(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
*/
@@ -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-----
@@ -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());
@@ -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-----
@@ -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);
@@ -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-----
@@ -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);
@@ -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-----
@@ -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 <sys/cdefs.h>
-#include <sys/types.h>
+#include <sys/param.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/nv.h>
@@ -42,6 +41,7 @@
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
+#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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 <src.opts.mk>
-.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 <bsd.test.mk>
--- /dev/null
+++ lib/libcasper/tests/cap_main_test.c
@@ -0,0 +1,142 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ *
+ * 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 <sys/resource.h>
+#include <sys/select.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcasper.h>
+
+#include <atf-c.h>
+
+#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());
+}
@@ -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-----
@@ -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 <sys/types.h>
+#include <sys/param.h>
#include <sys/queue.h>
#include <sys/socket.h>
#include <sys/nv.h>
@@ -41,6 +41,7 @@
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
+#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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 <src.opts.mk>
PACKAGE= tests
-KYUAFILE= yes
+
+ATF_TESTS_C= cap_main_test
+
+.if ${MK_CASPER} != "no"
+LIBADD+= casper
+CFLAGS+= -DWITH_CASPER
+.endif
+LIBADD+= nv
.include <bsd.test.mk>
--- /dev/null
+++ lib/libcasper/tests/cap_main_test.c
@@ -0,0 +1,142 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Mariusz Zaborski <oshogbo@FreeBSD.org>
+ *
+ * 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 <sys/resource.h>
+#include <sys/select.h>
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <libcasper.h>
+
+#include <atf-c.h>
+
+#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());
+}
@@ -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-----
@@ -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 <<EOF
+$NETWORKS
+EOF
+
+ 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" "" "$@"
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --extra-button \
+ --extra-label "Rescan" \
+ --menu "$prompt" \
+ $height $width $rows \
+ "$@" \
+ 2>&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 \
@@ -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-----
@@ -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 <<EOF
+$NETWORKS
+EOF
+
+ 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" "" "$@"
+ $DIALOG \
+ --title "$DIALOG_TITLE" \
+ --backtitle "$DIALOG_BACKTITLE" \
+ --extra-button \
+ --extra-label "Rescan" \
+ --menu "$prompt" \
+ $height $width $rows \
+ "$@" \
+ 2>&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 \
@@ -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-----
@@ -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) {
@@ -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-----