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

EFIIOC_GET_TABLE hands a kernel DMAP virtual address straight back to userspace: kernel-VA disclosure (root-only) and an ioctl ABI that can never work

Summary

The table location is returned as PHYS_TO_DMAP(ct_data) and copied verbatim to the caller: kernel-VA disclosure through the root-only 0700 /dev/efi AND a useless ABI (the DMAP is not mapped in any user pmap - FreeBSD later reworked it to copy contents). Mechanic demonstrated kernel-side by the DF-5477 harness. Copy-out-contents fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5481 Β· 4 files
FileTypeDescriptionSize
README.md β€” 1.6 KB ↓ raw
VERDICT.md β€” 2.1 KB ↓ raw
verdict.json β€” 2.3 KB view raw
manifest.json β€” 459 B view raw

DF-5481 β€” EFIIOC_GET_TABLE hands a kernel DMAP virtual address straight back to userspace: kernel-VA disclosure (root-only) and a useless ioctl ABI (userspace cannot dereference a DMAP address).

FILE: sys/platform/pc64/x86_64/efirt.c (+ the efidev ioctl ABI)

ROOT CAUSE (path:line): * efirt.c:481: *ptr = (void *)PHYS_TO_DMAP(ct->ct_data); β€” the table location is returned as a KERNEL direct-map VA (e.g. 0xffffc5..., DMAP base 0xffffb80000000000 on this build β€” demonstrated empirically by the DF-5477 harness, run.log). * sys/dev/misc/efirt/efidev.c:77-84: EFIIOC_GET_TABLE copies the efi_get_table_ioc struct (uuid in, ptr out) back to the caller β€” the kernel VA goes to userspace verbatim. * Two defects in one: (1) kernel-address disclosure to the (root-only, 0700 root:wheel β€” efidev.c:205-206) caller, a KASLR-adjacent leak of the fixed DMAP base relationship; (2) the returned pointer is unusable in userspace (DMAP is kernel-only VA), so the ioctl can never have worked as an API on this port β€” upstream FreeBSD later reworked the ABI to copy table contents instead.

SEVERITY: Info β€” root-gated, fixed-offset DMAP (little entropy value on this platform), no privilege boundary crossed. Filed for completeness of the /dev/efi surface review.

PACK: analysis-only; the disclosure mechanic itself was demonstrated kernel-side by the DF-5477 harness (see findings/poc/DF-5477/run.log: "returned kernel DMAP pointer 0xffffc5eadbeef000"); end-to-end ioctl delivery is not testable on this BIOS-boot rig (efi_get_table returns ENXIO). status inconclusive by rig limit.

VERDICT.md
↓ download raw

DF-5481 VERDICT β€” EFIIOC_GET_TABLE returns a kernel DMAP VA to userspace

Status: inconclusive (end-to-end ioctl delivery needs an EFI boot; the kernel-side mechanic was demonstrated by the DF-5477 harness). Impact class: leak, but root-only and of near-zero entropy value β€” severity Info.

Trace

  • efi_get_table() sets *ptr = (void *)PHYS_TO_DMAP(ct->ct_data) (efirt.c:481) β€” a high-canonical kernel virtual address (DMAP base 0xffffb80000000000 on this build, verified empirically).
  • efidev_ioctl() EFIIOC_GET_TABLE (efidev.c:77-84) passes &egtioc->ptr straight into efi_get_table and the ioctl framework copies the struct back β€” the kernel VA lands in the caller's buffer.
  • /dev/efi is 0700 root:wheel (efidev.c:205-206), so the recipient is root β€” no privilege boundary is crossed; on this platform the DMAP base is a build constant, so beyond confirming it the leak carries little information.
  • Equally importantly, the pointer is USELESS to userspace: the DMAP is not mapped in any user pmap. The ioctl ABI cannot ever have worked as written β€” consumers need the table contents, not its KVA (FreeBSD later changed this ABI to copy the table data).

Evidence linkage

The DF-5477 KLD harness (verbatim efi_get_table copy) demonstrated the return of 0xffffc5eadbeef000 for ct_data = 0x00DEADBEEF000 (findings/poc/DF-5477/run.log). On this BIOS-boot guest the real ioctl path returns ENXIO (efi_cfgtbl NULL), so the userspace delivery leg is not testable here.

Fix direction

Return the table CONTENTS (bounded copyout of the requested table, or a data-handle ABI) rather than a kernel pointer; at minimum do not propagate kernel VAs through ioctl structs.

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.

(No fix.diff for this item: documented recommendation only - see Fix above.)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

No fix validation possible on this rig (ioctl path returns ENXIO on BIOS boot).

per-fix-DF-5481

Confirmed kernel references

Detail

Evidence (decisive lines)

findings/poc/DF-5477/run.log: 'DF-5477A: FOUND entry 199 past table end -> returned kernel DMAP pointer 0xffffc5eadbeef000'; efirt.c:481; efidev.c:77-84, efidev.c:205-206.

PoC changes

No dedicated PoC: mechanic covered by the DF-5477 harness; the ioctl delivery leg needs an EFI boot.

Verified recommended fix

Change EFIIOC_GET_TABLE to copy out the table contents (bounded) instead of returning a kernel DMAP pointer.

Verdict

efi_get_table() returns the table location as a KERNEL direct-map virtual address (efirt.c:481, *ptr = PHYS_TO_DMAP(ct->ct_data)) and the EFIIOC_GET_TABLE ioctl (efidev.c:77-84) copies that pointer verbatim back to userspace β€” a kernel-VA disclosure through the root-only /dev/efi (0700 root:wheel, efidev.c:205-206), and an ioctl that can never work as an API since the DMAP is not mapped in any user pmap. The kernel-side mechanic was demonstrated on the guest by the DF-5477 harness (verbatim function copy): ct_data 0x00DEADBEEF000 returned as kernel pointer 0xffffc5eadbeef000 (DMAP base 0xffffb80000000000, empirical). The end-to-end userspace delivery leg is not testable on this BIOS-boot rig (efi_get_table returns ENXIO β€” efi_cfgtbl NULL), hence inconclusive. Root-gated, fixed-offset DMAP, no privilege boundary crossed: Info severity, filed for completeness of the /dev/efi surface.