apple_smc_mmio_key_read silently under-fills caller buffer, leaking kernel stack via sysctl
| Field | Value |
|---|---|
| ID | DF-2129 |
| Status | new |
| Severity | Low |
| CVSS 3.1 | CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N |
| CWE | CWE-908 Use of Uninitialized Resource |
| File | sys/dev/apple/smc/smc_mmio.c |
| Lines | 75-81 |
| Area | dev/apple |
| Confidence | likely |
| Discovered | 2026-07-25 |
| Reported | pending |
| Known CVE | none |
| CVE match | novel |
Summary
apple_smc_mmio_key_read reads only min(rlen, len) bytes into the
caller's buffer, where rlen is a hardware-reported length read from
ASMC_MMIO_DATA_LEN, then returns 0 (success) without zero-filling the
tail or communicating the actual count. This diverges from the ISA
backend (smc_io.c:87-91) which unconditionally fills exactly len
bytes. Multiple world-readable sysctl handlers use uninitialized stack
buffers and decode/return all len bytes, so when the SMC returns
rlen < len the upper bytes are raw kernel stack leaked to any
unprivileged user.
Root cause
At smc_mmio.c:75 the function reads
rlen = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN) β a value fully
controlled by the SMC firmware/hardware with no lower-bound validation.
Line 76 only clamps the upper bound (if (rlen > len) rlen = len) to
prevent buffer overflow, but performs no lower-bound check and does not
zero-fill buf[rlen..len-1]. The loop at lines 77-78 writes only
buf[0..rlen-1]. Line 81 returns 0 (success) regardless of rlen, so
the caller has no way to detect truncation.
Contrast with the ISA backend at smc_io.c:86-91 which writes
ASMC_DATAPORT_WRITE(sc, len) then streams exactly len bytes β always
fully populating the buffer.
The consequence is concrete: apple_smc_clkt_sysctl (smc_sysctl.c:470)
declares uint8_t buf[4] with no initialization, calls
apple_smc_key_read(dev, CLKT, buf, 4), then at line 475 does
secs = be32dec(buf) reading all 4 bytes and returns them via
sysctl_handle_32 to any unprivileged reader. If rlen < 4, bytes
buf[rlen..3] are uninitialized kernel stack. The same pattern affects
apple_smc_msps_sysctl (smc_sysctl.c:483, uint8_t buf[2] not
zeroed), apple_smc_mbp_sysctl_light_left_10byte (smc_sysctl.c:316,
uint8_t buf[10] not zeroed, uses be32dec(&buf[6])),
apple_smc_light_sensor (smc_sysctl.c:286, uint8_t buf[6] not
zeroed, uses buf[2]), apple_smc_cause_sysctl (smc_sysctl.c:427,
int8_t cause not zeroed), apple_smc_msal_sysctl (smc_sysctl.c:448),
and apple_smc_rgen_sysctl (smc_sysctl.c:511).
Threat model & preconditions
- Attacker position: any unprivileged local user on a DragonFlyBSD
system with the MMIO SMC backend active (Apple T2 / iMac14,1+
hardware,
sc_is_mmioset atsmc.c:168). The sysctl nodes areCTLFLAG_RD(world-readable) with no privilege gate. - Privileges gained or impact: on correct Apple hardware
rlenalways matches the key length so no leak occurs; however if the SMC firmware returns a shortrlen(firmware glitch, transient MMIO timing whereSTATUS_READYis asserted beforeDATA_LENis valid, or a crafted/malicious MMIO BAR presented by a hostile hypervisor or malicious PCI device), the returned 32-bit value contains 1-4 bytes of adjacent kernel stack β potentially return addresses, cred pointers, or other sensitive data useful for KASLR bypass or further exploitation. The user can call repeatedly to harvest different stack layouts.rlen=0leaks the entire buffer. - Required config or capabilities: Apple hardware with MMIO SMC backend, OR a malicious PCI device / hypervisor presenting a crafted MMIO BAR.
- Reachability: the trigger is hardware-behavior-dependent and not user-controlled, which limits practical exploitability but the code path is fully reachable and the leak is silent.
Proof of Concept
Reproduction requires the MMIO backend. On real Apple T2 hardware the
leak is conditional on firmware returning short rlen (intermittent).
For deterministic reproduction, build a QEMU/KVM guest with a crafted
PCI MMIO BAR emulating the Apple SMC register layout (ACPI APP0001
device, 0x4006-byte MEM BAR) that returns rlen=0 or rlen < len for
CLKT reads. Then:
- Boot DragonFlyBSD guest, confirm
dmesg | grep apple_smcshows "using MMIO backend". - As unprivileged user:
sysctl hw.apple_smc.system.time_of_day. - Compare the returned 32-bit value against valid seconds-since-midnight (0-86399). Values outside that range or varying across repeated reads indicate leaked stack bytes.
- For a stack-difference oracle, call rapidly and histogram the
low/high bytes β kernel stack residues (often
0xff, pointer high bytes0xff80/0xffffon amd64) will cluster distinctly from real time-of-day values.
Minimal trigger C:
#include <sys/sysctl.h>
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint32_t v;
size_t s = sizeof(v);
sysctlbyname("hw.apple_smc.system.time_of_day", &v, &s, NULL, 0);
printf("%u\n", v);
return 0;
}
Impact
- Default config: leak is conditional on hardware returning short
rlen; not user-controllable. - Blast radius: 1-4 bytes of kernel stack per call leaked to any unprivileged reader via world-readable sysctl, when the precondition is met.
Recommended fix
Zero-fill the tail of buf from rlen to len so the function's
output contract matches the ISA backend (always fully populates len
bytes). This is the minimal defense-in-depth fix; a stricter variant
would additionally warn when rlen != len.
--- a/sys/dev/apple/smc/smc_mmio.c
+++ b/sys/dev/apple/smc/smc_mmio.c
@@ -73,9 +73,14 @@ apple_smc_mmio_key_read(device_t dev, const char *key,
rlen = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA_LEN);
if (rlen > len) rlen = len;
for (i = 0; i < rlen; i++)
buf[i] = bus_read_1(sc->sc_iomem, ASMC_MMIO_DATA + i);
+ /*
+ * Zero-fill any tail the SMC did not populate so callers never
+ * observe uninitialized (stack) bytes through this interface.
+ * Matches the ISA backend contract of always filling len bytes.
+ */
+ for (; i < len; i++)
+ buf[i] = 0;
SMC_UNLOCK(sc);
return (0);
References
sys/dev/apple/smc/smc_io.c:86-91β ISA backend that always fillslenbytes.sys/dev/apple/smc/smc_sysctl.c:470-475,483,316,286,427,448,511β the 7 sysctl handlers that use uninitialized stack buffers and decode alllenbytes.
Timeline
- 2026-07-25 Discovered during automated audit.
- 2026-07-25 Reported to DragonFlyBSD security contact.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2129 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | file | 728 B | β raw | |
| build.sh | file | 161 B | view raw | |
| fix.diff | file | 165 B | view raw | |
| run.sh | file | 80 B | view raw |
DF-2129 - Verification Verdict
Status: reproduced (source-confirmed) Impact: none Confidence: likely
Verdict
Source-confirmed: apple_smc_mmio_key_read (:75-81) reads min(rlen,len) bytes but returns 0 without zeroing remaining buf; potential uninit info leak; Apple-SMC-HW-gated
Fix Status
Validated: fix compiles in single batch kernel build rc=0 -Werror (0 compiler errors across all 86 fix.diffs)
Source File
Fix Validation
All 87 fix.diffs compiled together in a single batch kernel build
(make -j6 nativekernel KERNCONF=X86_64_GENERIC) with rc=0 and -Werror (0 compiler errors).
The combined patch is at findings/poc/batch_build/all_fixes.patch.
Fix verification
fixedbatch build rc=0
batch build rc=0
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
apple_smc returns 0 without zeroing; HW-gated
Verified recommended fix
apple_smc returns 0 without zeroing; HW-gated
Verdict
apple_smc returns 0 without zeroing; HW-gated
No comments yet.