[PATCH] freevxfs: validate directory entry name lengths
From: Samuel Moelius
Date: Mon Jun 08 2026 - 20:47:40 EST
FreeVxFS directory entries store both a record length and a name
length on disk. vxfs_readdir() advanced by the record length but
passed the unchecked name length to dir_emit(). A malformed directory
entry can therefore make getdents copy name bytes past the end of the
directory page.
Validate each non-empty directory entry before using its name length.
Reject entries whose record length is too small, unaligned, crosses a
filesystem block or the directory limit, or cannot contain the
advertised name bytes.
Assisted-by: Codex:gpt-5.5-cyber-preview
Signed-off-by: Samuel Moelius <sam.moelius@xxxxxxxxxxxxxxx>
---
fs/freevxfs/vxfs_lookup.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/fs/freevxfs/vxfs_lookup.c b/fs/freevxfs/vxfs_lookup.c
index 138e08de976e..2f4349dddb4b 100644
--- a/fs/freevxfs/vxfs_lookup.c
+++ b/fs/freevxfs/vxfs_lookup.c
@@ -40,6 +40,28 @@ const struct file_operations vxfs_dir_operations = {
.setlease = generic_setlease,
};
+static bool vxfs_dirent_valid(struct vxfs_sb_info *sbi,
+ struct vxfs_direct *de, loff_t pos,
+ loff_t limit, unsigned long bsize)
+{
+ unsigned int block_off = pos & (bsize - 1);
+ unsigned int reclen = fs16_to_cpu(sbi, de->d_reclen);
+ unsigned int namelen = fs16_to_cpu(sbi, de->d_namelen);
+
+ if (reclen < VXFS_NAMEMIN)
+ return false;
+ if (reclen & (VXFS_DIRPAD - 1))
+ return false;
+ if (reclen > bsize - block_off)
+ return false;
+ if (reclen > limit - pos)
+ return false;
+ if (namelen > reclen - VXFS_NAMEMIN)
+ return false;
+
+ return true;
+}
+
/**
* vxfs_find_entry - find a mathing directory entry for a dentry
@@ -96,6 +118,10 @@ vxfs_find_entry(struct inode *ip, struct dentry *dp, struct page **ppp)
break;
}
+ if (!vxfs_dirent_valid(sbi, de, pos, limit, bsize)) {
+ vxfs_put_page(pp);
+ return NULL;
+ }
pg_ofs += fs16_to_cpu(sbi, de->d_reclen);
pos += fs16_to_cpu(sbi, de->d_reclen);
if (!de->d_ino)
@@ -246,6 +272,10 @@ vxfs_readdir(struct file *fp, struct dir_context *ctx)
break;
}
+ if (!vxfs_dirent_valid(sbi, de, pos, limit, bsize)) {
+ vxfs_put_page(pp);
+ return -EIO;
+ }
pg_ofs += fs16_to_cpu(sbi, de->d_reclen);
pos += fs16_to_cpu(sbi, de->d_reclen);
if (!de->d_ino)
--
2.43.0