[PATCH] freevxfs: fix divide-by-zero in vxfs_bmap_ext4() causing oops on mount

Đức Cảnh Nguyễn <[email protected]> Sat, 1 Aug 2026 17:45:37 +0700
Newsgroups org.kernel.vger.linux-fsdevel,org.kernel.vger.linux-kernel
Message-ID <CALFmVm4k=ZKZ_f02L9fB4-kWyPVWrF2iDx0yFr2wsCcL8pizwQ@mail.gmail.com>
vxfs_bmap_ext4() divides by attacker-controlled values from the on-disk
inode without fully validating them.  A crafted VxFS image triggers a
kernel "divide error" (oops) as soon as it is mounted -- no read or
ioctl is required.  The crash is reached from vxfs_fill_super() ->
vxfs_iget() -> vxfs_get_page() -> vxfs_read_folio() -> vxfs_bmap1() ->
vxfs_bmap_ext4().

Two independent paths hit the division by zero:

1. ve4_indsize == 0: line 62 computes the denominator
   (indsize * indsize * bsize / 4) == 0.  The existing guard
   (line 52) only rejects indsize > s_blocksize, so 0 gets through.

2. bn == 0: if block 0 is not covered by the first direct extent
   (size == 0), bn stays 0 after the direct-extent loop.  With
   indsize > 0 the expression at line 73,
   indir[(bn / indsize) % (indsize * bn)], becomes 0 % 0.

Reproduced in QEMU on a 7.0.0-28-generic kernel:

  Oops: divide error: 0000 [#1] SMP NOPTI
  RIP: 0010:vxfs_bmap_ext4+0xfc/0x1c0 [freevxfs]
  RAX: 0000000000000000 RBX: 0000000000000000 RDX: 0000000000000000
  Call Trace:
    vxfs_bmap_ext4+0xfc/0x1c0 [freevxfs]
    vxfs_bmap1+0x42/0x70 [freevxfs]
    vxfs_getblk+0x17/0x70 [freevxfs]
    block_read_full_folio+0x109/0x270
    vxfs_read_folio+0x18/0x30 [freevxfs]
    vxfs_get_page+0x13/0x40 [freevxfs]
    __vxfs_iget+0x3a/0xd0 [freevxfs]
    vxfs_iget+0x5b/0x1a0 [freevxfs]
    vxfs_fill_super+0x159/0x340 [freevxfs]
    get_tree_bdev_flags+0x141/0x1e0
    ...
    __x64_sys_mount+0x12b/0x160

Reject indsize == 0 up front and bail out when the logical block is 0.

Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
Signed-off-by: canhnguyen26 (Nguyen Duc Canh) <[email protected]>
---
 fs/freevxfs/vxfs_bmap.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/fs/freevxfs/vxfs_bmap.c b/fs/freevxfs/vxfs_bmap.c
index e85222892038..4c2a5b42df3c 100644
--- a/fs/freevxfs/vxfs_bmap.c
+++ b/fs/freevxfs/vxfs_bmap.c
@@ -49,7 +49,7 @@ vxfs_bmap_ext4(struct inode *ip, long bn)
  unsigned long bsize = sb->s_blocksize;
  u32 indsize = fs32_to_cpu(sbi, vip->vii_ext4.ve4_indsize);
  int i;

- if (indsize > sb->s_blocksize)
+ if (indsize == 0 || indsize > sb->s_blocksize)
  goto fail_size;

  for (i = 0; i < VXFS_NDADDR; i++) {
@@ -62,7 +62,8 @@ vxfs_bmap_ext4(struct inode *ip, long bn)
  bn -= fs32_to_cpu(sbi, d->size);
  }

- if ((bn / (indsize * indsize * bsize / 4)) == 0) {
+ if (bn == 0)
+ goto fail_buf;
+ if ((bn / (indsize * indsize * bsize / 4)) == 0) {
  struct buffer_head *buf;
  daddr_t bno;
  __fs32 *indir;
-- 
2.39.5
freevxfs-fix-divide-by-zero.patch (text/x-patch, 740 B)
diff --git a/fs/freevxfs/vxfs_bmap.c b/fs/freevxfs/vxfs_bmap.c
--- a/fs/freevxfs/vxfs_bmap.c
+++ b/fs/freevxfs/vxfs_bmap.c
@@ -49,7 +49,7 @@ vxfs_bmap_ext4(struct inode *ip, long bn)
 	unsigned long bsize = sb->s_blocksize;
 	u32 indsize = fs32_to_cpu(sbi, vip->vii_ext4.ve4_indsize);
 	int i;
 
-	if (indsize > sb->s_blocksize)
+	if (indsize == 0 || indsize > sb->s_blocksize)
 		goto fail_size;
 
 	for (i = 0; i < VXFS_NDADDR; i++) {
@@ -62,7 +62,8 @@ vxfs_bmap_ext4(struct inode *ip, long bn)
 		bn -= fs32_to_cpu(sbi, d->size);
 	}
 
-	if ((bn / (indsize * indsize * bsize / 4)) == 0) {
+	if (bn == 0)
+		goto fail_buf;
+	if ((bn / (indsize * indsize * bsize / 4)) == 0) {
 		struct buffer_head *buf;
 		daddr_t	bno;
 		__fs32 *indir;