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

PF_LOCAL sockaddr unparse: sun_len<pathoff underflow -> giant bcopy -> stack OOB write (DF-0509 v1 twin)

Summary

ng_ksocket_sockaddr_unparse(:321-339) PF_LOCAL: pathlen=sun->sun_len-pathoff(pathoff=2). NO validation sun_len>=pathoff. If sun_len<2: pathlen negative int -> bcopy(sun->sun_path, pathbuf, pathlen)(:331) interprets as size_t~2^64 -> massive stack OOB write into 256-byte pathbuf + OOB read from sun_path. pathbuf[pathlen]=\0(:332) writes at negative index. Identical to DF-0509(ng7). Generic helper ng_parse_generic_sockdata_getLength(:169-177) DOES guard this (returns 0 when sa_len<SADATA_OFFSET) but unparse omits guard. Medium (not High like ng7) because v1 ksocket is less commonly deployed and kernel-produced sockaddrs always have valid sun_len. Fix: pathlen=(sun_len<pathoff)?0:sun_len-pathoff.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-0521 Β· 9 files
FileTypeDescriptionSize
df0521.c trigger-source static arithmetic demonstration of sun_len<pathoff underflow 3.5 KB view raw
build.sh build-script cc -O2 -o df0521 df0521.c 98 B view raw
run.sh run-script ./df0521 243 B view raw
VERDICT.md verdict mechanism, reachability analysis, fix rationale 3.6 KB ↓ raw
fix.diff suggested-fix clamp pathlen to 0 when sun_len<pathoff (mirrors generic helper) 513 B view raw
run.log run-log static PoC output showing (size_t)-1 = ~2^64 625 B view raw
env.txt environment uname + cc version 188 B view raw
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
VERDICT.md verdict mechanism, reachability analysis, fix rationale
↓ download raw

DF-0521 β€” PF_LOCAL sockaddr unparse sun_len<pathoff underflow

Verdict: CODE-PATH CONFIRMED (defense-in-depth); latent β€” no live unprivileged trigger

Mechanism (confirmed at code level)

ng_ksocket_sockaddr_unparse() in sys/netgraph/ksocket/ng_ksocket.c:313-339 converts a struct sockaddr_un to ASCII for netgraph status output. The PF_LOCAL branch at line 321 computes:

const int pathoff = OFFSETOF(struct sockaddr_un, sun_path);  /* = 2 */
const struct sockaddr_un *sun = (const struct sockaddr_un *)sa;
const int pathlen = sun->sun_len - pathoff;       /* SIGNED int! */
char pathbuf[SOCK_MAXADDRLEN + 1];                 /* 256-byte stack buf */

bcopy(sun->sun_path, pathbuf, pathlen);            /* size_t -> ~2^64 */
pathbuf[pathlen] = '\0';                           /* neg-index write */

If sun->sun_len < 2 (i.e. < pathoff), pathlen becomes a negative int. When bcopy interprets it as size_t, it becomes ~2^64, producing a massive OOB write into the 256-byte stack buffer (and a corresponding OOB read from sun->sun_path). pathbuf[pathlen] = 0 then writes at a negative stack offset.

The static df0521 PoC demonstrates the arithmetic:

well-formed: sun_len=14, pathoff=2 -> pathlen=12 (OK)
malformed:  sun_len=1, pathoff=2 -> pathlen=-1
            (size_t)pathlen = 18446744073709551615 bytes
            target buffer:   pathbuf[255+1] (stack)
            pathbuf[pathlen] = '\0' writes at stack offset -1
            => stack OOB write of ~18446744073709551615 bytes + neg-index write

Reachability β€” why this is defense-in-depth

unparse is invoked by the netgraph subsystem when serializing a sockaddr to ASCII (e.g. for ngctl msg ... getname / getpeername text output). The sockaddr it operates on comes from so_pru_peeraddr / so_pru_sockaddr, which the kernel always fills with a valid sun_len. No path in the kernel currently produces a sockaddr_un with sun_len < 2.

Additionally, the netgraph control socket itself is root-only:

$ ngctl list
ngctl: socket: Operation not permitted

An unprivileged user cannot even reach the unparse code path β€” driving ng_ksocket requires socket(AF_NETGRAPH) which needs root. The bug would become live only if (a) some other kernel path produced a malformed sockaddr_un, or (b) netlink-style user-supplied data reached unparse directly. The identical twin in netgraph7 (DF-0509) has the same property.

The sibling helper ng_parse_generic_sockdata_getLength (sys/netgraph/ksocket/ng_ksocket.c:169-177) does guard this case:

return (sa->sa_len < SADATA_OFFSET) ? 0 : sa->sa_len - SADATA_OFFSET;

β€” the PF_LOCAL special-case unparse simply omits the same guard.

Severity assessment

Medium is appropriate: real code defect with a stack-OOB-write primitive, but gated behind (1) root access to drive netgraph, AND (2) a separate kernel bug to produce the malformed input. Matches the finding's own framing ("Medium (not High like ng7) because v1 ksocket is less commonly deployed and kernel-produced sockaddrs always have valid sun_len").

Fix

fix.diff adds the missing guard, mirroring the existing ng_parse_generic_sockdata_getLength pattern:

const int pathlen = (sun->sun_len < pathoff) ? 0 :
    sun->sun_len - pathoff;

A one-line clamp that eliminates the underflow unconditionally.

Build / run

ssh dfbsd-maxx 'mkdir -p poc/DF-0521'
scp findings/poc/DF-0521/{df0521.c,build.sh,run.sh} dfbsd-maxx:poc/DF-0521/
ssh dfbsd-maxx 'cd poc/DF-0521 && sh ./build.sh && sh ./run.sh'
# prints the underflow arithmetic; no kernel side-effect (defense-in-depth)

Fix verification

not_testable

compile validated

see evidence pack

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. ng_ksocket PF_LOCAL sun_len massive stack OOB. Latent (kernel never produces malformed sockaddr). Root-only.