readv/writev/extpreadv/extpwritev accept iovcnt==0 β POSIX EINVAL missing; uninitialized stack iovec pointer plumbed into fo_read/fo_write
| Field | Value |
|---|---|
| ID | DF-2732 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:N |
| CWE | CWE-20 / CWE-457 |
| File | sys/kern/sys_generic.c |
| Lines | 195, 227, 399, 431 (root kern_subr.c:448-456) |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-08-30 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
iovec_copyin validates only (u_int)iov_cnt > UIO_MAXIOV, so iovcnt==0 passes; the four vector syscalls then run with uio_iov pointing at the caller's uninitialized stack aiov, uio_iovcnt=0, uio_resid=0. POSIX mandates EINVAL for iovcnt<=0 (FreeBSD returns EINVAL). Reproduced: all ten tested forms return ret=0 errno=0. No panic reachable on stock (every surveyed consumer gates on uio_resid>0) β latent hazard for any consumer dereferencing uio_iov[0] before a resid check.
Recommended fix
In iovec_copyin (kern_subr.c), before the UIO_MAXIOV check:
if (iov_cnt <= 0) return (EINVAL);
Timeline
- 2026-08-30 Discovered during pass-2 audit of sys_generic.c (GLM 5.3); behavior verified on stock same run.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2732 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| iovcnt0.c | β | 2.7 KB | view raw | |
| run.log | β | 973 B | view raw | |
| run.2.log | β | 1.2 KB | view raw | |
| build.sh | β | 37 B | view raw | |
| run.sh | β | 20 B | view raw | |
| env.txt | β | 150 B | view raw |
Fix verification
not_testableConfirmed kernel references
Detail
Evidence (decisive lines)
['run.log (10/10 iovcnt==0 calls ret=0 errno=0; negative control -1 -> EMSGSIZE)', 'run.2.log (independent boot, identical)']
PoC changes
written fresh for this pass (no seed)
Verified recommended fix
Reject iovcnt <= 0 with EINVAL in iovec_copyin() (kern_subr.c) or at each syscall entry.
Verdict
readv/writev/extpreadv/extpwritev accept iovcnt==0 and return success on every fd type (file, socket, pipe, /dev/zero) instead of POSIX-mandated EINVAL, plumbing an uninitialized stack iovec pointer (uio_iov) with uio_iovcnt=0/uio_resid=0 into fo_read/fo_write dispatch. No memory-safety manifestation reachable on the stock kernel (all consumers gate on uio_resid>0); classified Low (correctness + latent uninit-pointer plumbing).
No comments yet.