From nobody Sun Oct 18 15:48:52 2026 X-Original-To: dev-commits-src-main@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 4j734j3KZNz6vvnm for ; Sun, 18 Oct 2026 15:48:53 +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) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 4j734h6xr7z4FW6 for ; Sun, 18 Oct 2026 15:48:52 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1792338533; 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=wXMtaBYRkxNF0Ha68eCXRxpPCo9Qhtg+/lsINjwNrfs=; b=Qm+6lig67zQHGgi4HeEl5O+hhEzjOb7Fu/N8Wg6k720i1aIjBv0G6P4fMFzZUY9HiZplvK /oDwaMNwVJmAbYwqb3bJmuX1f92+d1jDxYAeechsFu1ddpfyQWrvW9LvEIzJCNZ5ml5M5B 4HI3uP0czyQyPgq8TLFBUG/SeY9cCAw4zYrA2ALNuizaNNF6MQQ8jLf4Mb+NS7muV2pe8r XP6+dontyqlXo/p4PVIfnM4U+UItkBBGfzmUqzOriBFU+ivHUOlCf6QB3H8c5Nu0CLxaxc SNmOsJzxthmUR4uOY8MN2G3KJo5Qqc3vsAzZcKjzRopA1uVT20lQgCvjk2jWuA== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1792338533; b=eipEZZ8eBzaR2U95NSNazY8ZQq78FTFRJMp/fBg8baiB7pMkcfsshmn3yEeQAErG2DNC1s +YJxDdtB3G6Hiz7wv5bu7Oqn+hUACZo6XFRMsyhkiDzjqNKIa3UaCAHOFgC5/50AQeYMpp FnVfXuIZhgXlHimFfrBJmXMtauvCPgymEVxCGp1VviiMHok5qFutIpO/9qZonmENeX+ZQV pQPj3bcPkneQKqYrWbS87SOsRsBQxsspNRgNRFdek8rcoHS/7dSGbT9eo+V/8eT8IeH7If 0dshQuUV7e3Z5YPH5oQ24QgB1D5fCxcn+94QIFZAVQMpBp5ZF+chxeI03Z7bTg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1792338533; 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=wXMtaBYRkxNF0Ha68eCXRxpPCo9Qhtg+/lsINjwNrfs=; b=w136+I4OWfSXGHpVd2SEnr3VyOb961ICPYBq9N2cxj8hZszQQt79Bl05FlCHYS42kr8KaY 76VD+1PMIhOpoXjOb3y5BurIv6hqtOWtdu6LzGF/+vo3nZdQ37QwK29UovFmkbHz/q6sn2 8KW6o6t14T17Gg6LngCNBImTEvvgV7frmxlO/bydhqBFHgkWjZewbiN379cbH8qPmwvcp1 WssMYbovB++igaWaR1Du8F/sPx3BnxySyphY9UPpWlivU4HwpzJezTtvgQCepTGvbBf/p2 pWhTIiGwjESEAlDVzdZhRl5ZJgVg4jbRM/N46ibWFGFKb/fNCZmxQFDCNzRAMg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4j734h6KWdzwv1 for ; Sun, 18 Oct 2026 15:48:52 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 4457a by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Sun, 18 Oct 2026 15:48:52 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Devin Teske Subject: git: 23b326728778 - main - namei: retry absolute symlink expansions from the native root List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: Commit messages for the main branch of the src repository 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: dteske X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 23b326728778fd3a6c37b33686dea98d010eb1cf Auto-Submitted: auto-generated Date: Sun, 18 Oct 2026 15:48:52 +0000 Message-Id: <6ad4ea64.4457a.7aeca0a0@gitrepo.freebsd.org> The branch main has been updated by dteske: URL: https://cgit.FreeBSD.org/src/commit/?id=23b326728778fd3a6c37b33686dea98d010eb1cf commit 23b326728778fd3a6c37b33686dea98d010eb1cf Author: Devin Teske AuthorDate: 2026-10-18 15:44:12 +0000 Commit: Devin Teske CommitDate: 2026-10-18 15:44:12 +0000 namei: retry absolute symlink expansions from the native root For a Linux ABI process, namei() resolves a path first under the ABI root (compat.linux.emul_path) and, if that fails, restarts the walk of the original path from the native root. Since 459ac3044b51 an absolute symlink target found during the ABI pass stays under the ABI root, so self-contained Linux userlands resolve their own symlinks correctly (PR 289739); but when the target names a file that exists only in the native namespace, the walk fails and the restart cannot recover: it re-walks the original path, whose ABI-visible prefix (the symlink itself) does not exist natively. Casualties are the absolute symlinks planted under the emulator root to integrate the host into the Linux namespace -- among them linux-rl9-fontconfig's /compat/linux/etc/fonts -> /usr/local/etc/fonts (Linux fontconfig cannot load its configuration, degrading font matching to arbitrary results) and similar links from linux-rl9-alsa-lib and linux-rl9-ca-certificates -- and linprocfs' /proc//exe, whose target is a host path: following it fails for any Linux binary whose text lives outside the emulator root (Chromium-based browsers execve /proc/self/exe to spawn helpers), unless procfs(5) mounted at the native /proc masks the failure. Remember the expansion produced while following an absolute symlink during the ABI pass and, when that pass fails with ENOENT before the target has resolved, perform the one allowed native restart walking the saved expansion in place of the original path. The target has resolved once the walk has consumed it, leaving no more than the suffix appended past it; an ENOENT after that point restarts from the original path as it always has. Lookups that succeed under the ABI root are unaffected, preserving 459ac3044b51; lockless lookups are unaffected because cache_fplookup_symlink() already aborts to the locked path for absolute symlinks whenever an ABI root is active. PR: 297426 Fixes: 459ac3044b51 ("namei: Preserve ABI root for absolute symlinks before fallback") MFC after: 1 month Reviewed by: kib Differential Revision: https://reviews.freebsd.org/D58855 --- sys/kern/vfs_lookup.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 105 insertions(+), 1 deletion(-) diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 34e9bb47b532..a1d8a8cd765f 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -613,11 +613,25 @@ namei(struct nameidata *ndp) struct componentname *cnp; struct thread *td; struct pwd *pwd; + char *abi_symlink; /* abs symlink expansion from the ABI pass */ + size_t abi_symlink_len; + size_t abi_suffix_len; /* leftover path past the saved target */ + bool target_resolved; /* walk has consumed that target */ + enum { + NAMEI_ABI_PASS_ABI = 0, + NAMEI_ABI_PASS_SYMLINK, + NAMEI_ABI_PASS_NATIVE, + } abi_pass; int error; enum cache_fpl_status status; cnp = &ndp->ni_cnd; td = curthread; + abi_symlink = NULL; + abi_symlink_len = 0; + abi_suffix_len = 0; + target_resolved = false; + abi_pass = NAMEI_ABI_PASS_ABI; #ifdef INVARIANTS KASSERT((ndp->ni_debugflags & NAMEI_DBG_CALLED) == 0, ("%s: repeated call to namei without NDREINIT", __func__)); @@ -655,7 +669,15 @@ restart: ndp->ni_loopcnt = 0; ndp->ni_vp = NULL; - error = namei_getpath(ndp); + if (__predict_false(abi_pass == NAMEI_ABI_PASS_SYMLINK)) { + /* + * The expansion was installed when abi_pass became + * SYMLINK. It is not reloaded from userspace. + */ + MPASS(cnp->cn_pnbuf != NULL); + error = 0; + } else + error = namei_getpath(ndp); if (__predict_false(error != 0)) { namei_cleanup_cnp(cnp); nameicap_cleanup(ndp, error); @@ -734,6 +756,8 @@ restart: * Locked lookup. */ for (;;) { + size_t prev_pathlen; + ndp->ni_startdir = dp; error = vfs_lookup(ndp); if (error != 0) { @@ -746,10 +770,56 @@ restart: error == ENOENT && was_restarted == 0; if (error != ERESTART && !abi_restart) goto out; + /* + * One restart from the native root is allowed. + * abi_pass only moves ABI -> SYMLINK or ABI -> + * NATIVE; never backwards. If the target of an + * absolute symlink had not yet resolved when the + * ABI pass failed, restart walking the saved + * expansion in place of the original path: the + * target may name a file that exists only in the + * native namespace (host-integration symlinks + * under the ABI root, linprocfs' /proc//exe). + * An ENOENT after the target resolved, or with + * nothing saved, restarts from the original path. + * + * The old path buffer is released before the + * expansion is installed, so namei_cleanup_cnp() + * does not free the saved symlink. + * + * vfs_lookup() subtracts a component from + * ni_pathlen before looking it up, so a miss on + * the target's own last component leaves exactly + * the suffix; only less than that means the walk + * had moved past the target. + */ + if (abi_symlink != NULL && + ndp->ni_pathlen < abi_suffix_len) + target_resolved = true; nameicap_cleanup(ndp, error); pwd_drop(pwd); namei_cleanup_cnp(cnp); NDRESET(ndp); + if (abi_restart && abi_symlink != NULL && + !target_resolved) { + KASSERT(abi_pass == NAMEI_ABI_PASS_ABI, + ("%s: symlink restart from pass %d", + __func__, abi_pass)); + abi_pass = NAMEI_ABI_PASS_SYMLINK; + cnp->cn_pnbuf = abi_symlink; + ndp->ni_pathlen = abi_symlink_len; + abi_symlink = NULL; + } else { + if (abi_restart) { + KASSERT(abi_pass == + NAMEI_ABI_PASS_ABI, + ("%s: native restart from pass %d", + __func__, abi_pass)); + abi_pass = NAMEI_ABI_PASS_NATIVE; + } + uma_zfree(namei_zone, abi_symlink); + abi_symlink = NULL; + } if (abi_restart) was_restarted = ISRESTARTED; ndp->ni_cnd.cn_flags |= was_restarted; @@ -764,14 +834,19 @@ restart: ndp->ni_vp, false, ndp); nameicap_cleanup(ndp, 0); pwd_drop(pwd); + uma_zfree(namei_zone, abi_symlink); NDVALIDATE(ndp); return (0); } + prev_pathlen = ndp->ni_pathlen; error = namei_follow_link(ndp); if (error != 0) break; vput(ndp->ni_vp); dp = ndp->ni_dvp; + if (abi_symlink != NULL && + prev_pathlen <= abi_suffix_len) + target_resolved = true; /* * Check if root directory should replace current directory. */ @@ -789,10 +864,38 @@ restart: */ if ((cnp->cn_flags & ISRESTARTED) != 0) ndp->ni_rootdir = pwd->pwd_rdir; + else if (pwd->pwd_adir != pwd->pwd_rdir) { + /* + * Save the expansion, and the length of + * the not-yet-walked suffix appended past + * the target, so that should the target + * fail to resolve under the ABI root the + * ENOENT handling above can restart from + * the expansion in place of the original + * path. + */ + if (abi_symlink == NULL) + abi_symlink = uma_zalloc(namei_zone, + M_WAITOK); + memcpy(abi_symlink, cnp->cn_pnbuf, + ndp->ni_pathlen); + abi_symlink_len = ndp->ni_pathlen; + abi_suffix_len = prev_pathlen; + target_resolved = false; + } vrele(dp); error = namei_handle_root(ndp, &dp); if (error != 0) goto out; + } else if (target_resolved) { + /* + * Target already consumed under the ABI root + * (relative symlink at or past the end of the + * saved expansion). A later ENOENT is no + * grounds to restart from that expansion. + */ + uma_zfree(namei_zone, abi_symlink); + abi_symlink = NULL; } } vput(ndp->ni_vp); @@ -804,6 +907,7 @@ out: namei_cleanup_cnp(cnp); nameicap_cleanup(ndp, error); pwd_drop(pwd); + uma_zfree(namei_zone, abi_symlink); return (error); }