# DF-3448 — VERDICT **Status: reproduced** (deterministic, 2/2 runs) · **Impact: none (config corruption, root-only)** · **Confidence: certain** ## Root cause (path:line) `sys/netinet6/in6_proto.c:437-453`, `sysctl_ip6_tempvltime()`: ```c 447: old = ip6_temp_valid_lifetime; 448: error = SYSCTL_IN(req, arg1, sizeof(int)); /* writes ip6_temp_valid_lifetime */ 449: if (ip6_temp_valid_lifetime < ip6_temp_preferred_lifetime) { 450: ip6_temp_preferred_lifetime = old; /* restores the WRONG variable */ 451: return (EINVAL); 452: } ``` `arg1` is `&ip6_temp_valid_lifetime` (registration at in6_proto.c:495-498), so the SYSCTL_IN at line 448 installs the new value; the rejection path at line 450 was supposed to undo it (`ip6_temp_valid_lifetime = old`, exactly as the KAME/FreeBSD original and as the twin handler `sysctl_ip6_temppltime` at in6_proto.c:418-435 correctly does for `ip6_temp_preferred_lifetime`). The DragonFly transcription swapped the variable name. Consequences on every rejected write: - `ip6_temp_valid_lifetime` keeps the value the kernel just told the administrator was invalid (persistent misconfiguration); - `ip6_temp_preferred_lifetime` is overwritten with the old *valid* lifetime, so afterwards `preferred (604800) > valid (100)` — the exact invariant the check exists to prevent. These feed `in6_tmpaddpub()`/temporary-address lifetime math in sys/netinet6/in6_ifadd.c (e.g. `ip6_temp_preferred_lifetime`, `ip6_temp_valid_lifetime` use when creating/regenerating RFC 4941 temporary addresses), producing wrong preferred/valid expiry schedules for privacy addresses when `net.inet6.ip6.use_tempaddr` is later enabled. ## How verified Ran `trigger.sh` on the live QEMU guest (stock INVARIANTS kernel, DragonFly 6.5-DEVELOPMENT #0, securelevel -1, root): - `run.log` (run 1) and `run.2.log` (run 2): after the EINVAL, `tempvltime` reads back **100** (the rejected value) and `temppltime` reads back **604800** (old vltime). Both bug halves confirmed twice, deterministically. - Guest sysctls restored to defaults afterwards (86400 / 604800) — guest left clean. ## Why not higher severity - Write path is root-only (sysctl CTLFLAG_RW handler; no unprivileged route to it — sockopt/sysctl privilege). - No memory safety: both variables are plain `int`s consumed arithmetically by address-lifetime logic; no length/index/pointer derived from them. - Worse-case realistic impact: administrator gets a silent persistent invalid configuration plus a corrupted preferred lifetime — a correctness/privacy-regression bug, not an exploitation primitive. ## Fix validation `fix.diff` restores the correct variable (one-word change, mirroring the sibling `sysctl_ip6_temppltime` and the KAME/FreeBSD original). Fix validation by rebuild was **not performed** — mandatory rebuilds apply to memory-corruption findings; for this Low logic finding the correction is verifiable by inspection (fix_status: not_testable). ## Fix ```diff --- a/sys/netinet6/in6_proto.c +++ b/sys/netinet6/in6_proto.c @@ -446,7 +446,7 @@ old = ip6_temp_valid_lifetime; error = SYSCTL_IN(req, arg1, sizeof(int)); if (ip6_temp_valid_lifetime < ip6_temp_preferred_lifetime) { - ip6_temp_preferred_lifetime = old; + ip6_temp_valid_lifetime = old; return (EINVAL); } ``` ## 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.