β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3049

s_desc_size unvalidated without the 64BIT feature β€” ext2_gd_csum() over-reads the group-descriptor heap allocation; console-printed crc is a kernel-heap disclosure oracle

Field Value
ID DF-3049
Status new
Severity Medium
CVSS 3.1 CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N
CWE CWE-125 / CWE-909
File sys/vfs/ext2fs/ext2_vfsops.c
Lines 549-554 (sink: ext2_csum.c:684-686)
Area vfs/ext2fs
Confidence certain
Discovered 2026-09-02
Pass 2 (GLM 5.3 second pass)
Bucket kernleak
Reported pending
Known CVE none
CVE match novel

Summary

The desc_size check applies only inside the 64BIT branch. Without 64BIT, ext2_gd_csum() uses s_desc_size as a crc32c read length past &fs->e2fs_gd[i]+32 for every group (verify at mount, recompute on every cgupdate) β€” up to ~64KB past the allocation for the last groups. The truncated 16-bit crc is printed to the console on verify failure and written back into the group descriptors on rw sync/unmount. Crafted metadata_csum image (no 64BIT) with desc_size>64: VERIFIED an OOB-read oracle β€” kernel 'expected' crc values 0x7c2e/0x3c11 varied with heap churn and never matched the exact in-bounds computation (0x04e5), proving live kernel heap bytes enter the crc. Varying desc_size across mounts shifts the (GF(2)-linear, invertible) crc window so adjacent-heap bytes can be solved at ~2 observations per byte; rw unmount exposes the full 32-bit value on the attacker's own device. Fix validated (reject desc_size>64 at mount): EINVAL, oracle gone.

Timeline

  • 2026-09-02 Discovered during pass-2 audit of ext2_vfsops.c (GLM 5.3); heap-byte oracle demonstrated + fix validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3049 Β· 9 files
FileTypeDescriptionSize
craft.py β€” 4.1 KB view raw
vntool.c β€” 1.1 KB view raw
run.sh β€” 601 B view raw
build.sh β€” 156 B view raw
run.log β€” 969 B view raw
console_lines.txt β€” 780 B view raw
fix_run.txt β€” 386 B view raw
env.txt β€” 461 B view raw
verdict.json β€” 3.0 KB view raw

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

fixed module rejects the image with EINVAL at the desc_size bound before any gd checksum work; console shows no 'bad gd' line (no OOB read, no oracle).

findings/poc/DF-3049/fix_run.txt; findings/poc/DF-3047/fix.diff (hunk 1)
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #0 (same kernel; ext2fs.ko rebuilt with fix.diff hunk 1, 2026-09-05)

Confirmed kernel references

Detail

Exploit chain

crafted metadata_csum (no 64BIT) image, desc_size=512, gds 0..62 re-checksummed offline, gd63 wrong -> mount -> ext2_gd_csum_verify prints expected=0xNNNN computed over 448 OOB heap bytes -> repeat with varying desc_size / heap churn -> solve crc32c linear system for adjacent-heap bytes (KASLR-defeat / heap disclosure, ~N/2 mounts for N bytes)

Evidence (decisive lines)

console_lines.txt (3 mounts, varying expected), run.log, craft.py output (offline in-bounds csum 0x04e5 == stored), fix_run.txt (fixed: EINVAL, no bad-gd print)

PoC changes

crafted from scratch: 64-group 1K-block metadata_csum fs (512MB sparse), desc_size=512, sb crc32c recomputed, gd csums backward-solved for desc_size=512 semantics so the kernel reaches group 57/63 before failing; crc32c implemented to match calculate_crc32c (no inversions).

Verified recommended fix

validate s_desc_size <= sizeof(struct ext2_gd) (E2FS_64BIT_GD_SIZE) in ext2_compute_sb_data() regardless of feature bits

Verdict

s_desc_size is only validated when the 64BIT feature is set; ext2_gd_csum() otherwise reads s_desc_size-32 bytes from each fs->e2fs_gd[i], past the allocation. Demonstrated via the console-printed crc: kernel 'expected' values (0x7c2e, 0x3c11) vary with heap state and differ from the exact in-bounds computation (0x04e5) -> live kernel heap bytes entered the crc. 16-bit-per-mount oracle; rw unmount writes the crc to the attacker device.