From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pl1-f182.google.com (mail-pl1-f182.google.com [209.85.214.182]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4B6A83A4F31 for ; Wed, 1 Jul 2026 22:02:23 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.214.182 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782943344; cv=none; b=MD9IHhr9JUKlTOT5NGTEGtWnHhCD7WdWC5bifFgpr/PX6TRC9W1cObw/klhyav84f5EWNlof5oQRBZpSInYfOzoIkW3fCBKk8Y9hY2JhKvmmbgkVSjrLvsdan8ZNLFw7m9zL1jvNlAwjm8FRFuLSilJqwj5v3RRFpr4Gw3dph/s= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782943344; c=relaxed/simple; bh=I9zJ/ELKvS1b9QbGfD/ajk051xS8RPp3jkr1Jx27vPY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=N8JxshDta/sGs0CzGiUl75DanI8JxqAxSJTeqH0mmEhhpHQhx6ixKcmxwWn9kTxPyoyq/nTHwWFZ/hyBDeWKKCMChWpJ7wKc8MxZN3Xqv0/pnQ1FqrBPO0ftjsCWZ9X1ri0fSxEz2zSIQZcp5pHT/7igZvRaFm04QBELp4UrtoU= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=DjnSBevC; arc=none smtp.client-ip=209.85.214.182 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="DjnSBevC" Received: by mail-pl1-f182.google.com with SMTP id d9443c01a7336-2c9f10fa7a3so11106215ad.0 for ; Wed, 01 Jul 2026 15:02:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1782943343; x=1783548143; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=xaQCqntD2G2r4SGvhMzYMbh49s+WZ7/PYMhQxxgui58=; b=DjnSBevCOSH49dBnfffG3tI18XRpJH4RfjuS+FI3ySQMQZgVTJtQ+PP2VWYCDKF0RR wyBDDujlxcC67x9RydOvXJg8DunTfcZ3HuhthLEQIozpRbaP6hQb7vhor1cybzKsQUkU SyYIDFb1zfNTRR/xhlhuN3ZiWdPdHsQ5GxuYBCBaVaXDBWg7mr6veB2xzsEJMbReih4z EoD8wAsnbcXL297IBrNW1tVYIr4HeR1VS3CdBa1lLW2ndiUevGgflrQuRepP4HFifK2Y ghj5T26+nhJAABOhOq+0nnzNyIA+a0ewBA1lFEMJAnhsi8jE451OuwzutsTb02XKg5oG BjSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1782943343; x=1783548143; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=xaQCqntD2G2r4SGvhMzYMbh49s+WZ7/PYMhQxxgui58=; b=WeCX6WvS80a3LLDYLe7gR+IxfJAPekuQcUgbHolamPfQLmkeqLwSzgYyY0gkuL8T2D /Oa8QpwDhGnVERrOn8KjDbK0mVGRD4KhQHQapxnhbRzSbbo1zL8ds4if879YJMabCnRI aazH5d2IC2c+BvRVIhBCyOao+FQ1JfX+Vq1++/jidz6pMQOwn4AMFWRRqZ6q+80JHxuk CP4IEVWIZ9Lg9W3yJp4vMrQRIR/J2A1Fu3sHOQ8SYJ2jz710104fSUl0FJbfNJur22tQ q1nDo7ME0fqb9B5g4MGo97s7rL7/31kgsrTuay1sfh/YaWy7y4IhI1jEWBjHryTbjD9G x2rw== X-Forwarded-Encrypted: i=1; AHgh+RqNbLbDFJZFQO3Ao+Pff2mKDy+yqc839OD6i2wpOvcLqdiwxiO4QLAcuUl9VFUX6LnYGIp2+2LmaXafnik=@vger.kernel.org X-Gm-Message-State: AOJu0Yz2wWzxoJ2hmw8naVfx4Np7r744RVuPSK/Ngjb95bHbf+FWv1D6 dNx41OILbq/4AjAmIVU3dV+fy8Da1m40vBlSYKAk5iNXM0aDlc1z2KbWqyidSg== X-Gm-Gg: AfdE7cm9VE1gPhfGxtdTxRT9FJBUrif1dNkqfKMfTLxDebHq45T9Ku0gPGz6vKpzCti JlAEOWWu24J3hYJ5/AqxJukMu8xyhg1Zl8mmOMn/7yQ4oQJlWQwvoEGY+sv1vDsxlacVYNA3iu5 43GcRL2mOaa6z1wcKeYepepdHwY0kQgbrgcZyfdKB2lXvE3CnAWjvYJEYBvuWxeqYX53lXm7dfm t8jzBt2h6jmz3PQx+PyA+HO7rDoXj7ziJRWTiLRiO0zoptGgOuBtOvZbMdoD2vQ82bfb3snC/f3 dQnY17lfbrXuPas67mrMX9gMDkiSPv2/SMYS71hn17Eb2zNMT2U8n9lExRSrMUrA/uMsbr7VQye s3rLjExdG+YpdOxvINNSG5I/uIj5ZtfnlvFK//vcfeDk3P1BSXZkDLxz6bZ9ZgGUFTmYCBjYpAd sM8xcWA1Eb7aEAyTaaY0+f7BE2zi6HRE0= X-Received: by 2002:a17:903:191:b0:2c2:be43:39bc with SMTP id d9443c01a7336-2ca7e75c372mr37514155ad.22.1782943342607; Wed, 01 Jul 2026 15:02:22 -0700 (PDT) Received: from DESKTOP-L3Q0GIV.localdomain ([203.230.195.19]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-2ca9aa0b7e9sm4171885ad.81.2026.07.01.15.02.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 01 Jul 2026 15:02:21 -0700 (PDT) From: =?UTF-8?q?=EC=9D=B4=EC=83=81=ED=98=B8?= To: Bob Copeland Cc: linux-karma-devel@lists.sourceforge.net, linux-kernel@vger.kernel.org, =?UTF-8?q?=EC=9D=B4=EC=83=81=ED=98=B8?= Subject: [PATCH 2/2] omfs: detect file extent chain cycles Date: Thu, 2 Jul 2026 07:02:11 +0900 Message-ID: <20260701220211.822033-3-kudo3228@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260701220211.822033-1-kudo3228@gmail.com> References: <20260701220211.822033-1-kudo3228@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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: 이상호 --- 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