DragonFlyBSD Kernel Audit
DF-0023 / run.log
← back to finding ↓ download raw
==== BASELINE RUN (unpatched #0 kernel: 6.5-DEVELOPMENT #0, Thu Jul 2 06:02:54 UTC 2026) ====
$ ./einval_noop
read(fd,buf,SIZE_MAX) = 0, errno=0 (NOT EINVAL)
write child PID 869: probing write(fd,buf,SSIZE_MAX+1)...
write child PID 869: HUNG in kernel after 4s (DoS - uninterruptible infinite loop in mmrw)
after SIGKILL: child STILL ALIVE (unkillable in kernel loop)
RUN_EXIT=0
(ssh connection then lingered due to the orphaned, wedged child keeping the
 session's process group open -> parent tool timed out at RC=124 while the
 PoC process itself had already printed RUN_EXIT=0.)

==== INTERPRETATION ====
1. read(/dev/null, buf, SIZE_MAX) returns 0 with errno=0 instead of -1/EINVAL.
   => the guard `if ((ssize_t)uap->nbyte < 0) error = EINVAL;` in sys_read
      (sys/kern/sys_generic.c:130-131) is a dead store: `error` is unconditionally
      overwritten by `error = kern_preadv(...)` at line 143 before the `return`.
2. write(/dev/null, buf, SSIZE_MAX+1) HANGS in an infinite, uninterruptible
   kernel loop.  Chain:
      sys_write (sys_generic.c:336-337)   -- same dead-store EINVAL
        -> kern_pwritev (:456) -> dofilewrite (:506 fo_write)
        -> /dev/null cdev d_write = mmwrite (kern_memio.c:396) -> mmrw (:222)
      mmrw line 225 declares `u_int c;` (32-bit);
      line 298 does `c = iov->iov_len;` where iov_len is size_t 64-bit =
      0x8000000000000000, so c truncates to 0.
      line 382 `uio->uio_resid -= c;` subtracts 0, so `while(uio_resid>0)`
      (line 232) never terminates.  No signal check in the loop => SIGKILL
      cannot be delivered => the process is unkillable while pinning a CPU.
   => local DoS: any unprivileged user can permanently pin every CPU core
      with unkillable processes via a single write() syscall each.

Contrast the CORRECT sibling sys_extpread (sys_generic.c:161-162) which uses
`return(EINVAL);` and thus rejects nbyte>SSIZE_MAX as POSIX requires.