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

div_bind() writes sin_addr = INADDR_ANY at offset 4..8 of the user bind sockaddr before any length validation (OOB write lands in own-chunk slab slack)

Summary

div_bind() zeroes the caller-supplied sockaddr's sin_addr (4 bytes at offset 4..8) as soon as sa_family==AF_INET, before in_pcbbind() validates sa_len. With the 4-byte minimum bind sockaddr the write is 4 bytes past the requested size; the slab allocator rounds sub-128-byte requests to 8-byte multiples so it lands inside the chunk's own slack - no adjacent-object corruption, no security impact, but a language-level OOB write on a validated-less path (same family as DF-3440). Demonstrated: bind(4B AF_INET sockaddr) returns EINVAL from the later check (not EAFNOSUPPORT from the earlier family gate), proving the write branch executed. Root-only. Fix: check sa_len first.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3441 Β· 4 files
FileTypeDescriptionSize
div3441.c β€” 1.7 KB view raw
README.md β€” 1.1 KB ↓ raw
verdict.json β€” 1.7 KB view raw
manifest.json β€” 531 B view raw

DF-3441 β€” div_bind() writes into the user bind sockaddr before validation

div_bind() (sys/netinet/ip_divert.c:456-479) executes ((struct sockaddr_in *)nam)->sin_addr.s_addr = INADDR_ANY; at offset 4..8 of the caller-supplied sockaddr BEFORE any sa_len validation (in_pcbbind checks sa_len != sizeof *sin only afterwards). With a 4-byte bind sockaddr (getsockaddr minimum) the 4-byte zero write lands past the requested size β€” inside the 8-byte slab chunk's slack (zoneindex: sub-128B allocations round to 8-byte multiples, kern_slaballoc.c:638-645), so no adjacent-object corruption occurs on this allocator. Correctness/UB bug, no demonstrated security impact.

Build / run (root: divert socket)

cc -O -o div3441 div3441.c && ./div3441

Observed

bind(4B AF_INET sockaddr) = -1 errno=22 (Invalid argument)

EINVAL (not EAFNOSUPPORT) proves div_bind's else-branch β€” the write line β€” executed, with in_pcbbind rejecting the length afterwards. Note a fixed div_bind (early sa_len check) returns the same EINVAL, so this marker proves path execution on the current kernel, not a behavioral difference.

Fix verification

not_testable
per-fix-DF-3441

Confirmed kernel references

Detail

Evidence (decisive lines)

['findings/poc/DF-3441/README.md β€” observed errno=22 (EINVAL) for bind(4B AF_INET sockaddr)']

PoC changes

none (first-shot PoC ran clean)

Verified recommended fix

Validate nam->sa_len == sizeof(struct sockaddr_in) (and family) at the top of div_bind before zeroing sin_addr.

Verdict

div_bind()'s unvalidated 4-byte zero write at offset 4..8 of a 4-byte user bind sockaddr provably executes on the guest (bind returns EINVAL from in_pcbbind's later sa_len check, not EAFNOSUPPORT from div_bind's family gate β€” the write line's branch ran). The write lands inside the 8-byte slab chunk's own slack (DF slab allocator rounds sub-128B requests to 8-byte multiples), so there is no adjacent-object corruption and no security impact; this is a correctness/UB hardening defect. A fixed kernel returns the same EINVAL, so the marker demonstrates path execution, not a behavioral difference.