{
  "finding_id": "DF-3035",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Attacker-offset out-of-bounds kernel READ and WRITE reproduced deterministically on the stock INVARIANTS guest kernel from a crafted UFS1 image whose cylinder-group header carries a magic value plus one poisoned field. ffs_mountfs validates only fs_magic/fs_bsize (ffs_vfsops.c:642-646) and the allocator applies only cg_chkmagic() (fs.h:398, a magic compare) before using on-disk cg fields as kernel-memory offsets and indexes: cg_cgx -> fs_csp[16*cg_cgx].cs_nbfree-- and fs_maxcluster[4*cg_cgx] stores (ffs_alloc.c:1213,1963); cg_freeoff/cg_iusedoff/cg_boff/cg_btotoff/cg_clustersumoff/cg_clusteroff -> bit/int16/int32 read-modify-write through cgbuf+offset (ffs_alloc.c:1208-1216,1383-1463,1532-1609,1893-1955); cg_rotor/cg_frotor/cg_irotor/cg_nclusterblks -> OOB scan bounds (1129,1294,1410,1836); plus inode-derived bpref -> dtog() -> fs_csp[] OOB read before bread (1010,1370). Proven by: cg_cgx=0x40000000 -> Fatal trap 12 supervisor WRITE page-not-present at ffs_clusteracct.isra.0+0x1eb movl %eax,(%rdx,%rcx,4) with fault address = fs_maxcluster + 4*0x40000000 in three independent boots (panic1/2/3.txt); cg_iusedoff=0x20000000 -> Fatal trap 12 supervisor READ at ffs_nodealloccg+0x13f movzbl (%rdi,%rax,1), the inosused[] bitmap read at cgbuf+512MiB (panic4.txt); identical unpatched image allocates cleanly (control run). Panic is only the observable: any offset landing on mapped memory yields a silent attacker-relative int32 inc/dec/store (csum counters) or bit write (block/inode bitmaps), reach +/-8..32GiB around kernel heap objects via the signed 32-bit fields. Fail-stop panics from merely inconsistent in-range cg metadata (dup alloc 630, block-not-in-map 1451, freeing-free 1542/1581, map-mismatch 1317) make the same image a reliable DoS. Trigger model = crafted media mounted by root (DF-0820/DF-3015 class) with a single creat/write as the poisoned allocation; with vfs.usermount=1 or auto-mounted removable media the corruption itself is unprivileged. Root cause is distinct from DF-0820 (superblock geometry at mount) and DF-0794 (fs_ncg==0): this is the per-cylinder-group header consumed lazily at alloc/free time with no validation anywhere.",
  "exploit_chain": "crafted UFS1 image (cg_chkmagic-valid cg0, one poisoned field) mounted by root -> first creat/write on the mount -> ffs_balloc -> ffs_blkpref -> ffs_alloc -> ffs_hashalloc -> ffs_alloccg -> ffs_alloccgblk/ffs_nodealloccg -> wild write fs_maxcluster[cg_cgx]/fs_csp[cg_cgx] (attacker 32-bit index, store or inc/dec) or wild bitmap RMW at cgbuf+cg_freeoff/cg_iusedoff (attacker bit offset) -> with a heap-layout-stable target (no KASLR on this guest) point cg_cgx at a neighbouring kernel object: single-int32 decrement/Bit-flip corruption at an attacker-chosen relative address; panic when the offset is unmapped (the demonstrated observable). Full uid0 chain not developed: the mount precondition already requires root on the default configuration, so escalation adds nothing; the ceiling is arbitrary-offset kernel memory corruption (and unprivileged corruption wherever users can get images mounted).",
  "evidence": [
    "findings/poc/DF-3035/panic1.txt — Fatal trap 12, supervisor write, Stopped at ffs_clusteracct.isra.0+0x1eb: movl %eax,(%rdx,%rcx,4), fault 0xfffff8021776a200 (cg_cgx=0x40000000)",
    "findings/poc/DF-3035/panic2.txt — same sink, cg_cgx=0x40040000, fault 0xfffff8018dce3600 (linear cg_cgx addressing)",
    "findings/poc/DF-3035/panic3.txt — same sink, cg_cgx=0x40000000, fault 0xfffff80218519500 (boot-varying heap base + fixed 4*index displacement)",
    "findings/poc/DF-3035/panic4.txt — Fatal trap 12, supervisor READ, Stopped at ffs_nodealloccg+0x13f: movzbl (%rdi,%rax,1), fault 0xfffff8007c136000 (cg_iusedoff=0x20000000)",
    "findings/poc/DF-3035/run.setup.log — image build, cgxtool superblock/cg dump, patch confirmation, mount succeeds (mount never validates cg headers)",
    "findings/poc/DF-3035/run.patched.log — fix.diff kernel: both poisoned images fail gracefully with ENOSPC, no panic; unpatched control image still allocates (16384-byte file), clean unmount",
    "findings/poc/DF-3035/cgxtool.c + setup3035.sh — minimal crafted-image generator/patcher + trigger",
    "findings/poc/DF-3035/fix.diff — ffs_chkcg() validator called from all six cg consumers + ffs_hashalloc cg clamp; applied to guest /usr/src, nativekernel -Werror-clean, installed, rebooted"
  ],
  "kernel_refs": [
    "sys/vfs/ufs/ffs_alloc.c:1010",
    "sys/vfs/ufs/ffs_alloc.c:1127",
    "sys/vfs/ufs/ffs_alloc.c:1213",
    "sys/vfs/ufs/ffs_alloc.c:1215",
    "sys/vfs/ufs/ffs_alloc.c:1218",
    "sys/vfs/ufs/ffs_alloc.c:1257",
    "sys/vfs/ufs/ffs_alloc.c:1294",
    "sys/vfs/ufs/ffs_alloc.c:1370",
    "sys/vfs/ufs/ffs_alloc.c:1383",
    "sys/vfs/ufs/ffs_alloc.c:1415",
    "sys/vfs/ufs/ffs_alloc.c:1463",
    "sys/vfs/ufs/ffs_alloc.c:1532",
    "sys/vfs/ufs/ffs_alloc.c:1836",
    "sys/vfs/ufs/ffs_alloc.c:1893",
    "sys/vfs/ufs/ffs_alloc.c:1963",
    "sys/vfs/ufs/fs.h:394",
    "sys/vfs/ufs/fs.h:398",
    "sys/vfs/ufs/ffs_vfsops.c:642"
  ],
  "poc_changes": "No prior PoC seed — authored fresh. cgxtool.c computes all offsets from the image's own superblock via the tree's fs.h (needed the ufs_types.h 32-bit ufs_daddr_t/ufs_time_t typedefs; an int64 first attempt misaligned the superblock). setup3035.sh gained a notrigger mode so setup output could be captured before the panic kills sshd. Two early validator bugs in fix.diff were caught against the real newfs layout before building (btotoff=168 == offsetof(struct cg,cg_space), and the fpg-sized bitmap scan region legitimately overlapping clustersumoff by 4 bytes) — the fix must reject poison without rejecting legitimate filesystems, and the control run proves it does not.",
  "attempts": 7,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly v6.5.0.1712.g89e6a-DEVELOPMENT #1: Mon Jun 29 14:18:01 UTC 2026  root@ephemeral-5c2002c44b6c:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 5700,
  "guest_dirty": 1,
  "build_cmd": "guest# cc -O -o /tmp/cgxtool /root/poc/cgxtool.c   (fix kernel: cd /usr/src && patch -p1 < fix3035.diff && make -j4 nativekernel KERNCONF=X86_64_GENERIC && make installkernel)",
  "run_cmd": "guest# sh /root/poc/setup3035.sh cgx 0x40000000 ; sh /root/poc/setup3035.sh iusedoff 0x20000000 ; sh /root/poc/setup3035.sh cgx 0 (control)",
  "code_hash": "cgxtool.c 4e02787b90126976d3cafc4cbf99f231819e0de8d500593218af94d1a02c1484; setup3035.sh fd11fbec44524bc1c87aae00e399dc0ed9ba52c1748852cd4b7a56093c88d29f; fix.diff 03a6693afb9fb938a047b1519bb5dba268c7bbb24b94d9afaff2cc17e1348dde",
  "notes": "impact field: this is kernel memory corruption (arbitrary-offset relative write + read) with a root-mount precondition on default configs — filed severity High, bucket memcorrupt, same model as DF-0820/DF-3015. Guest left in panicked/reset cycles is NOT the case — final state reset with-src (guest_dirty reflects the fix-build during the run). Fail-stop DoS variants via in-range inconsistent csum/bitmap metadata are included in the finding (same fix covers them). The DF-0793 async-TRIM deferred-pointer pattern was swept: the only taskqueue use in this file is the known trim task; ffs_blkfree_trim_completed's double tp read is the same variable. Superblock-field div-by-zero/OOB variants (fs_maxbpg=0 at ffs_blkpref:805, fs_spc/fs_nsect/fs_npsect in cbtocylno/cbtorpos, fs_frag>8 in fragtbl/around/inside) belong to the DF-0820 mount-validation family and were not re-filed.",
  "recommended_fix": "Add ffs_chkcg() validating every cg-header field (cg_cgx, rotors, and all map offsets/sizes against fs_cgsize) after each cg bread() in ffs_alloc.c, and clamp the bpref-derived cg in ffs_hashalloc() before it indexes fs_csp[] (fix.diff, fix-validated in-guest).",
  "fix_status": "fixed",
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly v6.5.0.1712.g89e6a-DEVELOPMENT #1: Sat Sep  5 14:02:46 UTC 2026  root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff applied to guest /usr/src (9 hunks clean), make nativekernel KERNCONF=X86_64_GENERIC -Werror-clean (ffs_alloc.o), installkernel, rebooted into #1 Sat Sep 5 14:02:46. Identical crafted images: cgx=0x40000000 -> NO panic, dd fails 'No space left on device' (poisoned cg rejected by ffs_chkcg -> ENOSPC); iusedoff=0x20000000 -> same graceful ENOSPC. Control image (cgx rewritten to its legitimate value 0): first allocation still succeeds (16384-byte file created) and unmounts cleanly, proving the validator does not reject legitimate newfs geometry. Baseline bad behaviour GONE.",
  "fix_evidence": "findings/poc/DF-3035/run.patched.log"
}
