Unchecked SINF package index yields heap out-of-bounds read for short/malformed firmware packages
Summary
acpi_panasonic_sinf() indexes res->Package.Elements[index] without verifying index<res->Package.Count. Driver passes fixed indices up to HKEY_REG_SOUND_MUTE(0x08) assuming firmware SINF package always contains at least 9 elements. Buggy/hostile ACPI SINF method returning shorter package causes Elements[index] to read past end of ACPICA-allocated package buffer. Value read assigned to UINT64 ret propagated back through hkey_*_brightness -> acpi_panasonic_sysctl -> sysctl_handle_int to userspace via CTLFLAG_ANYBODY sysctl. Requires firmware defect or crafted ACPI table (VM with hand-built DSDT). Primarily robustness/firmware-trust hardening. [VERIFIED pass2: reproduced - Count=2 package returns exact canary UINT32s to unpriv userspace identifying the OOB index read, tight-allocation variant reads 0xEEEEEEEE from the chunk behind the package 5 of 5; index-lt-Count guard fix-validated]
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2387 Β· 17 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 1.8 KB | β raw | |
| VERDICT.md | β | 4.3 KB | β raw | |
| run.sh | β | 214 B | view raw | |
| run.log | β | 1.0 KB | view raw | |
| run_fixed.log | β | 943 B | view raw | |
| canary_run.txt | β | 53 B | view raw | |
| tight_run.txt | β | 160 B | view raw | |
| spray_run.txt | β | 160 B | view raw | |
| leak_sample.txt | β | 1.5 KB | view raw | |
| panasim.c | β | 19.4 KB | view raw | |
| panasim.h | β | 1.1 KB | view raw | |
| pana_shim.c | β | 2.6 KB | view raw | |
| build.sh | β | 88 B | view raw | |
| build.log | β | 510 B | view raw | |
| fix.diff | β | 1.4 KB | view raw | |
| env.txt | β | 1.9 KB | view raw | |
| verdict.json | β | 4.4 KB | view raw |
DF-2387 β PoC evidence pack
Finding: acpi_panasonic_sinf() indexes
res->Package.Elements[index] without checking
index < res->Package.Count
(sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:280). Registers
0x02..0x08 are read assuming the firmware SINF package always has
β₯ 9 elements; a short/malformed package makes the read run past what
the firmware declared, and the value flows back to userspace through
the CTLFLAG_ANYBODY sysctl (kernleak class).
Rig
Same verbatim-interposition kernel harness as DF-2386 (see that README;
panasim.c/pana_shim.c/build.sh are identical). The SINF reply is
runtime-selectable via hw.panasim.sinf_mode:
4= sane 9-element package (control),1= canary:Package.Count=2with a 9-element backing store where elements pastCounthold per-index canaries0xC2β¦,0xC3β¦,0xC4β¦,0xC8β¦β a returned canary proves the read ignored Count,2= tight:Count=2with only a 2-element allocation β the read lands past thekmallocand discloses adjacent kernel heap memory to the unprivileged reader.
Build
sh build.sh # shared with DF-2386 (baseline PANHARNESS kernel)
Run
sh run.sh # canary proof + tight-mode leak samples (maxx reads)
Expected result (baseline)
- canary mode:
maxxreads return exactly the per-register canary (reg2β0xC2C2C2C2,reg3β0xC3β¦,reg4β0xC4β¦,reg8β0xC8β¦, truncated to UINT32) β the value that reaches userspace identifies the exact out-of-package index that was read; - tight mode: non-deterministic adjacent-heap values (leak samples in
tight_run.txt), no panic (reads stay inside mapped slab memory); - fixed build: every short-package read returns
-1(index guard).
DF-2387 β PoC verdict
File: sys/dev/acpica/acpi_panasonic/acpi_panasonic.c (:280)
Verdict: REPRODUCED β acpi_panasonic_sinf() indexes
res->Package.Elements[index] with fixed register indices (0x02..0x08)
without ever checking index < res->Package.Count; with a short
firmware SINF package the verbatim driver (a) reads exactly the
out-of-package element and (b) hands the value to the unprivileged
reader through the CTLFLAG_ANYBODY sysctl. Two independent
attributions on the verbatim code: per-index canaries (read ignores
Count) and a cross-chunk heap read past the allocation into groomed
neighbor content. Fixed build returns -1 for every short package
(A/B validated).
Line-accurate confirmation
acpi_panasonic.c:279-280β the only guard is a type check:if (res->Type == ACPI_TYPE_PACKAGE) ret = res->Package.Elements[index].Integer.Value;βres->Package.Countis never consulted.- Callers pass fixed indices:
hkey_lcd_brightness_max:316 (reg 0x02/0x05),hkey_lcd_brightness_min:337 (0x03/0x06),hkey_lcd_brightness:363 (0x04/0x07),hkey_sound_mute:382 (0x08) β the driver hard-assumes β₯ 9 elements. - Value propagation:
*val = acpi_panasonic_sinf(...)(UINT32 truncation) βacpi_panasonic_sysctl:254sysctl_handle_intβ unprivileged userspace (CTLFLAG_ANYBODY, :171).
Mechanical verification
Same rig as DF-2386 (see that VERDICT). hw.panasim.sinf_mode
selects the fabricated SINF package:
- mode 4 (sane, 9 elements): maxx reads β
255 / 0 / 128 / 0(exactly the planted element values) β control. - mode 1 (canary):
Package.Count = 2, 9-element backing store, elements past Count hold per-index canaries0xC2..C8(repeated per byte). Unprivileged reads returned exactly the canary of each register index:
reg2_max: -1027423550 == (int32)0xC2C2C2C2 reg3_min: -1010580541 == (int32)0xC3C3C3C3 reg4_brt: -993737532 == (int32)0xC4C4C4C4 reg8_mute: -926365496 == (int32)0xC8C8C8C8
The value that reached uid 1001 identifies the precise element index
that was read β the driver read Elements[2],[3],[4],[8] of a
package declaring two elements, 5/5 stable across runs.
- mode 3 (tight + same-zone spray): the package is a tight
3Γ
sizeof(ACPI_OBJECT)= 72-byte kmalloc (external ACPI_OBJECT is 24 bytes, measured by the harness at boot); six same-size chunks marked0xEEare allocated directly behind it (s[2]/s[4] freed). Unprivileged reads returned-286331154 == 0xEEEEEEEEfor both reg4 (package+120 = 48 bytes past the chunk) and reg8 (package+216), 5/5 runs β the read crosses into the adjacent kernel heap chunk (leak_sample.txt). - mode 2 (tight, no grooming) returned 0s β adjacent chunks happened to be zeroed fresh slab; recorded as the un-groomed observation.
- No panic in any mode: the reads stay inside mapped slab memory, so on the stock kernel this is a silent disclosure, matching the Low/C:L severity.
Reachability / privesc assessment (honest)
- Requires Panasonic
MAT0019firmware AND a buggy/hostile SINF method returning a short package (finding's own precondition: "firmware defect or crafted ACPI table"). The read itself is unprivileged (ANYBODY sysctl) and silent. - Impact ceiling: kernel heap contents adjacent to a 72-byte kmalloc disclosed to an unprivileged user, one UINT32 per read, no crash. Grooming from unprivileged userspace is indirect (the allocation happens in the reader's syscall context, but kernel-side neighbors are not attacker-shaped without additional primitives). Not a privesc; Low/kernleak hardening confirmed.
Fix validation (A/B)
Fixed kernel (#6): identical canary/tight/spray sequences β every
read returns -1 (index β₯ Count guard), 15/15 reads across all modes
(run_fixed.log), no panic, guest up.
Artifacts
run.log/run_fixed.log, canary_run.txt/tight_run.txt/
spray_run.txt (leak samples), rig sources, build.sh/run.sh,
build.log, fix.diff, env.txt.
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_testableConfirmed kernel references
- sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:279-280
- sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:316
- sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:337
- sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:363
- sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:382
- sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:254
- sys/dev/acpica/acpi_panasonic/acpi_panasonic.c:171
Detail
Exploit chain
uid 1001 maxx -> sysctl read hw.acpi.panasonic.{lcd_brightness_max,lcd_brightness_min,lcd_brightness,sound_mute} (CTLFLAG_ANYBODY) -> acpi_panasonic_sinf(reg) -> Elements[reg].Integer.Value read without Count check -> UINT32 truncation -> sysctl_handle_int -> unprivileged userspace. Grooming demonstration is rig-side (kernel allocations adjacent to the package); an unprivileged user cannot directly shape the neighboring chunks, so the realistic ceiling is disclosure of incidental adjacent heap contents, matching Low severity.
Evidence (decisive lines)
canary_run.txt (reg2/3/4/8 returned 0xC2/C3/C4/C8C2.. canaries); spray_run.txt + leak_sample.txt (5/5 reads of 0xEEEEEEEE from the chunk behind the 72-byte package allocation); tight_run.txt (ungroomed zeros); run.log (sane control 255/0/128/0); run_fixed.log (all modes -> -1); panasim.c (SINF_CANARY/SINF_TIGHT/SINF_TIGHT_SPRAY modes)
PoC changes
No seed PoC existed; reused the DF-2386 verbatim-interposition rig unchanged and added SINF reply modes: SINF_CANARY (Count=2 with per-index canary tail for attribution), SINF_TIGHT (Count=2 with a tight 3*sizeof(ACPI_OBJECT)=72-byte allocation) and SINF_TIGHT_SPRAY (same-size marked neighbors allocated directly behind the package -- the first spray attempt used 256-byte chunks, a different malloc zone than the 72-byte package, and returned zeros; measured sizeof(ACPI_OBJECT)=24 from the harness boot line and switched to exact same-size grooming).
Verified recommended fix
acpi_panasonic_sinf: only read Elements[index] after verifying res != NULL, res->Type == ACPI_TYPE_PACKAGE and index < res->Package.Count (part of the shared fix.diff with DF-2386)
Verdict
Confirmed on the verbatim driver in the same kernel rig as DF-2386: acpi_panasonic_sinf reads res->Package.Elements[index] (acpi_panasonic.c:280) with fixed register indices 0x02..0x08 and never consults res->Package.Count. With a fabricated short SINF package (Count=2), the unprivileged reader of the CTLFLAG_ANYBODY sysctls received exactly the per-index canary planted past Count (reg2 -> 0xC2C2C2C2, reg3 -> 0xC3C3C3C3, reg4 -> 0xC4C4C4C4, reg8 -> 0xC8C8C8C8; the returned value identifies the exact out-of-package index read), and with a tight 72-byte package allocation plus same-zone grooming the reads crossed into the adjacent kernel heap chunk and returned the planted 0xEEEEEEEE marker content 5/5 runs -- a cross-chunk kernel heap read disclosed to an unprivileged user with no crash. Reach requires Panasonic MAT0019 firmware whose SINF method returns a short/malformed package (firmware defect or crafted ACPI table, exactly the finding precondition); on conformant firmware the read is in-bounds. Ceiling is a bounded one-UINT32-per-read kernel heap disclosure; no write primitive, not a privesc. fix.diff (index < Count guard) A/B validated: every short-package read returns -1 on the fixed kernel (15/15 across all modes), no panic.
No comments yet.