# 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:

```c
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:

```c
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:

```c
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)
```
