{
  "finding_id": "DF-3415",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "in6_pcbconnect() (sys/netinet6/in6_pcb.c:344) evaluates IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr) before any sa_len validation (the check only runs later in in6_pcbladdr(), in6_pcb.c:282, called at :351). getsockaddr() kmallocs exactly the user-supplied sockaddr length, so for len<28 this reads past the allocation (up to 16 bytes for len=2). Verified on the guest as an unprivileged user (uid 1001): grooming the shared 24-byte M_SONAME slab chunk via a rejected bind(24) and then connect(17) makes stale heap bytes 17..19 (outside the 17-byte sockaddr) complete the v4-mapped pattern and deterministically flip connect() from EINVAL to EADDRNOTAVAIL \u2014 20/20 vs 0/20 differential across 3 runs. Impact ceiling is a comparison oracle on stale heap bytes (they only select an errno; in6_pcbladdr still rejects short sockaddrs, so no connection state is established from OOB data). Severity Low, kernleak bucket, consistent with filed family DF-3357 (Medium: garbage addr used as packet dst) and DF-3372 (Low).",
  "exploit_chain": "unprivileged connect(2) on AF_INET6 SOCK_DGRAM with a 17-byte sockaddr -> getsockaddr kmalloc(17) [24-byte M_SONAME chunk reusing a groomed free chunk] -> udp6_connect (udp6_usrreq.c:596) then in6_pcbconnect (in6_pcb.c:344) read sin6_addr bytes 8..19 incl. 3 bytes past the allocation -> IN6_IS_ADDR_V4MAPPED true/false selected by attacker-groomed stale heap bytes -> observable errno oracle (EADDRNOTAVAIL vs EINVAL). No path to state establishment or privilege: validation still rejects; impact is leak-class only.",
  "evidence": "run.log / run.2.log: A groom 00 ff ff : EADDRNOTAVAIL=20 EINVAL=0 vs B groom 00 00 00 : EADDRNOTAVAIL=0 EINVAL=20 \u2014 variants differ only in heap bytes outside the connect() sockaddr. VERDICT.md: full trace + slab mechanics. in6_v4mapped_oob.c: PoC source.",
  "kernel_refs": [
    "sys/netinet6/in6_pcb.c:344",
    "sys/netinet6/in6_pcb.c:282",
    "sys/netinet6/in6_pcb.c:351",
    "sys/netinet6/in6_pcb.c:129",
    "sys/kern/uipc_syscalls.c:1513",
    "sys/netinet6/udp6_usrreq.c:596",
    "sys/netinet6/in6.h:316"
  ],
  "poc_changes": "No seed PoC existed; authored from scratch. First design (len=2 trigger + len=16 groom) was unworkable: zoneindex() maps <=16 to the 16-byte zone where the v4-mapped word2 (bytes 16..19) falls into the adjacent chunk whose byte 0 is always a stale sa_len != 0; switched to the 17-byte trigger / 24-byte groom pair which share the 24-byte zone (sizes 17..24 round to 24) so bytes 17..19 are stale-but-groomable. Verified kfree() preserves bytes 8..23 (only c_Next at offset 0 written; use_weird_array defaults 0) and that UDP6 has no pru_preconnect (only v4 udp_preconnect), so the netisr udp6_connect path runs unvalidated.",
  "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": 6,
  "guest_dirty": 0,
  "build_cmd": "cd /tmp/df3415 && cc -O2 -Wall -o in6_v4mapped_oob in6_v4mapped_oob.c",
  "run_cmd": "su -m maxx -c /tmp/df3415/in6_v4mapped_oob",
  "code_hash": "7ae0509ece85a0b5580f5560adc8edc921536179076515bfa2350ac75567cb8b",
  "notes": "attempts=3: initial design rejected after zone-class analysis; second build/run reproduced; third stability run reproduced (3x20 rounds, 100% stable differential). Guest left clean (PoC only touches /tmp; no kernel state change; guest stays up). Attribution: userspace errno cannot distinguish udp6_usrreq.c:596 (first reader, same family, other file) from in6_pcb.c:344; both read identical bytes; the finding is the in6_pcb.c instance, certain by trace. in6_pcbbind is NOT affected (sa_len checked at :129 before first field read :138). fix.diff verified with git apply --check against the read-only tree.",
  "recommended_fix": "Move the sa_len/family validation ahead of the IN6_IS_ADDR_V4MAPPED read in in6_pcbconnect() (mirror in6_pcbbinds checks at in6_pcb.c:129-135); see fix.diff.",
  "fix_status": "not_testable",
  "fix_kernel_uname": "",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "fix.diff authored (validation reorder, identical in shape to the family fix) and verified to apply cleanly with git apply --check; not rebuilt in-guest \u2014 Low-severity OOB read (errno oracle only) and this batchs conditional-verify policy reserves the kernel rebuild/reboot cycle for Critical/High or memcorrupt/privesc findings. The patch is a pure precondition reorder: with it, every sa_len!=28 connect() returns EINVAL before any address byte is read, eliminating the oracle by construction.",
  "fix_evidence": "findings/poc/DF-3415/fix.diff (git apply --check passes against sys/)"
}