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

sotoxsocket()/ssbtoxsockbuf() never initialize structure padding β€” net.inet.tcp.pcblist leaks 8 uninitialized kernel-stack bytes per record to unprivileged users (incl. kernel-pointer halves)

Field Value
ID DF-2838
Status new
Severity Low
CVSS 3.1 CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
CWE CWE-908 Use of Uninitialized Memory
File sys/kern/uipc_socket2.c
Lines 188-198, 827-848
Area kern
Confidence certain
Discovered 2026-08-31
Pass 2 (GLM 5.3 second pass)
Bucket kernleak
Reported pending
Known CVE none
CVE match novel

Summary

sotoxsocket fills every named field of struct xsocket but not its 4 tail-padding bytes (164β†’168 on amd64); ssbtoxsockbuf likewise leaves 2 tail pad bytes per struct xsockbuf (46β†’48). tcp_pcblist_sysctl exports a never-zeroed stack struct xtcpcb xt to any reader of the world-readable net.inet.tcp.pcblist, shipping 8 stale kernel-stack bytes per TCP pcb record. Demonstrated unprivileged: so_rcv pad=0xffff in every record of every run, xsocket tail pad=fffff801 (upper half of a kernel-range pointer) in 2/3 runs β€” the same pointer class as the verified DF-2702 leak. in_pcb.c pre-zeroes its buffer so the inpcb/udp lists are clean; the AF_UNIX variant was DF-2558.

Proof of contest

VERIFIED unpriv on the stock guest over 3 runs with TCP churn (findings/poc/DF-2838/pcbleak.c): rec#N pads: rcv=ffff snd=0000 tail=fffff801 (runs 1-2), tail=00000000 (run 3, by chance); all-zero on the bzero-patched kernel (validated in-guest via nativekernel rebuild).

bzero(xsb, sizeof(*xsb)); / bzero(xso, sizeof(*xso)); at the top of both exporters (validated diff in the pack).

Timeline

  • 2026-08-31 Discovered during pass-2 audit of uipc_socket2.c (GLM 5.3); unpriv pad leak reproduced + fix validated same run.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2838 Β· 13 files
FileTypeDescriptionSize
README.md β€” 2.4 KB ↓ raw
VERDICT.md β€” 2.4 KB ↓ raw
pcbleak.c β€” 3.2 KB view raw
build.sh β€” 123 B view raw
run.sh β€” 20 B view raw
run.log β€” 858 B view raw
run.2.log β€” 858 B view raw
run.3.log β€” 858 B view raw
fix_validation.log β€” 865 B view raw
fix.diff β€” 601 B view raw
env.txt β€” 261 B view raw
manifest.json β€” 801 B view raw
verdict.json β€” 3.2 KB view raw

DF-2838 β€” sotoxsocket()/ssbtoxsockbuf() leave struct padding uninitialized β†’ kernel stack bytes leaked via net.inet.tcp.pcblist

What

sys/kern/uipc_socket2.c:827-848 (sotoxsocket) fills every named field of struct xsocket but never the structure padding; likewise ssbtoxsockbuf() (lines 188-198) for struct xsockbuf. On amd64:

  • struct xsockbuf = 46 content bytes, tail-padded to 48 β†’ 2 uninitialized bytes per sockbuf (after short sb_flags)
  • struct xsocket = 164 content bytes, tail-padded to 168 β†’ 4 uninitialized bytes (after uid_t so_uid)

tcp_pcblist_sysctl() (sys/netinet/tcp_subr.c ~1265) uses a stack struct xtcpcb xt; that is never zeroed, calls sotoxsocket(), and SYSCTL_OUTs the whole record to any unprivileged reader of net.inet.tcp.pcblist (world-readable; netstat source). Result: 8 stale kernel-stack bytes per TCP pcb record reach userland. (sys/netinet/in_pcb.c:2467 pre-zeroes its buffer, so only the tcp pcblist path leaks; the unix variant was already filed as DF-2558.)

Impact

Unprivileged local info leak of limited kernel memory (kernel stack residue). Demonstrated values include 0xfffff801 β€” the upper half of a kernel text/data pointer (0xfffff801xxxxxxxx is DragonFly's kernel address range) β€” varying across runs. On KASLR-enabled deployments this is one of the two halves an attacker needs; contents are incidental stack garbage, so the leak is limited but real. Low severity.

Reproduce (unprivileged)

cc -O -D_KERNEL_STRUCTURES -include /usr/include/stdint.h \
   -I/usr/src/sys -I/usr/src/sys/sys -o pcbleak pcbleak.c
./pcbleak

The program dumps exactly the three pad regions for every pcblist record, three times, churning TCP connections between passes to dirty the sysctl thread's stack.

Observed (stock kernel, see run.log / run.2.log / run.3.log):

rec#0 pads: rcv=ffff snd=0000 tail=fffff801
...
run 3: tail=00000000

rcv pad constant 0xffff, xsocket tail pad fffff801 (kernel pointer upper half) in runs 1-2, zeroed by chance in run 3 β€” nonzero kernel-stack bytes in β‰₯2 of 3 runs, pattern matches kernel pointers.

Files

  • pcbleak.c, build.sh, run.sh
  • run.log, run.2.log, run.3.log β€” three decisive runs (stock kernel)
  • fix_validation.log β€” same PoC on the one-fix kernel (DF-2836+DF-2838 both applied): all pads zero
  • fix.diff β€” proposed fix (validated in-guest)
  • env.txt
VERDICT.md
↓ download raw

DF-2838 VERDICT

Status: reproduced (unprivileged; nonzero kernel-stack bytes in β‰₯2/3 runs, contents matching kernel-pointer patterns) Impact: leak β€” limited (8 bytes per pcb record) uninitialized kernel stack disclosed to unprivileged users.

Root cause (path:line)

  • sys/kern/uipc_socket2.c:188-198 β€” ssbtoxsockbuf() fills the 7 named fields of struct xsockbuf (46 content bytes) but never the 2 tail-padding bytes (struct size 48).
  • sys/kern/uipc_socket2.c:827-848 β€” sotoxsocket() likewise leaves struct xsocket's 4 tail-padding bytes (164 β†’ 168) uninitialized.
  • Sink: sys/netinet/tcp_subr.c tcp_pcblist_sysctl() (~1265) uses a stack struct xtcpcb xt; that is never zeroed and SYSCTL_OUTs the full record to any reader of net.inet.tcp.pcblist (world-readable).

Per record: 2 (so_rcv pad) + 2 (so_snd pad) + 4 (xsocket tail pad) = 8 stale kernel-stack bytes. (in_pcb.c:2467 pre-zeroes its buffer; the AF_UNIX variant of this class was filed as DF-2558.)

Reproduction

Unprivileged ./pcbleak β€” dumps exactly the three pad regions per record, 3 runs with connection churn in between (run.log / run.2.log / run.3.log):

rec#0 pads: rcv=ffff snd=0000 tail=fffff801     (runs 1-2, all records)
rec#0 pads: rcv=ffff snd=0000 tail=00000000     (run 3)
  • so_rcv pad = 0xffff in every record of every run β€” deterministic non-zero stack residue.
  • xsocket tail pad = 0xfffff801 β€” the upper half of a kernel-range pointer (0xfffff801xxxxxxxx), the same address class seen in the verified DF-2702 leak. Value varies with stack history (zeroed in run 3), confirming it is uninitialized memory and not a constant.

Leaked bytes match kernel-memory pattern across β‰₯2 of 3 runs β‡’ leak, reproduced per the honest-status table.

Fix validation

fix.diff (bzero the exported structs in ssbtoxsockbuf/sotoxsocket) applied in-guest together with the DF-2836 fix, kernel rebuilt, PoC re-run: all pad bytes zero in all records, all runs (fix_validation.log). Baseline vs patched differ exactly on the leak β‡’ fix validated.

Threat

Unprivileged local user reads 8 bytes/record of kernel stack residue; when the residue contains pointer halves it is KASLR-relevant information. The disclosure is structurally deterministic (the pads are never written), so an attacker can retry under differing stack histories to harvest more samples.

Fix verification

fixed
baseline reproduced→ patch + rebuild →patched clean

Applied bzero() to both exporters in the guest /usr/src copy, rebuilt with make nativekernel KERNCONF=X86_64_GENERIC, rebooted, re-ran the identical PoC: baseline nonzero pads (rcv=ffff, tail=fffff801) -> all pads zero in all records on the patched kernel (fix_validation.log). Leak gone; fix validated.

['findings/poc/DF-2838/fix.diff', 'findings/poc/DF-2838/fix_validation.log']
↓ fix.diffDragonFly dfbsd 6.5-DEVELOPMENT #1: Wed Sep 2 04:31:48 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (one-fix nativekernel: DF-2836 + DF-2838 edits in uipc_socket2.c)

Confirmed kernel references

Detail

Evidence (decisive lines)

["run.log/run.2.log: 'rec#0 pads: rcv=ffff snd=0000 tail=fffff801' (every record)", "run.3.log: 'tail=00000000' - value varies with stack history, proving uninitialized memory", 'fix_validation.log: all pads zero on the patched kernel']

PoC changes

Seed written fresh. Fixed twice during bring-up: (1) include stdint.h/-include for uint32_t with _KERNEL_STRUCTURES against kernel headers, (2) corrected pad offset math (first version sampled content bytes at offsetof(so_snd)-46; correct is offsetof(so_rcv)+46 - first run's all-zero result was offset error, not absence of leak).

Verified recommended fix

bzero the exported struct at the top of sotoxsocket() and ssbtoxsockbuf() (see fix.diff); validated in-guest.

Verdict

sotoxsocket (uipc_socket2.c:827-848) and ssbtoxsockbuf (188-198) never initialize structure padding (2 tail bytes per struct xsockbuf, 4 tail bytes of struct xsocket on amd64). tcp_pcblist_sysctl (tcp_subr.c ~1265) exports a never-zeroed stack struct xtcpcb to any unprivileged reader of net.inet.tcp.pcblist, shipping 8 stale kernel-stack bytes per TCP pcb record. Demonstrated unprivileged 3 runs: so_rcv pad = 0xffff in every record of every run; xsocket tail pad = fffff801 (upper half of a kernel-range pointer, 0xfffff801xxxxxxxx) in runs 1-2, 0 by chance in run 3 - uninitialized memory with kernel-pointer pattern across >=2/3 runs. Patched kernel (bzero in both exporters) zeroes all pads (fix_validation.log). Limited-magnitude kernel info leak, KASLR-relevant on hardened deployments.