Forged btree leaf with data_offset==0: hammer_btree_extract returns cursor->data==NULL with error==0 (CRC skipped) β NULL-deref kernel panics in readdir/readlink/nresolve/dounlink/strategy_read sinks on crafted HAMMER images
| Field | Value |
|---|---|
| ID | DF-3002 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H |
| CWE | CWE-476 |
| File | sys/vfs/hammer/hammer_vnops.c |
| Lines | sinks :1734-1738, :1877-1880, :2866-2870, :1254-1255, :3343-3346 (root: hammer_btree.c:730) |
| Area | vfs/hammer |
| Confidence | certain |
| Discovered | 2026-09-02 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:vfs |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
hammer_btree_extract reports SUCCESS with a NULL data pointer for a crafted record leaf whose data_offset is 0 β the leaf/data CRC is never computed on this path, so a crafted image (attacker owns the media CRCs anyway) passes straight through. This file's sinks then dereference it: readdir vop_write_dirent(cursor.data->entry.obj_id/ name), readlink uiomove(cursor.data->symlink.name, data_len), strategy_read bcopy(cursor.data + roff), nresolve bcmp, dounlink bcmp/obj_id read. Deterministic NULL-page kernel panic, INVARIANTS or not (the KKASSERT passes for any data_len>7). Crafted-media local DoS: mount of a maliciously modified HAMMER image (vnconfig + mount -t hammer; requires privilege to mount, the DF-2902-2912 crafted-media threat model), then one getdents/readlink/unlink/strategy on the forged directory panics the kernel. Not run (Low, crafted-image class); sink reachability source-deterministic. Fix: return EINVAL for data_off==0 (one-liner in hammer_btree.c).
Timeline
- 2026-09-02 Discovered during pass-2 audit of hammer_vnops.c (GLM 5.3). DF-0769/DF-2698 re-verified, not re-reported.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3002 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 778 B | β raw | |
| VERDICT.md | β | 2.2 KB | β raw | |
| build.sh | β | 125 B | view raw | |
| df3002_run.log | β | 129 B | view raw | |
| env.txt | β | 401 B | view raw | |
| fix.diff | β | 387 B | view raw | |
| fix_run.log | β | 193 B | view raw | |
| h1patch2.py | β | 6.1 KB | view raw | |
| manifest.json | β | 759 B | view raw | |
| run.sh | β | 152 B | view raw | |
| verdict.json | β | 2.7 KB | view raw |
DF-3002 PoC evidence pack
What
Crafted leaf data_offset==0: hammer_btree_extract success + NULL data -> NULL-deref panics in readdir/nresolve.
Build
guest: newfs_hammer -f on 12G sparse vn image; host: python3 h1patch2.py IMG 3002 file_2902
Run
vnconfig -c vn0 IMG; mount_hammer /dev/vn0 /mnt/h1t; ls /mnt/h1t; ls -la /mnt/h1t/d -> panic
Expected
Fatal trap 12, fault VA 0x0, hammer_vop_readdir+0x1e8 on ls of the crafted dir
Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 All logs are full untrimmed captures unless noted. fix.diff is the git-apply-able verified fix (authored after verification; never applied to the audit tree).
DF-3002 - verification verdict
Status: reproduced | reproduced: 1 | impact: panic | confidence: certain
Crafted-image panic at the exact predicted sink. A HAMMER1 filesystem image was built with newfs_hammer, populated, then offline-patched (CRC32C-verified leaf matching; node CRCs recomputed via the v6-crc32 fallback the kernel accepts) so that the DIRENTRY leaf for d/file_2902 has data_offset == 0. hammer_btree_extract returns cursor->data = NULL with error == 0 (hammer_btree.c:705,:730 - the data CRC is never computed on this path). Mount succeeds clean; ls of the crafted directory dereferenced the NULL data pointer in kernel mode: Fatal trap 12, fault virtual address = 0x0, Stopped at hammer_vop_readdir+0x1e8 - the vop_write_dirent sink (:1734 area); nresolve/readlink/strategy_read sinks are the same NULL-data shape. Deterministic, INVARIANTS or not (KKASSERT at :1728 passes for data_len>7). Mount of crafted media requires privilege (DF-2902-2912 family threat model). Fix (EINVAL for data_off==0) A/B validated on the fix-all kernel.
Evidence pointers
df3002_panic.txt: Fatal trap 12 fault VA 0x0 Stopped at hammer_vop_readdir+0x1e8; df3002_run.log: mount clean, root ls ok, crafted-dir ls -> panic
Kernel references
- sys/vfs/hammer/hammer_btree.c:730
- sys/vfs/hammer/hammer_vnops.c:1734
- sys/vfs/hammer/hammer_vnops.c:1254
PoC changes vs the filing sketch
wrote a scan-based offline patcher: b-tree leaf located by CRC32C-matching the direntry data blob; node CRC rewritten with v6 crc32 (accepted fallback)
Fix
recommendation: return EINVAL for data_off == 0 in hammer_btree_extract
fix status: fixed
A/B on fix-all kernel: same crafted image, readdir of crafted dir returns clean error (no panic)
Guest: DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:04 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedA/B on fix kernel #2: same crafted image mounts; ls of the crafted dir completes with rc=0 and the diagnostic hammer_btree_extract: illegal zero data_offset logged twice - NO panic (stock: NULL-deref at hammer_vop_readdir+0x1e8).
Confirmed kernel references
Detail
Exploit chain
crafted HAMMER1 image with direntry leaf data_offset=0 -> mount (root) -> getdents on crafted dir -> hammer_btree_extract success+NULL -> vop_write_dirent deref NULL -> kernel page fault at 0x0
Evidence (decisive lines)
df3002_panic.txt: Fatal trap 12 fault VA 0x0 Stopped at hammer_vop_readdir+0x1e8; df3002_run.log: mount clean, root ls ok, crafted-dir ls -> panic
PoC changes
wrote a scan-based offline patcher: b-tree leaf located by CRC32C-matching the direntry data blob; node CRC rewritten with v6 crc32 (accepted fallback)
Verified recommended fix
return EINVAL for data_off == 0 in hammer_btree_extract
Verdict
Crafted-image panic at the exact predicted sink. A HAMMER1 filesystem image was built with newfs_hammer, populated, then offline-patched (CRC32C-verified leaf matching; node CRCs recomputed via the v6-crc32 fallback the kernel accepts) so that the DIRENTRY leaf for d/file_2902 has data_offset == 0. hammer_btree_extract returns cursor->data = NULL with error == 0 (hammer_btree.c:705,:730 - the data CRC is never computed on this path). Mount succeeds clean; ls of the crafted directory dereferenced the NULL data pointer in kernel mode: Fatal trap 12, fault virtual address = 0x0, Stopped at hammer_vop_readdir+0x1e8 - the vop_write_dirent sink (:1734 area); nresolve/readlink/strategy_read sinks are the same NULL-data shape. Deterministic, INVARIANTS or not (KKASSERT at :1728 passes for data_len>7). Mount of crafted media requires privilege (DF-2902-2912 family threat model). Fix (EINVAL for data_off==0) A/B validated on the fix-all kernel.
No comments yet.