[PATCH v2 1/2] nsfs: fix u32-vs-bytes unit mismatch in nsfs_fh_to_dentry()
From: Hui Peng
Date: Sat Sep 19 2026 - 07:26:21 EST
In nsfs_fh_to_dentry(), both fh_len and NSFS_FID_SIZE_U32_LATEST (4) are
expressed in units of 4-byte u32 words rather than bytes, whereas
pointer arithmetic on (void *)fid and the byte count passed to
memchr_inv() are in bytes (NSFS_FILE_HANDLE_SIZE_LATEST = 16).
Passing (void *)fid + NSFS_FID_SIZE_U32_LATEST and
fh_len - NSFS_FID_SIZE_U32_LATEST to memchr_inv() inspects bytes
[4 .. fh_len) inside struct nsfs_file_handle (fid->ns_id and
fid->ns_type) instead of the trailing bytes [16 .. fh_len * 4) after
struct nsfs_file_handle. Consequently:
1. Valid zero-padded handles with handle_bytes >= 36 (fh_len >= 9) where
fid->ns_type != 0 (at byte offset 8) are falsely rejected with
-ESTALE.
2. Non-zero trailing garbage in bytes [16 .. fh_len * 4) is ignored when
the upper 32 bits of fid->ns_id (bytes [4..7]) are zero.
Fix this by offsetting (void *)fid by NSFS_FILE_HANDLE_SIZE_LATEST (16)
and multiplying (fh_len - NSFS_FID_SIZE_U32_LATEST) by sizeof(u32).
Fixes: 5222470b2fbb ("nsfs: support file handles")
Assisted-by: LLM
Signed-off-by: Hui Peng <benquike@xxxxxxxxx>
---
v2: Add a Fixes: tag. nsfs_fh_to_dentry(), both NSFS_FID_SIZE_U32_*
constants and this comparison were all added together by 5222470b2fbb
("nsfs: support file handles"), first released in v6.18.
To be explicit about the impact, since a Fixes: tag will get this picked
up for stable: this is NOT a memory-safety bug. (void *)fid + 4 stays
well inside the 16-byte struct nsfs_file_handle, and fh_len - 4 is
smaller than the intended (fh_len - 4) * 4, so the existing scan is
strictly narrower than it should be. The consequences are a spurious
-ESTALE for handles with handle_bytes >= 36, and trailing non-zero bytes
not being rejected as intended.
fs/nsfs.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/fs/nsfs.c b/fs/nsfs.c
index c3b6ae765..a1842e12f 100644
--- a/fs/nsfs.c
+++ b/fs/nsfs.c
@@ -529,8 +529,8 @@
/* Check that any trailing bytes are zero. */
if ((fh_len > NSFS_FID_SIZE_U32_LATEST) &&
- memchr_inv((void *)fid + NSFS_FID_SIZE_U32_LATEST, 0,
- fh_len - NSFS_FID_SIZE_U32_LATEST))
+ memchr_inv((void *)fid + NSFS_FILE_HANDLE_SIZE_LATEST, 0,
+ (fh_len - NSFS_FID_SIZE_U32_LATEST) * sizeof(u32)))
return NULL;
switch (fh_type) {
--
2.43.0