sys_kenv KENV_SET ignores ksetenv() failure β variables rejected at limit boundaries (name/value of exactly KENV_M*LEN, full table) silently appear 'set'
| Field | Value |
|---|---|
| ID | DF-2895 |
| Status | new |
| Severity | Info |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N |
| CWE | CWE-252 Unchecked Return Value |
| File | sys/kern/kern_environment.c |
| Lines | 164 |
| Area | kern |
| Confidence | certain |
| Discovered | 2026-09-02 |
| Pass | 2 (GLM 5.3 second pass) |
| Bucket | base:kern |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
sys_kenv() calls ksetenv(name, value) and discards its return; ksetenv returns β1 (and sets nothing) when strlen(name)==128, the value hits the same boundary, or the dynamic table is full (512 entries). The privileged caller sees syscall success while the variable was never stored β silent configuration drop for boot-time tunables set at runtime. Info: not memory-unsafe; privileged-only. Fix: propagate EINVAL.
Timeline
- 2026-09-02 Discovered during pass-2 audit of kern_environment.c (GLM 5.3).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2895 Β· 10 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 690 B | β raw | |
| VERDICT.md | β | 860 B | β raw | |
| build.sh | β | 75 B | view raw | |
| env.txt | β | 509 B | view raw | |
| fix.diff | β | 387 B | view raw | |
| kenv_set.c | β | 1.5 KB | view raw | |
| manifest.json | β | 546 B | view raw | |
| run.log | β | 253 B | view raw | |
| run.sh | β | 71 B | view raw | |
| verdict.json | β | 2.0 KB | view raw |
DF-2895 β sys_kenv KENV_SET ignores ksetenv failure (Info-tier hardening verification)
What
sys_kenv KENV_SET ignores ksetenv failure
Build
cc -Wall -O2 -o kenv_set kenv_set.c (as root)
Run
./kenv_set
Expected
baseline (stock kernel): see verdict below fix kernel (all-41-fixes build): the bad behavior is gone
Files
README.md VERDICT.md build.sh env.txt fix.diff kenv_set.c manifest.json run.log run.sh verdict.json
Shared harness: h41.c (combined KLD, sysctl debug.h41.cmd=N driven).
Full batch context: the fix kernel contains all 41 hardening fixes of
this batch compiled together (build RC=0) and passed the complete A/B
suite plus a clean reboot.
DF-2895 β VERDICT
status: reproduced | reproduced: 1 | impact: none | confidence: certain
Mechanically reproduced as root on the stock guest via the kenv(2) syscall: KENV_SET with a name of exactly KENV_MNAMELEN (128) chars returns 0 (success) - the syscall's copyinstr accepts 129 bytes (kern_environment.c:132) but ksetenv rejects namelen 129 > 128 and stores nothing (kern_environment.c:265-266) - and a follow-up KENV_GET returns ENOENT: the privileged caller believes a boot tunable was set at runtime while it was silently dropped (return of ksetenv discarded at :164). Same at the value boundary and with a full dynamic table. Control: a 127-char name round-trips correctly. On the fix kernel the 128-char set returns -1/EINVAL.
Baseline vs fix kernel
baseline: see verdict above / run log fix: behavior changed as designed (see verdict)
Fix verification
fixedA/B: baseline silent drop; fix kernel propagates EINVAL from ksetenv failure.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log baseline: SET rv=0 + GET ENOENT; control 127 chars works; fix kernel: SET rv=-1 errno=22
PoC changes
KENV_* constants and kenv() prototype declared locally (not in stock headers)
Verified recommended fix
Propagate ksetenv failure as EINVAL
Verdict
Mechanically reproduced as root on the stock guest via the kenv(2) syscall: KENV_SET with a name of exactly KENV_MNAMELEN (128) chars returns 0 (success) - the syscalls copyinstr accepts 129 bytes (kern_environment.c:132) but ksetenv rejects namelen 129 > 128 and stores nothing (kern_environment.c:265-266) - and a follow-up KENV_GET returns ENOENT: the privileged caller believes a boot tunable was set at runtime while it was silently dropped (return of ksetenv discarded at :164). Same at the value boundary and with a full dynamic table. Control: a 127-char name round-trips correctly. On the fix kernel the 128-char set returns -1/EINVAL.
No comments yet.