From nobody Tue Jun 30 17:20:33 2026 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gqVKG1N3Mz6jY4V for ; Tue, 30 Jun 2026 17:20:34 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gqVKF3KfFz3qdh for ; Tue, 30 Jun 2026 17:20:33 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840033; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=76AnUywevDdzn6sQk1aUF5AtIVUZRa49vnwjVB6BwdY=; b=SYLhdVANGeHsPzCeVFPOa+kaFEBrsX1kMTSwMtT/eOsp5GOo1yVQr9ddklBjXRl2HJa76z ImaAvWEEoLDaU/NijQUobr/Ob7v+Rp65Wyj1pUdSacj4wA0+uUT/ltQtIJTv9YYNauXyPq z6DK8yhnrU4VH4sesDu7PhwvADm/2eKImvG/0bgQ9aMdXCG9H5mbsAPtcnTGgxYje8Y7CA P6DElYVV0dEFzqHXU3f+HmRzFvAQnQkshH3K9lDil+4kEvTubOZfFJp+vjO9k9zsJj+Nu/ SDiBB82OhIPIXpI7lNVE+RsafpJnQFgFH9SthuSVcbhu7XIvDQtstXhoRBQvAw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1782840033; a=rsa-sha256; cv=none; b=uCdXiBbEniNjLmtutDZ9BwX3pk6yq8DHCwvgyj7MaR4zmgVVDul6susyAYAWf+exU5lV4y cpb9keUo6Jcgf265x5+NvAON6k3RqZtkj9MIt8vI0hrn9nfqRGvIAa16YthkuZ+0uQ3XhQ utGRAExNkiT9+4EvWySfilrpY+ZAT0a3IkNPm/V1w5+nh90izlgnnQKPguvIa+hZ4rqz5L 0dfRDvmiGFHKdKYHdTr/eXT6LdSi/6ynOflm4G2Dzlu59ajsKUfGWFj5NAvr36XRorjDAV xIym4tiMiRmz1tuL+xwcSF8m55CuYc0KWkD057m4JwCcGvtIV4OgWzEc6vLQCQ== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1782840033; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=76AnUywevDdzn6sQk1aUF5AtIVUZRa49vnwjVB6BwdY=; b=Dj1r6DMCt1hw2FSvAURd6A0a/phXi83xMWsNu6E84oyFYBKQy/zaPCmhZCEIMa9E49KT9T db8NjEoyHZIs0X9wqs5GF/e3umGz6QXQF9ReM1GfGtety/zvZsLVS10UQ+xi3cvtV4yVLq SZI+TMecphx4LiQ5e7lvlRAbICHSSJhGct4UPJNx1cvsZ3nKkU4DFjsoZys9ufxjekkMKJ OdTkHbkALtewX/Vg8P5mytFifL3zhdz0B0s3HUUfAyofDwBCwWgaOpRXjXtWUp5iR+dhdY MFTk4XGTfkhmUq/saspyRnrzu7R945K1CPPaquhzXAWMvPxI7zpa2oyXOAxcTQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gqVKF29FZztDQ for ; Tue, 30 Jun 2026 17:20:33 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 44eed by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 30 Jun 2026 17:20:33 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: acfff8b35bfe - releng/14.3 - unlinkat: Pass flags down to the work functions List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: markj X-Git-Repository: src X-Git-Refname: refs/heads/releng/14.3 X-Git-Reftype: branch X-Git-Commit: acfff8b35bfe31c53f92cf6ac040afa4a9a0f0ef Auto-Submitted: auto-generated Date: Tue, 30 Jun 2026 17:20:33 +0000 Message-Id: <6a43fae1.44eed.26baa565@gitrepo.freebsd.org> The branch releng/14.3 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=acfff8b35bfe31c53f92cf6ac040afa4a9a0f0ef commit acfff8b35bfe31c53f92cf6ac040afa4a9a0f0ef Author: Mark Johnston AuthorDate: 2026-06-23 20:43:53 +0000 Commit: Mark Johnston CommitDate: 2026-06-30 02:32:01 +0000 unlinkat: Pass flags down to the work functions The unlinkat(2) and funlinkat(2) system calls were ignoring AT_RESOLVE_BENEATH. Also pass pathseg through instead of assuming it's UIO_USERSPACE. Add some tests which make sure that AT_RESOLVE_BENEATH is handled properly by various system calls. Approved by: so Security: FreeBSD-SA-26:42.unlinkat Security: CVE-2026-49421 Reported by: Yuxiang Yang, Yizhou Zhao, Ao Wang, Xuewei Feng, Qi Li, and Ke Xu from Tsinghua University using GLM-5.1 from Z.ai Reviewed by: kib Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D57790 --- sys/kern/vfs_syscalls.c | 11 +- tests/sys/kern/Makefile | 1 + tests/sys/kern/resolve_beneath_test.c | 220 ++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+), 5 deletions(-) diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index ded3a87e7f8b..d58cfcd3bcba 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1892,16 +1892,17 @@ sys_unlink(struct thread *td, struct unlink_args *uap) static int kern_funlinkat_ex(struct thread *td, int dfd, const char *path, int fd, - int flag, enum uio_seg pathseg, ino_t oldinum) + int flags, enum uio_seg pathseg, ino_t oldinum) { - if ((flag & ~(AT_REMOVEDIR | AT_RESOLVE_BENEATH)) != 0) + if ((flags & ~(AT_REMOVEDIR | AT_RESOLVE_BENEATH)) != 0) return (EINVAL); - if ((flag & AT_REMOVEDIR) != 0) - return (kern_frmdirat(td, dfd, path, fd, UIO_USERSPACE, 0)); + if ((flags & AT_REMOVEDIR) != 0) + return (kern_frmdirat(td, dfd, path, fd, pathseg, + flags & ~AT_REMOVEDIR)); - return (kern_funlinkat(td, dfd, path, fd, UIO_USERSPACE, 0, 0)); + return (kern_funlinkat(td, dfd, path, fd, pathseg, flags, 0)); } #ifndef _SYS_SYSPROTO_H_ diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index 82bac2aa8b80..a7d9b125b28f 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -29,6 +29,7 @@ ATF_TESTS_C+= procdesc ATF_TESTS_C+= ptrace_test TEST_METADATA.ptrace_test+= timeout="15" ATF_TESTS_C+= reaper +ATF_TESTS_C+= resolve_beneath_test ATF_TESTS_C+= sched_affinity ATF_TESTS_C+= sigaltstack ATF_TESTS_C+= sigwait diff --git a/tests/sys/kern/resolve_beneath_test.c b/tests/sys/kern/resolve_beneath_test.c new file mode 100644 index 000000000000..b83926038ded --- /dev/null +++ b/tests/sys/kern/resolve_beneath_test.c @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2026 The FreeBSD Foundation + * + * This software was written by Mark Johnston under sponsorship from + * the FreeBSD Foundation. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +#include +#include +#include + +#include + +/* + * Verify that AT_RESOLVE_BENEATH is respected by various system calls. + */ + +static int +setup(void) +{ + int fd, tfd; + + ATF_REQUIRE_EQ(0, mkdir("base", 0755)); + ATF_REQUIRE_EQ(0, mkdir("outside", 0755)); + ATF_REQUIRE((tfd = open("outside/target", O_CREAT | O_WRONLY, 0644)) >= + 0); + ATF_REQUIRE(close(tfd) == 0); + ATF_REQUIRE_EQ(0, mkdir("outside/dir", 0755)); + ATF_REQUIRE((tfd = open("base/file", O_CREAT | O_WRONLY, 0644)) >= 0); + ATF_REQUIRE(close(tfd) == 0); + ATF_REQUIRE_EQ(0, mkdir("base/subdir", 0755)); + ATF_REQUIRE((fd = open("base", O_DIRECTORY | O_RDONLY)) >= 0); + return (fd); +} + +ATF_TC_WITHOUT_HEAD(faccessat_beneath); +ATF_TC_BODY(faccessat_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + faccessat(fd, "file", F_OK, AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + faccessat(fd, "../outside/target", F_OK, + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(chflagsat_beneath); +ATF_TC_BODY(chflagsat_beneath, tc) +{ + int fd, ret; + + fd = setup(); + ret = chflagsat(fd, "file", UF_NODUMP, AT_RESOLVE_BENEATH); + if (ret != 0) + ATF_REQUIRE_EQ(EOPNOTSUPP, errno); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + chflagsat(fd, "../outside/target", UF_NODUMP, + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(fchmodat_beneath); +ATF_TC_BODY(fchmodat_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + fchmodat(fd, "file", 0644, AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + fchmodat(fd, "../outside/target", 0644, + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(fchownat_beneath); +ATF_TC_BODY(fchownat_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + fchownat(fd, "file", -1, -1, AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + fchownat(fd, "../outside/target", 0, 0, + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(utimensat_beneath); +ATF_TC_BODY(utimensat_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + utimensat(fd, "file", NULL, AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + utimensat(fd, "../outside/target", NULL, + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(linkat_beneath); +ATF_TC_BODY(linkat_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + linkat(fd, "file", fd, "hardlink", AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + linkat(fd, "../outside/target", fd, "hardlink2", + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(unlinkat_beneath); +ATF_TC_BODY(unlinkat_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + unlinkat(fd, "file", AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + unlinkat(fd, "../outside/target", + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(unlinkat_rmdir_beneath); +ATF_TC_BODY(unlinkat_rmdir_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + unlinkat(fd, "subdir", + AT_REMOVEDIR | AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + unlinkat(fd, "../outside/dir", + AT_REMOVEDIR | AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE_EQ(0, access("outside/dir", F_OK)); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(funlinkat_beneath); +ATF_TC_BODY(funlinkat_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + funlinkat(fd, "file", FD_NONE, AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + funlinkat(fd, "../outside/target", FD_NONE, + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC_WITHOUT_HEAD(funlinkat_rmdir_beneath); +ATF_TC_BODY(funlinkat_rmdir_beneath, tc) +{ + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, mkdir("base/subdir2", 0755)); + ATF_REQUIRE_EQ(0, + funlinkat(fd, "subdir2", FD_NONE, + AT_REMOVEDIR | AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + funlinkat(fd, "../outside/dir", FD_NONE, + AT_REMOVEDIR | AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE_EQ(0, access("outside/dir", F_OK)); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TC(getfhat_beneath); +ATF_TC_HEAD(getfhat_beneath, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); +} +ATF_TC_BODY(getfhat_beneath, tc) +{ + fhandle_t fh; + int fd; + + fd = setup(); + ATF_REQUIRE_EQ(0, + getfhat(fd, "file", &fh, AT_RESOLVE_BENEATH)); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + getfhat(fd, "../outside/target", &fh, + AT_RESOLVE_BENEATH) == -1); + ATF_REQUIRE(close(fd) == 0); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, faccessat_beneath); + ATF_TP_ADD_TC(tp, chflagsat_beneath); + ATF_TP_ADD_TC(tp, fchmodat_beneath); + ATF_TP_ADD_TC(tp, fchownat_beneath); + ATF_TP_ADD_TC(tp, utimensat_beneath); + ATF_TP_ADD_TC(tp, linkat_beneath); + ATF_TP_ADD_TC(tp, unlinkat_beneath); + ATF_TP_ADD_TC(tp, unlinkat_rmdir_beneath); + ATF_TP_ADD_TC(tp, funlinkat_beneath); + ATF_TP_ADD_TC(tp, funlinkat_rmdir_beneath); + ATF_TP_ADD_TC(tp, getfhat_beneath); + return (atf_no_error()); +}