[PATCH v3 14/31] ocfs2: abstract the fast symlink NUL-termination check

From: Joseph Qi

Date: Mon Nov 09 2026 - 07:27:21 EST


Fold the strnlen() test into a predicate that bounds the read itself
against ocfs2_fast_symlink_chars(), which is exactly the space available
for id2.i_symlink in the inode block.

The open-coded version was only safe because the preceding i_size check
had already bailed: i_size comes from disk, so an unbounded strnlen() on
i_size + 1 bytes can run past the end of the buffer. Keeping that
ordering dependency implicit in a helper would be fragile, especially
once the filecheck variant starts calling it, so the predicate returns
false for an oversized i_size and leaves that case to
ocfs2_dinode_has_oversized_fast_symlink().

At the existing call site the size check still runs first, so behaviour
is unchanged.

No functional change intended.

Signed-off-by: Joseph Qi <joseph.qi@xxxxxxxxxxxxxxxxx>
---
fs/ocfs2/inode.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/fs/ocfs2/inode.c b/fs/ocfs2/inode.c
index bf27925dd5c8..dbb4563dbc70 100644
--- a/fs/ocfs2/inode.c
+++ b/fs/ocfs2/inode.c
@@ -207,6 +207,25 @@ static bool ocfs2_dinode_has_oversized_fast_symlink(struct super_block *sb,
return le64_to_cpu(di->i_size) >= ocfs2_fast_symlink_chars(sb);
}

+static bool ocfs2_dinode_has_unterminated_fast_symlink(struct super_block *sb,
+ struct ocfs2_dinode *di)
+{
+ u64 i_size = le64_to_cpu(di->i_size);
+
+ /*
+ * Bound the strnlen() here rather than relying on the caller having
+ * rejected an oversized i_size first. i_size comes from disk, and
+ * ocfs2_fast_symlink_chars() is exactly the space available in the
+ * inode block, so without this bound the read can run past it.
+ * An oversized i_size is reported by
+ * ocfs2_dinode_has_oversized_fast_symlink() instead.
+ */
+ if (i_size >= ocfs2_fast_symlink_chars(sb))
+ return false;
+
+ return strnlen((char *)di->id2.i_symlink, i_size + 1) != i_size;
+}
+
void ocfs2_set_inode_flags(struct inode *inode)
{
unsigned int flags = OCFS2_I(inode)->ip_attr;
@@ -1812,7 +1831,7 @@ int ocfs2_validate_inode_block(struct super_block *sb,
goto bail;
}

- if (strnlen((char *)di->id2.i_symlink, i_size + 1) != i_size) {
+ if (ocfs2_dinode_has_unterminated_fast_symlink(sb, di)) {
rc = ocfs2_error(sb,
"Invalid dinode #%llu: fast symlink is not NUL-terminated at i_size %llu\n",
(unsigned long long)bh->b_blocknr,
--
2.39.3