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)
PoC verification
Evidence pack
findings/poc/DF-5481 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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.
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->ptrstraight 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_testableNo fix validation possible on this rig (ioctl path returns ENXIO on BIOS boot).
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.
No comments yet.