-n ':' / -n '' : strtok() NULL fed to strtol() crashes the vkernel during option parsing
Summary
A -n argument of only delimiters (or empty) makes strtok return NULL, passed unguarded to strtol - the child segfaults during getopt, before any boot output; the monitor masks it (WEXITSTATUS of a signaled child is 0) so the whole vkernel exits 0 silently. ktrace-proven (PSIG SIGSEGV, empty boot log); control boots. NULL-check+usage_err fix validated.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5774 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.4 KB | β raw | |
| VERDICT.md | β | 1.6 KB | β raw | |
| run5774.sh | β | 1.0 KB | view raw | |
| baseline_run.log | β | 214 B | view raw | |
| baseline.kdump.txt | β | 84 B | view raw | |
| fixed_run.log | β | 223 B | view raw | |
| fix.diff | β | 1.5 KB | view raw | |
| env.txt | β | 293 B | view raw | |
| verdict.json | β | 2.1 KB | view raw | |
| manifest.json | β | 650 B | view raw |
DF-5774 β -n : / -n '' makes strtok() return NULL and strtol(NULL) kills the vkernel before boot
File: sys/platform/vkernel64/platform/init.c
Lines: 394-395
Severity: Low (operator-input NULL-deref DoS of the vkernel process at option parsing)
Status: reproduced (stock) / fix validated.
Reproduce (guest, root, VKERNEL64 built)
cd /usr/obj/usr/src/sys/VKERNEL64 ktrace -i -f /tmp/vk5774.ktrace ./kernel.debug -m 512m -n : ; echo monitor rc=$? kdump -f /tmp/vk5774.ktrace | grep PSIG
Expected output
Vulnerable (baseline_run.log / baseline.kdump.txt):
monitor rc=0 kernel.debug PSIG SIGSEGV SIG_DFL <- vkernel child dies in strtol(NULL) (no boot output at all)
Control -n 2 parses fine and the boot proceeds
(Using anonymous memory (512 MB)).
Fixed (fixed_run.log, rebuilt kernel with fix.diff hunk 2):
-- kdump signals: kernel.debug CALL exit(0x40) <- EX_USAGE, no signal -- output was: Bad -n option
Root cause
init.c:394 tok = strtok(optarg, ":"); returns NULL when the whole -n
argument is delimiters or empty (-n :, -n '', -n ::), and
init.c:395 passes it straight to strtol(tok, NULL, 0) β a NULL nptr
dereference in libc. The child segfaults during getopt, the monitor sees
a signaled child (WEXITSTATUS of the wait status is 0) and exits 0 with
zero diagnostics.
Fix
fix.diff hunk 2: NULL-check tok and usage_err("Bad -n option").
DF-5774 β VERDICT
Status: reproduced (stock kernel, deterministic). Fix validated on a rebuilt kernel. Impact: none beyond process death (DoS of the vkernel at startup); classified Low. Confidence: certain.
-n : (or an empty -n argument): strtok(optarg, ":") at
init.c:394 consumes the whole delimiter string and returns NULL;
init.c:395 optcpus = strtol(tok, NULL, 0) dereferences NULL inside
libc β SIGSEGV of the vkernel child during option parsing, before
writepid()/boot output. The monitor's WEXITSTATUS(status) (init.c:228)
on a signaled child yields 0, so the whole vkernel exits 0 silently β
no diagnostic, no core by default.
Live: ktrace/kdump on the guest shows
kernel.debug PSIG SIGSEGV SIG_DFL (baseline.kdump.txt) with an empty
boot log; control -n 2 boots (Using anonymous memory (512 MB)).
This is the same operator-input crash class as the known DF-1031
(init_disk NULL deref on -c) but a distinct defect at a different
site (option parsing, not device setup).
Fix (fix.diff hunk 2: if (tok == NULL) usage_err("Bad -n option");)
validated on the rebuilt kernel: kdump now shows CALL exit(0x40)
(EX_USAGE) and the message Bad -n option β no signal, no crash
(fixed_run.log).
Not uid0-relevant: no memory-corruption primitive (NULL read).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedBaseline: PSIG SIGSEGV, no output. Patched kernel: exit(0x40) with 'Bad -n option', no signal, control -n 2 unchanged.
baseline.kdump.txt vs fixed_run.log
Confirmed kernel references
Detail
Evidence (decisive lines)
baseline_run.log + baseline.kdump.txt (PSIG SIGSEGV SIG_DFL; control -n 2 boots); fixed_run.log (exit 0x40, 'Bad -n option', no signal)
PoC changes
authored fresh (no seed); run5774.sh wraps the ktrace/kdump evidence and a -n 2 control boot
Verified recommended fix
if (tok == NULL) usage_err("Bad -n option"); after the strtok at init.c:394
Verdict
strtok(optarg, ":") at init.c:394 returns NULL for a -n argument made entirely of delimiters or empty ('-n :', "-n ''"), and init.c:395 feeds it to strtol() unguarded: the vkernel child segfaults during option parsing (ktrace: PSIG SIGSEGV SIG_DFL, zero boot output, monitor exits 0 silently). Same operator-input crash class as DF-1031, distinct site/defect. One-line NULL check fixes it; validated on the rebuilt kernel (clean EX_USAGE exit 0x40, 'Bad -n option').
No comments yet.