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).
Recommended fix
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)
PoC verification
Evidence pack
findings/poc/DF-2838 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 (aftershort sb_flags)struct xsocket= 164 content bytes, tail-padded to 168 β 4 uninitialized bytes (afteruid_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.shrun.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 zerofix.diffβ proposed fix (validated in-guest)env.txt
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 ofstruct xsockbuf(46 content bytes) but never the 2 tail-padding bytes (struct size 48).sys/kern/uipc_socket2.c:827-848βsotoxsocket()likewise leavesstruct xsocket's 4 tail-padding bytes (164 β 168) uninitialized.- Sink:
sys/netinet/tcp_subr.ctcp_pcblist_sysctl()(~1265) uses a stackstruct xtcpcb xt;that is never zeroed andSYSCTL_OUTs the full record to any reader ofnet.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_rcvpad =0xffffin every record of every run β deterministic non-zero stack residue.xsockettail 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
fixedApplied 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']
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.
No comments yet.