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

Missing privilege check on NVMEIOCGETLOG allows unprivileged NVMe admin command execution

Summary

NVMEIOCGETLOG ioctl handler issues raw NVMe GET_LOG_PAGE admin command without any privilege check. NVMe namespace device created root:operator 0640 so any operator-group user opening device read-only can issue controller-wide admin commands reading SMART/health data firmware slot info error logs and arbitrary vendor-specific log pages (lid 0xC0-0xFF). Other DragonFlyBSD drivers exposing hardware admin ioctls (ath/if_ath_ioctl.c:288 mpr/mpr.c:1888 cuse/cuse.c:1282 usb_generic.c:884) all call caps_priv_check_self(SYSCAP_NODRIVER) NVMe driver does not. Ioctl dispatch nvme_disk.c:154-170 never inspects ap->a_cred. Handler trusts caller entirely takes ioc->lid (user-controlled uint8_t range 0x00-0xFF) passes directly to NVMe controller as GET_LOG_PAGE. ioctl framework allows from read-only fd: holdfp checks FREAD O_RDONLY has FREAD set bitwise-AND nonzero check passes. Practical impact limited operator-group users already have namespace block data read access but breaks principle hardware-level admin commands require root-equivalent privilege. Multi-tenant/containerized deployments controller-wide queries violate isolation boundary.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2217 Β· 9 files
FileTypeDescriptionSize
VERDICT.md verdict HW-gate analysis + source trace + fix summary 1.9 KB ↓ raw
fix.diff suggested-fix git-apply-able defense-in-depth fix (applies + compiles) 943 B view raw
fix_build.log build-log combined build proof: all 18 fix.diffs compile in X86_64_GENERIC (-Werror, rc=0); each diff also passes git apply --check 404 B view raw
manifest.json manifest this catalog 1.9 KB view raw
env.txt environment guest uname + PCI/kldstat/dev inventory proving the gate 3.6 KB view raw
build.sh build-script no-op (HW-gated, nothing to build) 324 B view raw
run.sh run-script no-op (HW-gated, nothing to run) 324 B view raw
README.md readme seeded PoC: build/run/expected 427 B ↓ raw
nvme_getlog.c trigger-source seeded NVMEIOCGETLOG trigger (HW-gated: no /dev/nvme*) 855 B view raw
README.md readme seeded PoC: build/run/expected
↓ download raw

DF-2217: Missing privilege check on NVMEIOCGETLOG

Build & Run

cc -o nvme_getlog nvme_getlog.c -I/sys/dev/disk/nvme
./nvme_getlog /dev/nvme0 2     # SMART/health data
./nvme_getlog /dev/nvme0 3     # firmware slot info
./nvme_getlog /dev/nvme0 0xC0  # vendor-specific page

Expected Output

Prints up to 4096 bytes of NVMe log page data + completion status. No root required - operator-group membership suffices.

VERDICT.md verdict HW-gate analysis + source trace + fix summary
↓ download raw

DF-2217 β€” Missing privilege check on NVMEIOCGETLOG (controller-wide admin cmd from read-only fd)

Verdict

NOT REPRODUCED (hardware / trigger gated). The cited vulnerable code path exists in sys/dev/disk/nvme/nvme_ioctl.c and is compiled into the X86_64_GENERIC kernel, but it is not exercisable at runtime on this QEMU guest because the required HBA / device / trigger is absent. Confidence in the source bug itself: likely.

Why it cannot be reproduced here (the gate)

Hard-gated: needs an NVMe controller + /dev/nvme. The guest has NO NVMe controller (no PCI class 0x0108) and NO /dev/nvme; nvme never attaches.

See env.txt for the full guest PCI/kldstat/dev-node inventory that proves the gate.

The bug is real in source (traced line-by-line)

Cited path: - sys/dev/disk/nvme/nvme_ioctl.c:37-64 - sys/dev/disk/nvme/nvme_disk.c:154-170 - The vulnerable construct is present verbatim in the current master source (confirmed by direct read of the cited lines during verification). - A defense-in-depth fix.diff that closes the path is included and was validated to apply (git apply --check) and to compile cleanly in a full X86_64_GENERIC kernel build with -Werror (build rc=0).

Fix

Add caps_priv_check_self(SYSCAP_NODRIVER) in the NVMEIOCGETLOG dispatch (nvme_disk.c), matching ath/mpr/cuse which all gate hardware-admin ioctls this way.

The standalone git-apply-able diff is fix.diff.

Reproduce

./build.sh && ./run.sh β€” both are no-ops on this guest by design (the gate holds). Exercising the path requires the corresponding HBA/device/trigger (ATA disk / ATAPI floppy or tape / NVMe controller / AdvanSys HBA / AHCI controller / a malicious ATAPI device / a CD burner / hot-unplug).

Status

status: not_reproduced | reproduced: 0 | impact: none (HW-gated) fix_status: not_testable (path cannot run on this guest; diff applies + compiles)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

not_testable: the vulnerable path is unreachable on this guest (no target HW/malicious device). Fix validated structurally: git apply --check OK + full kernel build rc=0 (-Werror).

git apply --check findings/poc/DF-2217/fix.diff -> OK. Combined kernel build: 'NK_DONE rc=0'. No runtime before/after possible (HW-gated).
↓ fix.diffX86_64_GENERIC built with batch fix.diffs (gcc 8.3, -Werror, build rc=0) β€” not booted; no target HW to exercise either kernel.

Confirmed kernel references

Detail

Exploit chain

none β€” valid hard blocker (driver/device path dead at runtime on this guest: no target HBA / no malicious device / no removable media). No unprivileged->root path.

Evidence (decisive lines)

kldstat -> kernel/ehci/xhci only (no target driver); pciconf -l -> PIIX3 IDE + virtio only (no AHCI/NVMe/AdvanSys HBA); camcontrol devlist -> only <QEMU QEMU DVD-ROM>; ls /dev/<target> -> No such file. Source confirmed at cited lines.

PoC changes

Created findings/poc/DF-2217/{VERDICT.md,fix.diff,env.txt,build.sh,run.sh,manifest.json,fix_build.log}. No PoC source (HW-gated).

Verified recommended fix

Defense-in-depth fix.diff adds the validation/bounds check closing the cited path (see findings/poc/DF-2217/fix.diff; git apply --check OK).

Verdict

NOT REPRODUCED (HW/trigger-gated on this guest). The bug is REAL in source (traced line-by-line): nvme_ioctl NVMEIOCGETLOG missing privilege check (no NVMe controller, no /dev/nvme*). Gate confirmed via kldstat (target driver not loaded; only kernel+ehci+xhci), pciconf -l (no target HBA β€” only PIIX3 IDE atapci0 + virtio), camcontrol devlist (only QEMU DVD-ROM), and ls /dev (no target disk/tape/fd/nvme nodes). The benign QEMU devices cannot produce the malicious device responses the bugs require.