fsck_ffs(8) crashes with SIGSEGV when processing a corrupted FFS
filesystem image.

The crash occurs in pass5() at pass5.c:336:

  if (memcmp(cg_inosused(newcg), cg_inosused(cg), mapsize) ...)

cg_inosused() accesses the cylinder group buffer at the offset
cg_iusedoff, which comes from the on-disk image.  A corrupted
offset causes memcmp to read past the buffer.

Confirmed on OpenBSD 7.8/arm64 with a 608KB mutated FFS image:

  #0 _libc_memcmp at memcmp.c:46
  #1 pass5 () at pass5.c:336

Since fsck_ffs processes untrusted filesystem images (e.g., USB
sticks), this is security-relevant.

Fix: validate cg_iusedoff and cg_freeoff against fs_bsize just
before the bitmap comparison that uses them.  The check is placed
after newcg has been fully rebuilt and cstotal accumulated, so
superblock totals remain accurate.  If offsets are out of bounds,
offer to rewrite the entire CG from newcg via dofix(), then skip
the unsafe bitmap comparison.

This follows the existing fsck_ffs conventions: dofix() respects
-n (no modify), -y (auto-fix), and interactive mode.  The
rewritten CG contains correct offsets and bitmaps rebuilt from
the inode and block allocation state already collected by earlier
phases.

Found by AFL++ fuzzing.

Index: sbin/fsck_ffs/pass5.c
===================================================================
RCS file: /cvs/src/sbin/fsck_ffs/pass5.c,v
retrieving revision 1.52
diff -u -p -r1.52 pass5.c
--- sbin/fsck_ffs/pass5.c       15 Sep 2024 07:14:58 -0000      1.52
+++ sbin/fsck_ffs/pass5.c
@@ -333,6 +333,15 @@ pass5(void)
                        memcpy(cg, newcg, (size_t)basesize);
                        dirty(cgbp);
                }
+               if (cg->cg_iusedoff >= fs->fs_bsize ||
+                   cg->cg_freeoff >= fs->fs_bsize ||
+                   cg->cg_iusedoff + mapsize > fs->fs_bsize) {
+                       if (dofix(&idesc[1], "CG OFFSETS WRONG")) {
+                               memcpy(cg, newcg, (size_t)fs->fs_cgsize);
+                               dirty(cgbp);
+                       }
+                       continue;
+               }
                if (memcmp(cg_inosused(newcg), cg_inosused(cg),
                           mapsize) != 0 &&
                    dofix(&idesc[1], "BLK(S) MISSING IN BIT MAPS")) {

Reply via email to