From: 이상호 <kudo3228@gmail.com>
To: Bob Copeland <me@bobcopeland.com>
Cc: linux-karma-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org, 이상호 <kudo3228@gmail.com>
Subject: [PATCH 2/2] omfs: detect file extent chain cycles
Date: Thu, 2 Jul 2026 07:02:11 +0900 [thread overview]
Message-ID: <20260701220211.822033-3-kudo3228@gmail.com> (raw)
In-Reply-To: <20260701220211.822033-1-kudo3228@gmail.com>
OMFS file block lookup follows an on-disk extent continuation chain
until e_next reaches ~0.
The continuation block number is image-controlled. A crafted filesystem
can point a continuation table back to itself, making read paths loop in
the kernel while resolving a file block.
Detect cycles in OMFS extent continuation chains and return -ELOOP when
a cycle is found. Apply the same detection to truncate-to-zero cleanup.
Fixes: a3ab7155ea21 ("omfs: add directory routines")
Signed-off-by: 이상호 <kudo3228@gmail.com>
---
fs/omfs/file.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 78 insertions(+), 1 deletion(-)
diff --git a/fs/omfs/file.c b/fs/omfs/file.c
index 28f3b113340e..8e4e9660789a 100644
--- a/fs/omfs/file.c
+++ b/fs/omfs/file.c
@@ -17,6 +17,70 @@ static u32 omfs_max_extents(struct omfs_sb_info *sbi, int offset)
sizeof(struct omfs_extent_entry);
}
+static int omfs_extent_table_offset(struct inode *inode, u64 block)
+{
+ return block == inode->i_ino ? OMFS_EXTENT_START : OMFS_EXTENT_CONT;
+}
+
+static int omfs_extent_next(struct inode *inode, u64 block, u64 *next)
+{
+ struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
+ struct buffer_head *bh;
+ struct omfs_extent *oe;
+ u32 extent_count;
+ int offset = omfs_extent_table_offset(inode, block);
+
+ bh = omfs_bread(inode->i_sb, block);
+ if (!bh)
+ return -EIO;
+
+ if (omfs_is_bad(sbi, (struct omfs_header *)bh->b_data, block)) {
+ brelse(bh);
+ return -EIO;
+ }
+
+ oe = (struct omfs_extent *)&bh->b_data[offset];
+ extent_count = be32_to_cpu(oe->e_extent_count);
+ if (extent_count > omfs_max_extents(sbi, offset)) {
+ brelse(bh);
+ return -EIO;
+ }
+
+ *next = be64_to_cpu(oe->e_next);
+ brelse(bh);
+ return 0;
+}
+
+static int omfs_check_extent_cycle(struct inode *inode, u64 *slow, u64 *fast)
+{
+ int err;
+
+ if (*fast == ~0)
+ return 0;
+
+ err = omfs_extent_next(inode, *fast, fast);
+ if (err)
+ return err;
+
+ if (*fast == ~0)
+ return 0;
+
+ err = omfs_extent_next(inode, *fast, fast);
+ if (err)
+ return err;
+
+ if (*slow != ~0) {
+ err = omfs_extent_next(inode, *slow, slow);
+ if (err)
+ return err;
+ }
+
+ if (*slow != ~0 && *slow == *fast)
+ return -ELOOP;
+
+ return 0;
+}
+
void omfs_make_empty_table(struct buffer_head *bh, int offset)
{
struct omfs_extent *oe = (struct omfs_extent *) &bh->b_data[offset];
@@ -35,6 +99,7 @@ int omfs_shrink_inode(struct inode *inode)
struct omfs_extent_entry *entry;
struct buffer_head *bh;
u64 next, last;
+ u64 slow, fast;
u32 extent_count;
u32 max_extents;
int ret;
@@ -43,6 +108,8 @@ int omfs_shrink_inode(struct inode *inode)
* than inode->i_size;
*/
next = inode->i_ino;
+ slow = next;
+ fast = next;
/* only support truncate -> 0 for now */
ret = -EIO;
@@ -70,6 +137,10 @@ int omfs_shrink_inode(struct inode *inode)
next = be64_to_cpu(oe->e_next);
entry = oe->e_entry;
+ ret = omfs_check_extent_cycle(inode, &slow, &fast);
+ if (ret)
+ goto out_brelse;
+
/* ignore last entry as it is the terminator */
for (; extent_count > 1; extent_count--) {
u64 start, count;
@@ -228,6 +299,7 @@ static int omfs_get_block(struct inode *inode, sector_t block,
struct omfs_sb_info *sbi = OMFS_SB(inode->i_sb);
int max_blocks = bh_result->b_size >> inode->i_blkbits;
int remain;
+ u64 slow, fast;
ret = -EIO;
bh = omfs_bread(inode->i_sb, inode->i_ino);
@@ -237,6 +309,8 @@ static int omfs_get_block(struct inode *inode, sector_t block,
oe = (struct omfs_extent *)(&bh->b_data[OMFS_EXTENT_START]);
max_extents = omfs_max_extents(sbi, OMFS_EXTENT_START);
next = inode->i_ino;
+ slow = next;
+ fast = next;
for (;;) {
@@ -262,6 +336,10 @@ static int omfs_get_block(struct inode *inode, sector_t block,
if (next == ~0)
break;
+ ret = omfs_check_extent_cycle(inode, &slow, &fast);
+ if (ret)
+ goto out_brelse;
+
brelse(bh);
bh = omfs_bread(inode->i_sb, next);
if (!bh)
@@ -377,4 +455,3 @@ const struct address_space_operations omfs_aops = {
.bmap = omfs_bmap,
.migrate_folio = buffer_migrate_folio,
};
-
--
2.43.0
prev parent reply other threads:[~2026-07-01 22:02 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-01 22:02 [PATCH 0/2] omfs: detect metadata chain cycles 이상호
2026-07-01 22:02 ` [PATCH 1/2] omfs: detect directory sibling " 이상호
2026-07-01 22:02 ` 이상호 [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260701220211.822033-3-kudo3228@gmail.com \
--to=kudo3228@gmail.com \
--cc=linux-karma-devel@lists.sourceforge.net \
--cc=linux-kernel@vger.kernel.org \
--cc=me@bobcopeland.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.