β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-3580

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3580 Β· 5 files
FileTypeDescriptionSize
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);
VERDICT.md
↓ download raw

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_testable
baseline no→ patch + rebuild →patched clean

one-line logic fix; not validated on guest (no iwn cdev without hardware).

findings/poc/DF-3580/fix.diff
↓ fix.diffper-fix-DF-3580

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.