โฌข DragonFlyBSD Kernel Audit
โ† dashboard
DF-0027

wait4/wait6 leak uninitialized kernel stack via status, rusage/wrusage and siginfo on WNOHANG/WCONTINUED return paths

Field Value
ID DF-0027
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-909 Initialization of Resource with a Sensitive Value; CWE-200 Information Exposure
File sys/kern/kern_exit.c
Lines 913-948 (wait4), 950-992 (wait6), 1388-1431 (kern_wait)
Area kern
Confidence certain
Discovered 2026-06-29
Reported pending

Summary

sys_wait4 and sys_wait6 declare uninitialized stack locals (int status;, struct __wrusage wrusage;, siginfo_t info;), call kern_wait(...), then copyout() those locals whenever kern_wait returns error == 0. But kern_wait returns error == 0 on the WNOHANG-with-no-waitable-child path (:1427-1431, only *res is set) and on the WCONTINUED match path (:1388-1419, leaves *wrusage untouched) without writing *status, *wrusage, or *info. The wrappers therefore copyout uninitialized kernel-stack bytes to userland: up to ~4 B (status), ~72 B (wait4 rusage), ~144 B (wait6 __wrusage), and ~128 B (wait6 siginfo_t) per call. An unprivileged local user with a running child can sample this deterministically and repeatably โ€” a KASLR/stack-residue oracle.

Root cause

sys/kern/kern_exit.c:913-948 (sys_wait4):

struct __wrusage wrusage;   /* :916  uninitialized */
int status;                 /* :918  uninitialized */
...
error = kern_wait(idtype, id, &status, options, &wrusage, NULL,
                  &sysmsg->sysmsg_result);
if (error == 0 && uap->status)
    error = copyout(&status, uap->status, sizeof(*uap->status));     /* :942 */
if (error == 0 && uap->rusage) {
    ruadd(&wrusage.wru_self, &wrusage.wru_children);
    error = copyout(&wrusage.wru_self, uap->rusage, sizeof(*uap->rusage)); /* :945 */
}

sys_wait6 (:950-992) is the same shape plus a siginfo_t copyout (:991).

kern_wait WNOHANG-no-match (:1427-1431):

if (options & WNOHANG) {
    *res = 0;
    error = 0;
    goto done;          /* status/wrusage/info never written */
}

The WCONTINUED branch (:1388-1419) sets *res, *status = SIGCONT, and *info but never writes *wrusage. ECHILD (nfound==0) is safe only because the wrappers gate copyout on error == 0; the WNOHANG-no-match path returns error == 0 and is therefore exposed.

Threat model & preconditions

  • Attacker position: any local unprivileged user.
  • Privileges gained or impact: information disclosure. Each call leaks up to ~4 B + ~72 B (wait4) or ~144 B + ~128 B (wait6) of kernel-stack residue (pointer fragments, canary, prior-syscall data). A samplable KASLR/stack-residue oracle that lowers the bar for exploiting a separate kernel bug. Not a direct LPE.
  • Required config or capabilities: none; default kernel.
  • Reachability: wait4(child, &status, WNOHANG, &ru) or wait6(...) with a running (non-waitable) child present.

Proof of concept

PoC source: findings/poc/DF-0027/wait_leak.c

Build & run (unprivileged)

cc -o wait_leak findings/poc/DF-0027/wait_leak.c
./wait_leak

Expected output

iter  0: status=0x<residue> (LEAKED)  rusage-nonzero-byte (LEAKED)
...
result: LEAK CONFIRMED

Impact

Low-impact kernel-stack info leak, samplable in a tight loop. Useful as a KASLR/stack-residue oracle ingredient. Rated Low (info-leak only; same class as DF-0007/DF-0010).

Initialize all output parameters up front in kern_wait so the early-success non-reap return paths cannot leak:

--- a/sys/kern/kern_exit.c
+++ b/sys/kern/kern_exit.c
@@ -1020
+   /*
+    * Initialize all output parameters up front so that early-success
+    * return paths that do not reap a child (e.g. WNOHANG with no
+    * waitable child, or WCONTINUED) cannot leak uninitialized kernel
+    * stack memory back to userland via the wait4/wait6 copyouts.
+    */
+   *status = 0;
+   bzero(wrusage, sizeof(*wrusage));
+   if (info)
+       bzero(info, sizeof(*info));

(Place this at the top of kern_wait, after argument validation.)

References

Timeline

  • 2026-06-29 Discovered during automated file-by-file audit of sys/kern/kern_exit.c.
  • pending Reported to DragonFlyBSD security contact.