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

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2387 Β· 17 files
FileTypeDescriptionSize
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=2 with a 9-element backing store where elements past Count hold per-index canaries 0xC2…, 0xC3…, 0xC4…, 0xC8… β€” a returned canary proves the read ignored Count,
  • 2 = tight: Count=2 with only a 2-element allocation β€” the read lands past the kmalloc and 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: maxx reads 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).
VERDICT.md
↓ download raw

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

  1. 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.Count is never consulted.
  2. 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.
  3. Value propagation: *val = acpi_panasonic_sinf(...) (UINT32 truncation) β†’ acpi_panasonic_sysctl :254 sysctl_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 canaries 0xC2..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 marked 0xEE are allocated directly behind it (s[2]/s[4] freed). Unprivileged reads returned -286331154 == 0xEEEEEEEE for 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 MAT0019 firmware 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_testable
↓ fix.diffper-fix-DF-2387

Confirmed kernel references

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.