From nobody Sun Oct 18 15:48:52 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 4j734j3Kxsz6vvmR 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 4j734h6fBXz4FQl 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=1792338532; 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=E6vfW9xsKT2Rc3RVjyjeaBYnK8pcContsgzSijpSBFYYYiMajAKdQu3yILKgrAs1Jm1+a0 aTU1q4ysI6SmC7aTKKN8XfOu7auizkHq0ztzKAWb5cb9814ebmb64TMqVwEWJdwSbTMjLE 19N7PFbKWB890s0KEEdAbe2FmVIf3VFP5RLMPqXN6VRPKqnd0tIsAUVGBp0UPKGilBCPQe O/dosdP2swONLHKwhP87ZM91CmmrxHSkAZu8jLVprC5muMB/Ae9wQUCAh1hBUtc6ZY5Wsm 9VyA1ea8TlygibQFZQqYaO7NzWQljr+O10fKtapm7Vnjm3G2tpzHz5GY8fhLzw== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1792338532; b=dpXat8AyDEh9rlcy5PkDT1UK8Q9XFn0Q9gK9529cw7OX1/FDEwQv2cYMs2ObLSepqsTiCh YSSM4f+FoX4JoiBtGv71LBeCcYF/ltv3R4acK8pBxSsEKVFVKf7Baa9YfNUs88dyHCPjCZ xuNGvdRCPsGMwH7bYrPMqmcEbk+bjzVvsi/D24MyN14YZDGHO6yykaEqZNB/zgNtUIC5e9 FQy1YPV5yQQrKayvUl2olciZnu493/iEJBFl0P5LoTE6IYka4MAHU+9pUgov78mHaM4REq dmFyW6LkY/WZlMydusySe6FFCUQ9p8BPMAKR0uy0bQovG4mgzEbsdqrp43TeMw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1792338532; 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=k4VYXsXh8OHtwx1rAoI21/v18QqTeBhIze2Y8TSqS75v7Qcc9uMCE6PokYEg++BiiGG/hi pwdu5Jd/6ODaL4ZZQ2i371JfynkPoknn6+JUBKjb7Cr36fhto9TqXwfzso76REa6QzY3Gt Lze5ZOgolqp9Eaoile6WeZBlWltWZC2NoAYfPiaUyNaUAuNtY8CZdp3d2ESVpLtaK/mZNj 1fUsCYXt68UgS6qAcdhkENGWQDjcpdPB1AS88Annj1cFm3qAEbQcptD55fAZuOY2OuIlwo topfZI6sWlBCdZQzUw5uW4RGfXkLiy5nxLUjOLewZIVQJJBgBlAjUd+6L/5/DQ== 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 4j734h6KVyzwLJ 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-all X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@FreeBSD.org List-Id: Commit messages for all branches 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); }