From: Narek Jilavyan <njilav@gmail.com>
To: Alexander Viro <viro@zeniv.linux.org.uk>,
Christian Brauner <brauner@kernel.org>
Cc: Jan Kara <jack@suse.cz>, Mateusz Guzik <mjguzik@gmail.com>,
linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
Narek Jilavyan <njilav@gmail.com>
Subject: [PATCH] fs: do not cache a symlink length that disagrees with the string
Date: Mon, 17 Aug 2026 08:17:56 +0000 [thread overview]
Message-ID: <20260817081756.4176757-1-njilav@gmail.com> (raw)
inode_set_cached_link() stores a caller-supplied length in i_linklen and
sets IOP_CACHED_LINK. vfs_readlink() then uses that length directly:
if (inode->i_opflags & IOP_CACHED_LINK)
return readlink_copy(buffer, buflen, inode->i_link,
inode->i_linklen);
and readlink_copy() clamps only against the user buffer, not against
the string, so a length larger than the symlink body becomes a
copy_to_user() of adjacent kernel memory - reachable by any process
calling readlink() on such a symlink.
The only thing standing behind the invariant is
VFS_WARN_ON_INODE(strlen(link) != linklen, inode);
which expands to BUILD_BUG_ON_INVALID() unless CONFIG_DEBUG_VFS is set.
On a production kernel it type-checks the expression and evaluates
nothing, so the value is stored unvalidated.
All four in-tree callers are correct today, and notably the two whose
length comes from on-disk metadata (fs/ext4/inode.c, fs/erofs/inode.c)
both re-derive it and reject the inode rather than relying on this
helper. The API should not require that of the next caller.
Validate unconditionally and fail safe: if the length disagrees, warn
and leave IOP_CACHED_LINK clear. i_linklen has exactly one reader in
the tree and it is gated on that flag, and vfs_readlink() falls back to
i_link with a strlen() of its own, so the inode degrades to the
behaviour that predates the cached length instead of disclosing memory.
The check runs once per symlink inode setup, not once per readlink(),
which is what the cache was for.
Fixes: ea3821990719 ("vfs: support caching symlink lengths in inodes")
Signed-off-by: Narek Jilavyan <njilav@gmail.com>
---
include/linux/fs.h | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 50ce731a2..e1d8f2614 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -946,9 +946,19 @@ static inline void inode_state_replace(struct inode *inode,
static inline void inode_set_cached_link(struct inode *inode, char *link, int linklen)
{
- VFS_WARN_ON_INODE(strlen(link) != linklen, inode);
VFS_WARN_ON_INODE(inode->i_opflags & IOP_CACHED_LINK, inode);
inode->i_link = link;
+
+ /*
+ * i_linklen is used as a copy_to_user() length by vfs_readlink(), so it
+ * must not be taken on trust. If it disagrees with the string, leave
+ * IOP_CACHED_LINK clear: vfs_readlink() then falls back to i_link and
+ * recomputes the length with strlen(), which is what it did before the
+ * cached length was introduced.
+ */
+ if (WARN_ON_ONCE(strlen(link) != linklen))
+ return;
+
inode->i_linklen = linklen;
inode->i_opflags |= IOP_CACHED_LINK;
}
--
2.43.0
next reply other threads:[~2026-08-17 8:18 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-17 8:17 Narek Jilavyan [this message]
2026-08-17 15:24 ` [PATCH] fs: do not cache a symlink length that disagrees with the string Mateusz Guzik
2026-08-18 21:35 ` Jan Kara
2026-08-19 13:03 ` [PATCH v2] " Narek Jilavyan
2026-08-17 16:17 ` Narek Jilavyan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260817081756.4176757-1-njilav@gmail.com \
--to=njilav@gmail.com \
--cc=brauner@kernel.org \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=mjguzik@gmail.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.