hammer2_igetv panics on unhandled on-media objtype (0,3,8,10,11..255) β crafted-image kernel DoS at mount/first access
| Field | Value |
|---|---|
| ID | DF-2636 |
| Status | new |
| Severity | Medium |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-754 Improper Check for Unusual or Exceptional Conditions |
| File | sys/vfs/hammer2/hammer2_inode.c |
| Lines | 791-793 |
| Area | vfs |
| Confidence | certain |
| Discovered | 2026-08-29 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | hammer2 |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
hammer2_igetv() switches on ip->meta.type, copied verbatim from the
on-media inode by hammer2_pfsalloc (vfsops.c:456) with no validation on
the mount path. The switch handles only
DIRECTORY/REGFILE/SOFTLINK/CDEV/BDEV/FIFO/SOCKET; every other byte value
(0 UNKNOWN, 3, 8, 10 WHITEOUT, 11..255) reaches
default: panic("hammer2: unhandled objtype %d") at inode.c:791-793.
Threat model & preconditions
Crafted or bit-flipped (checksum-forged) hammer2 image: unconditional kernel panic at mount or first access to any inode with an unhandled objtype. Mount privilege required β same trust boundary as DF-0763, DF-0804, DF-0875, DF-2616..2632.
Proof of concept
VERIFIED on the stock INVARIANTS guest: forge_2636.py flips meta.type at
inode-block +0x50 of the PFS root to 0x42; mount -o ro succeeds; the
first VFS_ROOT (lstat inside ls -la) panics:
panic: hammer2: unhandled objtype 66 with backtrace
hammer2_igetv β hammer2_vfs_root β nlookup β kern_stat β sys_lstat.
Fix (kprintf + vp->v_type = VBAD + *errorp = EINVAL instead of panic)
validated on a nativekernel rebuild: ls returns Invalid argument,
guest stays up. Evidence: findings/poc/DF-2636/.
Impact
Unconditional kernel panic (local DoS) from a crafted image. No memory-corruption primitive; controlled panic.
Recommended fix
--- a/sys/vfs/hammer2/hammer2_inode.c
+++ b/sys/vfs/hammer2/hammer2_inode.c
@@
default:
- panic("hammer2: unhandled objtype %d",
- ip->meta.type);
- break;
+ kprintf("hammer2: unhandled objtype %d inum %ld\n",
+ ip->meta.type, (long)ip->meta.inum);
+ vp->v_type = VBAD;
+ vx_put(vp);
+ vp = NULL;
+ *errorp = EINVAL;
+ break;
}
+ if (vp == NULL)
+ break;
Full diff: findings/poc/DF-2636/fix.diff (validated).
Timeline
- 2026-08-29 Discovered during pass-2 audit of hammer2_inode.c (GLM 5.3); verified reproduced + fix validated same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2636 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| forge_2636.py | β | 2.0 KB | view raw | |
| run.sh | β | 536 B | view raw | |
| panic.txt | β | 2.9 KB | view raw | |
| run.log | β | 2.2 KB | view raw | |
| build.log | β | 146 B | view raw | |
| env.txt | β | 319 B | view raw | |
| fix.diff | β | 662 B | view raw | |
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 2.3 KB | β raw | |
| verdict.json | β | 3.3 KB | view raw | |
| fix_build.log | β | 1.4 KB | view raw | |
| fix_run.log | β | 258 B | view raw | |
| manifest.json | β | 1.0 KB | view raw |
DF-2636 β panic on unhandled on-media objtype in hammer2_igetv
What
hammer2_igetv() (sys/vfs/hammer2/hammer2_inode.c:744-795) switches on
ip->meta.type to initialize the vnode. meta.type is copied verbatim from
the on-media inode (hammer2_pfsalloc: iroot->meta = ripdata->meta,
hammer2_vfsops.c:456). Any on-media value outside
{DIRECTORY,REGFILE,SOFTLINK,CDEV,BDEV,FIFO,SOCKET} β e.g. 0 (UNKNOWN), 3,
8, 10 (WHITEOUT), 11..255 β hits:
default:
panic("hammer2: unhandled objtype %d", ip->meta.type);
An attacker who can craft (or bit-flip, given a checksum-forged image) a hammer2 filesystem image gets an unconditional kernel panic at mount or at the first VFS_ROOT (ls/stat) on the mount. Mounting requires root (or vfs.usermount policies), same trust boundary as the rest of the crafted-image family (DF-0763/DF-0804/DF-0875/DF-2616...).
Reproduce (on the DragonFly QEMU guest, as root)
# forge: flip meta.type (inode block +0x50) of the 'testvol' PFS root # inode to 0x42, set CHECK_NONE on ancestors, recompute volhdr CRC32Cs python3 forge_2636.py h2base.img craft2636.img # host side scp craft2636.img dfbsd:/root/poc/df2636/ # guest side sh run.sh
run.sh does: vnconfig + mount_hammer2 -o ro /dev/vn1@testvol /mnt/h2636
+ ls -la /mnt/h2636.
Expected
Mount succeeds; the first VFS_ROOT (the lstat inside ls) panics:
panic: hammer2: unhandled objtype 66 hammer2_igetv() at hammer2_igetv+0x42a hammer2_vfs_root() at hammer2_vfs_root+0xdc nlookup() at nlookup+0xb9c kern_stat() ... sys_lstat()
Observed verbatim on the stock INVARIANTS kernel β see panic.txt / run.log.
Fix
Return an error (and mark the vnode VBAD) instead of panicking; see fix.diff.
DF-2636 β VERDICT
Status: reproduced (impact: panic, confidence: certain).
Narrative
The PoC forger (cribbing the proven DF-2616/DF-2624 technique) walks
volhdr -> sroot blockset -> PFS inodes, finds the inode named testvol
(inode block at image offset 0x1400800 on our base image), flips
meta.type (inode block offset +0x50 per hammer2_disk.h
hammer2_inode_meta_t) from 1 (DIRECTORY) to 0x42, sets CHECK_NONE on the
poison bref and the sroot bref, zeroes mirror_tid on the poison bref so
recovery does not recurse, and recomputes the volume-header CRC32Cs.
On the guest (stock INVARIANTS kernel, DragonFly 6.5-DEVELOPMENT #0):
mount_hammer2 -o ro /dev/vn1@testvol /mnt/h2636succeeded (console:hammer2_mount: devstr="/dev/vn1@testvol" ... rdonly=1,HAMMER2: INITIATE SPANs). No type validation exists on the mount path.- The first VFS_ROOT β triggered by the
lstatinsidels -laβ panics exactly at the cited site:
panic: hammer2: unhandled objtype 66 hammer2_igetv() at hammer2_igetv+0x42a hammer2_vfs_root() at hammer2_vfs_root+0xdc nlookup() at nlookup+0xb9c kern_stat() at kern_stat+0x1d sys_lstat() at sys_lstat+0x57
The ssh session died at the panic (guest froze in ddb) β panic.txt and run.log contain the serial-console capture.
Why this is the cited bug
hammer2_igetv() at sys/vfs/hammer2/hammer2_inode.c:791-793:
default: panic("hammer2: unhandled objtype %d", ip->meta.type);
ip->meta was populated from media in hammer2_pfsalloc
(hammer2_vfsops.c:456, iroot->meta = ripdata->meta) with no validation
of type. Valid-but-unhandled on-media values include 0, 3, 8, 10
(WHITEOUT), and 11..255.
Exploit ceiling
Unauthenticated kernel panic (DoS) from a crafted filesystem image at mount/first-access; requires the ability to mount a hammer2 image (root). No memory-corruption primitive β the switch is a straight dispatch on an unvalidated byte, the default branch is a controlled panic.
Fix validation
fix.diff changes the default branch to log, mark the vp VBAD, vx_put it,
and return EINVAL; if (vp == NULL) break; guards the tail of the loop.
Built with make nativekernel in the guest, installed, rebooted: the same
forged image then mounts and ls returns an error instead of panicking
(see fix_run.log, fix_build.log). Baseline panic confirmed on the stock
kernel in the same session.
Fix verification
fixedfix.diff (default case -> kprintf + VBAD + vx_put + *errorp=EINVAL, with 'if (vp == NULL) break;' guard) applied to the guest /usr/src, kernel rebuilt with 'make nativekernel' + installkernel (fix_build.log), rebooted into kernel #1. Re-running the exact PoC: mount succeeds, 'ls -la /mnt/h2636' returns 'Invalid argument' instead of panicking, guest stays up (fix_run.log). Baseline panic confirmed on stock kernel #0 in the same session.
fix_build.log (install completed), fix_run.log (MOUNT_RC=0, ls: Invalid argument, no panic, vm up)
Confirmed kernel references
Detail
Evidence (decisive lines)
panic.txt lines 46-53 (panic + backtrace), run.log (mount lines then trap), forge_2636.py (byte flip at inode block +0x50 per hammer2_disk.h), fix_run.log (fixed kernel: error return, no panic)
PoC changes
Cribbed the DF-2616/DF-2624 image-forger harness (h2common.py volhdr->sroot->PFS walk, CHECK_NONE ancestors, volhdr CRC32C recompute) instead of writing a forger from scratch; targeted the mounted 'testvol' PFS root inode at image offset 0x1400800, field offset +0x50 (meta.type).
Verified recommended fix
Replace the panic with a VBAD vnode + EINVAL error return in hammer2_igetv's default case (see fix.diff).
Verdict
Reproduced on the stock INVARIANTS guest kernel (DragonFly 6.5-DEVELOPMENT #0). A forged hammer2 image with the PFS-root inode's meta.type set to 0x42 (valid values 0,3,8,10,11..255 also work; only 1,2,4,5,6,7,9 are handled) mounts cleanly; the first VFS_ROOT -- the lstat inside ls -la -- panics exactly at hammer2_inode.c:791-793: 'panic: hammer2: unhandled objtype 66', backtrace hammer2_igetv <- hammer2_vfs_root <- nlookup <- kern_stat <- sys_lstat. ip->meta is copied verbatim from media by hammer2_pfsalloc (vfsops.c:456) with no type validation anywhere on the mount path. Controlled panic (DoS) from a crafted image; requires mount privilege. Fixed kernel returns EINVAL instead of panicking (fix validated).
No comments yet.