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

From: Đức Cảnh Nguyễn

Date: Sat Aug 01 2026 - 06:46:03 EST


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) <0206canh@xxxxxxxxx>
---
 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
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;