Inverted privilege-check result in iwn_cdev_ioctl(): returns success when caps_priv_check fails
Summary
iwn_cdev_ioctl() runs caps_priv_check(SYSCAP_NODRIVER|__SYSCAP_NULLCRED) and then 'if (rc != 0) return (0);' - reporting success to unprivileged callers whose request is silently skipped (FreeBSD-heritage typo). Effectively fail-closed today (cdev is 0600 root:wheel), so no bypass exists; filed as hardening because an inverted security-check return is one refactor away from a real bypass. The SIOCGIWNSTATS handler also ignores the user-supplied dst_len but the copyout length is fixed (no overflow).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3580 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.7 KB | β raw | |
| VERDICT.md | β | 1.5 KB | β raw | |
| fix.diff | β | 295 B | view raw | |
| verdict.json | β | 1.8 KB | view raw | |
| manifest.json | β | 526 B | view raw |
DF-3580 β Inverted privilege-check result in iwn_cdev_ioctl()
Root cause (source-verified)
sys/dev/netif/iwn/if_iwn.c:5157-5160:
rc = caps_priv_check((td ? td->td_ucred : NULL),
SYSCAP_NODRIVER | __SYSCAP_NULLCRED);
if (rc != 0)
return (0); /* <-- returns SUCCESS on privilege FAILURE */
When the caller lacks the privilege (rc != 0) the ioctl returns 0
(success) while doing nothing; when the caller has the privilege the
command executes. The intended code is return (rc); (or EPERM). This is a
long-standing typo inherited from FreeBSD's if_iwn.c (priv_check +
return (0)).
Additionally, the SIOCGIWNSTATS handler (if_iwn.c:5162-5169) trusts the
user-supplied struct iwn_ioctl_data { void *dst_addr; int dst_len; }
without touching dst_len β the copyout length is fixed
(sizeof(struct iwn_stats)), so no overflow arises, but the in-code
"XXX validate permissions/memory/etc?" comment (if_iwn.c:5166) is accurate.
Impact
None demonstrated. The device node is created 0600 root:wheel
(if_iwn.c:776-777), and the buggy early-return is fail-closed in effect
(unprivileged callers get errno 0 but no data β an errno-correctness bug,
not an exposure). Filing as Info/hardening: inverted security-check results
are exactly the kind of line a future refactor turns into a real bypass
(e.g. if someone "fixes" the return into goto out semantics or reuses the
pattern elsewhere).
Status
untested β behavior is a two-line logic inversion; the cdev exists only with iwn hardware attached, which the audit guest lacks. Source-certain.
Fix
- if (rc != 0)
- return (0);
+ if (rc != 0)
+ return (rc);
DF-3580 β VERDICT
Status: untested. The /dev/iwn* cdev only exists when iwn hardware is
attached; the audit guest is virtio-only. The defect is a two-line logic
inversion visible on read.
Analysis
iwn_cdev_ioctl() (if_iwn.c:5139-5180):
rc = caps_priv_check(..., SYSCAP_NODRIVER | __SYSCAP_NULLCRED);
if (rc != 0)
return (0);
Privilege failure β errno 0 ("success") with the requested action silently skipped. Privilege success β action performed. Impact assessment:
- No unauthorized access: unprivileged callers receive no data (the fail-closed direction).
- The node is 0600 root:wheel (if_iwn.c:776-777) so unprivileged open() already fails; the caps check is defense-in-depth whose result is misreported.
- Correctness/robustness issue and a hazardous pattern (an inverted security-check return is one refactor away from a real bypass), hence Info severity, confidence certain (the code is unambiguous).
SYSCAP_NODRIVER (sys/sys/caps.h:153) is the DragonFly capability gating
driver ioctls; __SYSCAP_NULLCRED only permits the NULL-credential form of
the call.
Fix
Return the failure code: return (rc); (fix.diff).
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
not_testableone-line logic fix; not validated on guest (no iwn cdev without hardware).
findings/poc/DF-3580/fix.diff
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (code excerpt + impact analysis), fix.diff (one-line fix), README.md.
PoC changes
none β Info-level logic bug; no PoC authored.
Verified recommended fix
Propagate the caps_priv_check failure: return (rc);
Verdict
Inverted privilege-check result in iwn_cdev_ioctl(): if_iwn.c:5157-5160 returns 0 (success) when caps_priv_check() FAILS, instead of propagating rc/EPERM. Effectively fail-closed (unprivileged callers get errno 0 but no data; node is 0600 root:wheel anyway), so no access bypass exists today β filed as Info/hardening because an inverted security-check return is a hazardous pattern one refactor away from a real bypass. Untestable on the virtio-only guest (cdev exists only with iwn hardware). Code is unambiguous, hence confidence certain.
No comments yet.