# 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.
