mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] ufs: reject malformed cylinder summary geometry
@ 2026-07-01 21:57 이상호
  2026-08-01  2:44 ` Ali Ahmet Memis
  0 siblings, 1 reply; 2+ messages in thread
From: 이상호 @ 2026-07-01 21:57 UTC (permalink / raw)
  To: Kees Cook, Al Viro; +Cc: 이상호, linux-fsdevel, linux-kernel

ufs_read_cylinder_structures() allocates uspi->s_cssize bytes for the
cylinder summary area, but it calculates the number of fragments to copy
using the on-disk fs_fshift value and then copies a full s_fsize bytes for
every fragment:

	size = uspi->s_cssize;
	blks = (size + uspi->s_fsize - 1) >> uspi->s_fshift;
	base = space = kmalloc(size, GFP_NOFS);
	...
	memcpy(space, bh->b_data, uspi->s_fsize);

ufs_fill_super() validates fs_fsize, but not the sibling fs_fshift and
fs_cssize fields. A crafted writable UFS/FFS image can therefore set an
inconsistent fs_fshift, or set fs_cssize to a value that does not match the
number of cylinder groups, causing the loop to copy past the end of the
allocated cylinder summary buffer.

Reject inconsistent cylinder summary geometry before writable mount. This
preserves the existing read/write paths for valid filesystems and fails
malformed superblocks before ufs_read_cylinder_structures() can overrun the
kmalloc(s_cssize) buffer.

This matches normal producer output: UFS/FFS tools derive fs_fshift from
fs_fsize and round the cylinder summary table size up to a fragment
boundary.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: 이상호 <kudo3228@gmail.com>
---
Notes:
- Public third-party writeup for the same issue:
  https://psn.af/k/17/
- Unpatched CONFIG_UFS_FS_WRITE=y KASAN replay reproduces the
  ufs_read_cylinder_structures slab-out-of-bounds write.
- Patched replay rejects the malformed image with EINVAL before the
  vulnerable copy loop; no UFS KASAN signature appears.
- The same patch applies cleanly with git apply --check to checked
  mainline/stable refs: origin/master, linux-6.18.y, linux-6.12.y,
  linux-6.6.y, linux-6.1.y, linux-5.15.y, and linux-5.10.y.
- A latest-master raw-source patchcheck also reports vulnerable_shape=present
  and patch_status=applies for fs/ufs/super.c.
- Full logs and reproducer image are available privately on request.

 fs/ufs/super.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/fs/ufs/super.c b/fs/ufs/super.c
index c4831a8b9b3f..c295b637920a 100644
--- a/fs/ufs/super.c
+++ b/fs/ufs/super.c
@@ -725,6 +725,7 @@ static int ufs_fill_super(struct super_block *sb, struct fs_context *fc)
 	struct ufs_buffer_head * ubh;	
 	struct inode *inode;
 	unsigned block_size, super_block_size;
+	u64 expected_cssize;
 	unsigned flags;
 	unsigned super_block_offset;
 	unsigned maxsymlen;
@@ -1139,6 +1140,19 @@ static int ufs_fill_super(struct super_block *sb, struct fs_context *fc)
 		uspi->s_csaddr = fs32_to_cpu(sb, usb1->fs_csaddr);
 
 	uspi->s_cssize = fs32_to_cpu(sb, usb1->fs_cssize);
+	if (uspi->s_fshift != ilog2(uspi->s_fsize)) {
+		pr_err("%s(): fragment size/shift mismatch (%u/%u)\n",
+		       __func__, uspi->s_fsize, uspi->s_fshift);
+		goto failed;
+	}
+	expected_cssize = (u64)uspi->s_ncg * sizeof(struct ufs_csum);
+	expected_cssize = (expected_cssize + uspi->s_fsize - 1) &
+		~((u64)uspi->s_fsize - 1);
+	if (!uspi->s_cssize || uspi->s_cssize != expected_cssize) {
+		pr_err("%s(): invalid cylinder summary size %u (expected %llu)\n",
+		       __func__, uspi->s_cssize, expected_cssize);
+		goto failed;
+	}
 	uspi->s_cgsize = fs32_to_cpu(sb, usb1->fs_cgsize);
 	uspi->s_ntrak = fs32_to_cpu(sb, usb1->fs_ntrak);
 	uspi->s_nsect = fs32_to_cpu(sb, usb1->fs_nsect);
-- 
2.43.0

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [PATCH] ufs: reject malformed cylinder summary geometry
  2026-07-01 21:57 [PATCH] ufs: reject malformed cylinder summary geometry 이상호
@ 2026-08-01  2:44 ` Ali Ahmet Memis
  0 siblings, 0 replies; 2+ messages in thread
From: Ali Ahmet Memis @ 2026-08-01  2:44 UTC (permalink / raw)
  To: Sangho Lee, Kees Cook, Alexander Viro; +Cc: linux-fsdevel, linux-kernel

I confirmed the overflow this patch closes. ufs_read_cylinder_structures()
in fs/ufs/super.c derives blks from (s_cssize + s_fsize - 1) >> s_fshift and
then copies a full s_fsize bytes per iteration into a kmalloc(s_cssize)
buffer, so once s_fshift is inconsistent with s_fsize, or s_cssize is not a
fragment multiple, the loop writes past the allocation.

The two new checks cover both cases. Requiring s_fshift to equal
ilog2(s_fsize) keeps the shift that produces blks correct, and requiring
s_cssize to equal s_ncg times sizeof(struct ufs_csum) rounded up to a
fragment ties the buffer to the real cylinder group count and makes it a
fragment multiple, so blks times s_fsize equals s_cssize and the copy stays
in bounds. struct ufs_csum is four 32 bit fields for both ufs1 and ufs2, and
this expected size matches the fragroundup(ncg times sizeof(csum)) sizing
that newfs writes.

s_ncg, s_fshift and s_fsize are all assigned earlier in ufs_fill_super(),
before this check runs, so the validation uses initialised values.

Reviewed-by: Ali Ahmet Memis <ali@iusegentoo.com>

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-08-01  2:44 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-07-01 21:57 [PATCH] ufs: reject malformed cylinder summary geometry 이상호
2026-08-01  2:44 ` Ali Ahmet Memis

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®