Re: git: 4bd01d6ae016 - main - fts: refactor to use fd-relative operations internally

Don Lewis <[email protected]>
Newsgroups gmane.os.freebsd.devel.cvs.src
Message-ID <tkrat.7f2fee12777d555a__34706.330944185$1786338537$gmane$org@FreeBSD.org>
On  7 Aug, Don Lewis wrote:
> On  4 Aug, Herbert J. Skuhra wrote:
>> On Mon, 03 Aug 2026 21:13:08 +0200, Alan Somers wrote:
>>> 
>>> The branch main has been updated by asomers:
>>> 
>>> URL: https://cgit.FreeBSD.org/src/commit/?id=4bd01d6ae01632501b63438b8d9a401db9744a78
>>> 
>>> commit 4bd01d6ae01632501b63438b8d9a401db9744a78
>>> Author:     Jitendra Bhati <[email protected]>
>>> AuthorDate: 2026-06-12 17:07:55 +0000
>>> Commit:     Alan Somers <[email protected]>
>>> CommitDate: 2026-08-03 19:12:28 +0000
>>> 
>>>     fts: refactor to use fd-relative operations internally
>>>     
>>>     Replace all _open() calls with _openat() in __fts_open(), fts_read(),
>>>     and fts_children().  Replace statfs() with _fstatfs().
>>>     
>>>     Add fts_dirfd to struct _ftsent, set to the file descriptor of the
>>>     parent directory. Callers can use openat(ent->fts_dirfd, ent->fts_name,
>>>     ...) to access files safely without relying on fts_accpath, which
>>>     enables programs in capability mode to open the files described by
>>>     _ftsent.
>>>     
>>>     This is a preparatory change for fts_openat() which will allow callers
>>>     to provide a pre-opened directory fd, enabling fts(3) traversal inside
>>>     Capsicum capability mode.
>>>     
>>>     Mirror all fts_open() changes to fts_open_b().
>>>     
>>>     As a result of expanding _ftsend, publish new ELF symbol versions for
>>>     fts_openat and related functions.
>>>     
>>>     Sponsored by:   Google LLC (GSoC 2026)
>>>     Reviewed by:    asomers
>>>     Pull Request:   https://github.com/freebsd/freebsd-src/pull/2303
>>> ---
>>>  include/fts.h               |    2 +
>>>  lib/libc/gen/Makefile.inc   |    1 +
>>>  lib/libc/gen/Symbol.map     |   18 +-
>>>  lib/libc/gen/fts-compat15.c | 1355 +++++++++++++++++++++++++++++++++++++++++++
>>>  lib/libc/gen/fts-compat15.h |   97 ++++
>>>  lib/libc/gen/fts.3          |   18 +-
>>>  lib/libc/gen/fts.c          |   49 +-
>>>  7 files changed, 1519 insertions(+), 21 deletions(-)
>> 
>> This change seems to break poudriere:
>> 
>> [00:00:04] Recording filesystem state for prepkg...mtree: ./bin: No such file or directory
>> [ERROR] Unhandled error!
>> [00:00:04] Cleaning up
>> [00:00:04] Unmounting file systems
>> Exiting with status 1
> 
> I am seeing a different error with poudriere-devel:
> 
> [00:00:01] Starting jail CURRENTamd64-default
> Updating /var/run/os-release done.
> [00:00:02] Will build as nobody:nobody (65534:65534)
> [00:00:03] Ports supports: FLAVORS SUBPACKAGES SELECTED_OPTIONS
> [00:00:03] Acquiring build logs lock for CURRENTamd64-default... done
> [00:00:03] Logs: /var/poudriere/data/logs/bulk/CURRENTamd64-default/2026-08-07_15h29m37s
> [00:00:03] Loading MOVED for /var/poudriere/data/.m/CURRENTamd64-default/ref/usr/ports
> [00:00:03] Gathering ports metadata
> [00:00:03] Warning: (misc/freebsd-release-manifests): jexec: jail_attach(41): Operation not permitted
> [00:00:03] Warning: (misc/freebsd-release-manifests): Error: Error looking up dependencies for misc/freebsd-release-manifests
> [00:00:03] Warning: (ports-mgmt/pkg): jexec: jail_attach(41): Operation not permitted
> [00:00:03] Warning: (ports-mgmt/portconfig): jexec: jail_attach(41): Operation not permitted
> [00:00:03] Warning: (ports-mgmt/pkg): Error: Error looking up dependencies for ports-mgmt/pkg
> [00:00:03] Warning: (ports-mgmt/portconfig): Error: Error looking up dependencies for ports-mgmt/portconfig
> [00:00:03] Error: /usr/local/share/poudriere/bulk.sh:gather_port_vars:184:Fatal errors encountered gathering ports metadata
> [CURRENTamd64-default] [2026-08-07_15h29m37s] [crashed] Time: 00:00:01
> [00:00:03] Logs: /var/poudriere/data/logs/bulk/CURRENTamd64-default/2026-08-07_15h29m37s
> 
> 
> When I upgraded my pkg build machine from
> 3abd3d3cf3f034cb3d4250a95a8d6c41e72971ef to
> 30ccf2f48c11e54fc0540510dcec7cd006a2c366, I found that poudriere-devel
> started having jexec failures.  I suspected a kernel problem and started
> to set things up to track down the offending commit.  I was surprised to
> find that using the old world 3abd3d3cf3f034cb3d4250a95a8d6c41e72971ef
> with the new 30ccf2f48c11e54fc0540510dcec7cd006a2c366 kernel did not
> have this problem, which makes this look like a userland problem.
> 
> I did a git bisect to find the problematic commit by doing:
>   make buildworld
>   make installworld
>   cd /usr/ports/ports-mgmt/poudriere-devel && make clean reinstall
>   poudriere bulk -j CURRENTamd64 -C ports-mgmt/poudriere-devel
> at each step.  I did the poudriere-devel build and reinstallation so
> that it is propertly built against and linked to the newly installed
> world.  The result of the final poudriere bulk determines whether the
> iteration is a pass or a fail.  Any failures of the earlier steps are
> handled as skips.
> 
> When I ran git bisect, I got this result:
>   4bd01d6ae01632501b63438b8d9a401db9744a78 is the first 'bad' commit
>   commit 4bd01d6ae01632501b63438b8d9a401db9744a78
>   Author: Jitendra Bhati <[email protected]>
>   Date:   Fri Jun 12 22:37:55 2026 +0530
>  
>       fts: refactor to use fd-relative operations internally
> 
> poudriere-devel runs jexec on the host and not in the jail.  I think the
> permission error should only happen if jexec is run by a user other than
> root.  I added some debug output to the poudriere internal scripts and
> it looks like the uid is root.
> 
> I don't know why jexec would be sensitive to fts, since jexec does not
> use it:
> %ldd /usr/sbin/jexec
> /usr/sbin/jexec:
> 	libjail.so.1 => /lib/libjail.so.1 (0x3704d89e7000)
> 	libutil.so.10 => /lib/libutil.so.10 (0x3704d8ae3000)
> 	libc.so.7 => /lib/libc.so.7 (0x3704d9af8000)
> 	libsys.so.7 => /lib/libsys.so.7 (0x3704da328000)
> 	[vdso] (0x3704d7516000)
> 
> I see that a fix to fts was committed, but uupgrading to
>   commit 2ae8976a3f6a7e4deb1905326145f5c2575264f4
>   Author: Gleb Smirnoff <[email protected]>
>   Date:   Fri Aug 7 13:46:22 2026 -0700
> 
>       ocs_fc: return correct error code from ocs_hw_set_persistent_topology()
> did not make a difference.
> 
> I'm stumped.

If I go back one commit to 9590878fca68e62c63d607da73139698e204d0f0 on
both the host and vm, and rebuild poudriere-devel to use the old fts,
poudriere-devel is able to build ports.

If I then, update the host world to
4bd01d6ae01632501b63438b8d9a401db9744a78 (there are no kernel changes
at that step), poudriere-devel still works.  If I then rebuild and
reinstall poudriere-devel so that it picks up the new fts API, I start
seeing jexec errors again.

I added some debug fprintfs to jail exec and its inputs look OK:
[00:00:02] Loading MOVED for /var/poudriere/data/.m/CURRENTamd64-default/ref/usr/ports
[00:00:03] Gathering ports metadata
[00:00:03] Warning: (ports-mgmt/poudriere-devel): jexec CURRENTamd64-default jid=11 uid=0 euid=0
[00:00:03] Warning: (misc/freebsd-release-manifests): jexec CURRENTamd64-default jid=11 uid=0 euid=0
[00:00:03] Warning: (misc/freebsd-release-manifests): jexec jail_attach() errno=1
[00:00:03] Warning: (misc/freebsd-release-manifests): jexec: jail_attach(11): Operation not permitted
[00:00:03] Warning: (ports-mgmt/pkg): jexec CURRENTamd64-default jid=11 uid=0 euid=0
[00:00:03] Warning: (misc/freebsd-release-manifests): Error: Error looking up dependencies for misc/freebsd-release-manifests
[00:00:03] Warning: (ports-mgmt/pkg): jexec jail_attach() errno=1
[00:00:03] Warning: (ports-mgmt/portconfig): jexec CURRENTamd64-default jid=11 uid=0 euid=0
[00:00:03] Warning: (ports-mgmt/pkg): jexec: jail_attach(11): Operation not permitted
[00:00:03] Warning: (ports-mgmt/portconfig): jexec jail_attach() errno=1
[00:00:03] Warning: (ports-mgmt/portconfig): jexec: jail_attach(11): Operation not permitted
[00:00:03] Warning: (ports-mgmt/pkg): Error: Error looking up dependencies for ports-mgmt/pkg
[00:00:03] Warning: (ports-mgmt/portconfig): Error: Error looking up dependencies for ports-mgmt/portconfig
[00:00:03] Error: /usr/local/share/poudriere/bulk.sh:gather_port_vars:184:Fatal errors encountered gathering ports metadata

but the call fo jail_attach() fails.

I then added some kernel printf calls and found that chroot_refuse_vdir_fds()
is failing with EPERM.  This is not documented in the jail_attach()
man page, but it will fail if the calling process has any file
descriptors that point to directories, since they would open security
holes.  That made me suspect a file descriptor leak in FTS, possibly a
missing CLOEXEC.

Without any deep analysis, I sprinkled some into the code and it seemed
to fix poudriere-devel:
fts.diff (text/x-diff, 1.4 KB)
diff --git a/lib/libc/gen/fts.c b/lib/libc/gen/fts.c
index 204340e4b87f..396e484cd953 100644
--- a/lib/libc/gen/fts.c
+++ b/lib/libc/gen/fts.c
@@ -444,7 +444,7 @@ fts_read(FTS *sp)
 		p->fts_info = fts_stat(sp, p, 1, -1);
 		if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
 			if ((p->fts_symfd = p->fts_dirfd >= 0 ?
-			    _dup(p->fts_dirfd) :
+			    _fcntl(p->fts_dirfd, F_DUPFD_CLOEXEC) :
 			    _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
 				p->fts_errno = errno;
 				p->fts_info = FTS_ERR;
@@ -539,7 +539,7 @@ next:	tmp = p;
 			if (p->fts_info == FTS_D && !ISSET(FTS_NOCHDIR)) {
 				if ((p->fts_symfd =
 				    p->fts_dirfd >= 0 ?
-				    _dup(p->fts_dirfd) :
+				    _fcntl(p->fts_dirfd, F_DUPFD_CLOEXEC) :
 				    _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0) {
 					p->fts_errno = errno;
 					p->fts_info = FTS_ERR;
@@ -682,7 +682,7 @@ fts_children(FTS *sp, int instr)
 		return (sp->fts_child = fts_build(sp, instr));
 
 	if ((fd = sp->fts_cur->fts_dirfd >= 0 ?
-	    _dup(sp->fts_cur->fts_dirfd) :
+	    _fcntl(sp->fts_cur->fts_dirfd, F_DUPFD_CLOEXEC) :
 	    _open(".", O_RDONLY | O_CLOEXEC, 0)) < 0)
 		return (NULL);
 	sp->fts_child = fts_build(sp, instr);
@@ -919,7 +919,7 @@ mem1:				saved_errno = errno;
 		}
 
 		p->fts_level = level;
-		p->fts_dirfd = _dup(_dirfd(dirp));
+		p->fts_dirfd = _fcntl(_dirfd(dirp), F_DUPFD_CLOEXEC);
 		p->fts_parent = sp->fts_cur;
 		p->fts_pathlen = len + dnamlen;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.