{
 "finding_id": "DF-3102",
 "status": "reproduced",
 "reproduced": 1,
 "impact": "leak",
 "confidence": "certain",
 "verdict": "udf_readatoffset() clamps the read size by min(resid, max_size, MAXBSIZE) but not by the in-block remainder, while returning data = b_data + offset%bsize and bread()ing only roundup(size, bsize) bytes; uiomove() in udf_read() therefore copies up to bsize-1 bytes past the buffer-cache buffer to userspace. Reproduced on the stock INVARIANTS guest as uid 1001 with a valid minimal UDF image: pread(fd, 2048, 1000) returned 1000 bytes of non-disk kernel heap (expected 0xAA-filled file tail), identical across 3 runs. Fixed by a one-line clamp; the patched module returns correct file data.",
 "exploit_chain": "any mounted UDF volume -> unprivileged open() of one world-readable file -> pread() at an unaligned offset with count >= bsize - (offset % bsize) -> kernel heap bytes (stale buffer-cache content / allocator remnants) disclosed to userspace, repeatable without bound. No write primitive (read-only path).",
 "evidence": [
  "run.log: PREFIX-MATCH: first 1048 bytes correct + differs from expected disk data in 1000/1000 bytes + tail identical across 3 runs: YES + DF3102_LEAK_CONFIRMED",
  "fix_run.log: patched kernel -> differs ... in 0/1000 bytes (leak gone, correct 0xAA data returned)",
  "fix_build.log: udf.ko rebuilt cleanly with the fix",
  "craft_img.py: F1 extent {len=4096, pos=4}, sector5=0xAA",
  "poc.c leak1(): unaligned-first ordering, 3 runs, reference read last"
 ],
 "kernel_refs": [
  "sys/vfs/udf/udf_vnops.c:1046",
  "sys/vfs/udf/udf_vnops.c:1048",
  "sys/vfs/udf/udf_vnops.c:1057",
  "sys/vfs/udf/udf_vnops.c:403",
  "sys/vfs/udf/udf.h:96"
 ],
 "poc_changes": "Seed PoC authored fresh for pass 2. Two iterations were needed: (1) add <fcntl.h> for DragonFly; (2) reorder the unaligned preads BEFORE the block-aligned reference pread \u2014 the reference otherwise caches the same block in a 4096-byte buffer and getblk returns the larger buffer, masking the overread; also switched execution from root to su maxx (unprivileged).",
 "attempts": 3,
 "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
 "runtime_sec": 300,
 "guest_dirty": 0,
 "build_cmd": "cc -O2 -Wall -o /tmp/df3102_poc /tmp/df3102_poc.c (guest); python3 craft_img.py df3102.udf (host)",
 "run_cmd": "./run.sh (host driver: vnconfig + mount_udf as root, su maxx -c /tmp/df3102_poc leak1 /mnt/F1)",
 "code_hash": "b50456f09049a3a3f9cecccbf847b544c5408e238f3831a07660d0c0ed0b9d2a",
 "notes": "Same class reachable through udf_readdir()s ds->data walk. Upstream FreeBSD already carries the equivalent fix in its udf_readatoffset. Impact is disclosure only (no write); leak volume is attacker-chosen and repeatable, content includes stale cached data of other files.",
 "recommended_fix": "In udf_readatoffset(), after the MAXBSIZE clamp add: *size = min(*size, udfmp->bsize - (offset % udfmp->bsize));",
 "fix_status": "fixed",
 "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0 (same kernel; udf.ko module rebuilt from patched /usr/src/sys/vfs/udf and kldloaded)",
 "fix_baseline_reproduced": 1,
 "fix_patched_reproduced": 0,
 "fix_verdict": "fix.diff applied to guest source, udf.ko rebuilt (fix_build.log, clean -Werror build), module swapped in, same image remounted, exact PoC re-run: the 1000-byte overread is gone, the read returns the correct 0xAA file tail across two loop iterations, and normal file reads are unaffected (fix_run.log).",
 "fix_evidence": [
  "fix_run.log",
  "fix_build.log",
  "df31xx_combined_fix.diff"
 ]
}