[PATCH] romfs: detect hard link cycles

From: 이상호

Date: Wed Jul 01 2026 - 18:09:21 EST


romfs_iget() follows on-disk hard link entries until it reaches a non-hard
link inode:

pos = be32_to_cpu(ri.spec) & ROMFH_MASK;

The target position is image-controlled, and the loop does not detect
cycles. A crafted romfs image can make the root inode a hard link. The hard
link can point back to itself and leave mount(2) spinning in the kernel.

Reject excessive hard link indirection with -ELOOP. Normal romfs images do
not need long hard link chains. This bounds corrupted-image traversal.
Propagate romfs_iget() errors from lookup because hard link traversal can
now fail with -ELOOP.

Signed-off-by: 이상호 <kudo3228@xxxxxxxxx>
---
fs/romfs/super.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/fs/romfs/super.c b/fs/romfs/super.c
index 794a6051d0b5..2d42f7f95260 100644
--- a/fs/romfs/super.c
+++ b/fs/romfs/super.c
@@ -240,6 +240,8 @@ static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry,
if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
inode = romfs_iget(dir->i_sb, offset);
+ if (IS_ERR(inode))
+ return ERR_CAST(inode);
break;
}

@@ -262,6 +262,8 @@ static const struct inode_operations romfs_dir_inode_operations = {
.lookup = romfs_lookup,
};

+#define ROMFS_MAX_HARDLINK_DEPTH 64
+
/*
* get a romfs inode based on its position in the image (which doubles as the
* inode number)
@@ -274,6 +276,7 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
struct inode *i;
unsigned long nlen;
unsigned nextfh;
+ unsigned int depth = 0;
int ret;
umode_t mode;

@@ -289,6 +292,9 @@ static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
break;

+ if (++depth > ROMFS_MAX_HARDLINK_DEPTH)
+ return ERR_PTR(-ELOOP);
+
pos = be32_to_cpu(ri.spec) & ROMFH_MASK;
}

--
2.43.0