# DF-3794 — `ACPIIO_DO_MCALL` leaks the copied-in object list when `copyinstr` faults

* File: `sys/dev/acpica/acpi.c` (pass 2)
* Lines: 3341–3378 (leak at 3356–3357)
* Class: CWE-401 (missing release of memory on error path) / CWE-772
* Severity: Low (root + boot-tunable gated) · Confidence: certain (measured on guest)

## What

```c
params = (struct acpi_mcall_ioctl_arg *)addr;
args = acpi_copyin_object_list(&params->args);   /* kernel allocations */
if (!args)
    return EINVAL;
if (copyinstr(params->path, path, sizeof(path), NULL))
    return EINVAL;                               /* <-- `args` never freed */
```

`acpi_copyin_object_list()` allocates `sizeof(ACPI_OBJECT_LIST) +
Count*sizeof(ACPI_OBJECT)` bytes (plus per-STRING/BUFFER payloads) from
M_ACPICA. If the user-supplied `path` pointer faults on `copyinstr`, the
ioctl returns `EINVAL` without freeing `args`. Every failing call leaks
exactly that allocation set. There is no bound on repetitions.

Note: `ACPIIO_DO_MCALL` is reachable only with `FWRITE` on `/dev/acpi`
(root; mode 0644) *and* the boot tunable `debug.acpi.allow_method_calls=1`
— hence severity Low (robustness/DoS by a privileged-but-untrusted
process, e.g. a root daemon exposing the ioctl, and defense-in-depth for
the debugging path).

## Measured on the guest (DragonFly 6.5-DEVELOPMENT, tunable enabled)

```
vmstat -m | grep acpica
             acpica      46   6.39K       0    390M     138K   <- baseline
+100,000 faulted DO_MCALL (args: 2 INTEGER objects)
             acpica   97.7K   6.11M       0    390M     236K
+1,000,000 faulted DO_MCALL
             acpica   1.05M   67.1M       0    390M    1.18M
+100,000 faulted DO_MCALL (second session)
             acpica   1.14M   73.2M       0    390M    1.33M
+ 20,000 *valid* DO_MCALL (control, copyinstr succeeds -> args freed)
             acpica   1.14M   73.2M       0    390M    1.36M   <- InUse unchanged
```

64 bytes/call (16 + 2×24) as predicted; control run leaks nothing.
1.1M iterations took seconds ⇒ a privileged looping process can exhaust
kernel heap unboundedly.

## Reproduce

1. `debug.acpi.allow_method_calls=1` in `/boot/loader.conf.local`, reboot.
2. `cc -O -I/usr/src/sys -I/usr/src/sys/contrib/dev/acpica/source/include \
      -o mcallleak mcallleak.c`
3. `vmstat -m | grep acpica; ./mcallleak 100000; vmstat -m | grep acpica`
   (and `./mcallctrl 20000` for the no-leak control).

## Fix

See `fix.diff`: free `args` on the `copyinstr` failure path.
