[PATCH] jfs: reject negative symlink size in jfs_iget()
From: Hyungmin Lee
Date: Mon Sep 07 2026 - 03:47:06 EST
The on-disk di_size field is copied into the signed inode->i_size
without checking whether the resulting value is negative.
For a symbolic link, a negative size passes the existing
inode->i_size >= IDATASIZE
check and enters the fast-symlink path. jfs_iget() then uses the
negative size directly as an index into the inline symlink buffer:
inode->i_link[inode->i_size] = '\0';
With an on-disk di_size of 0x8000000000000000, inode->i_size becomes
LLONG_MIN and the store generates a general protection fault on a
non-canonical address.
Reject negative symlink sizes before selecting the fast- or
page-backed symlink operations. The affected inode lookup then fails
with -EIO instead of crashing the kernel.
The original crash and the fix were tested on Linux 6.12.108 in an
x86-64 KVM guest with CONFIG_JFS_FS=y. A malformed JFS image was
created with a symlink inode whose di_size is LLONG_MIN. Looking up
that symlink with stat(2) reached the affected path in jfs_iget().
The same unchecked path is present at mainline commit
df2908090cda368b01ff43709f51890076c56157.
Before this change, looking up the symlink caused a GPF followed by a
kernel panic. After this change, the same lookup produces:
jfs_lookup: iget failed on inum 34
stat: can't stat '/mnt/file0/file1': Input/output error
and the guest powers down normally without a GPF or kernel panic.
AI assistance from Claude Code using Claude Opus 4.8 was used to
identify the affected code path, structure the fix, and prepare the
technical documentation and changelog. The author independently
reviewed the analysis and patch, built the resulting kernel, and
verified the fix using the reproducer described above.
Tested with:
make -j16 bzImage
scripts/checkpatch.pl --no-tree --no-signoff
checkpatch reported 0 errors and 0 warnings. A tested reproducer is
available on request.
Fixes: d69e83d99cf8 ("jfs: ensure symlinks are NUL-terminated")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: Claude-Code:Claude-Opus-4.8
Signed-off-by: Hyungmin Lee <hungmin090929@xxxxxxxxx>
---
fs/jfs/inode.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/fs/jfs/inode.c b/fs/jfs/inode.c
index 470976271..9bbac7fec 100644
--- a/fs/jfs/inode.c
+++ b/fs/jfs/inode.c
@@ -46,6 +46,10 @@ struct inode *jfs_iget(struct super_block *sb, unsigned long ino)
inode->i_op = &jfs_dir_inode_operations;
inode->i_fop = &jfs_dir_operations;
} else if (S_ISLNK(inode->i_mode)) {
+ if (inode->i_size < 0) {
+ iget_failed(inode);
+ return ERR_PTR(-EIO);
+ }
if (inode->i_size >= IDATASIZE) {
inode->i_op = &page_symlink_inode_operations;
inode_nohighmem(inode);
--
2.53.0