UAF: Global Static Jack GPIO data Pointer in tegra_asoc_machine.c

Summary

A use-after-free vulnerability exists in the Tegra ASoC machine driver due to storing a per-device machine pointer in a module-level static snd_soc_jack_gpio structure. The pointer can be dereferenced via a GPIO interrupt handler after the device-managed memory has been freed.

Vulnerability Details

File: sound/soc/tegra/tegra_asoc_machine.c

Root Cause

Three jack GPIO structures are declared as module-level (file-scope) statics:

// lines 31–35
static struct snd_soc_jack_gpio tegra_machine_hp_jack_gpio = {  };
// lines 46–50
static struct snd_soc_jack_gpio tegra_machine_headset_jack_gpio = {  };
// lines 72–76
static struct snd_soc_jack_gpio tegra_machine_mic_jack_gpio = {  };

During card initialization (tegra_asoc_machine_init, line 139), the per-device machine pointer — allocated with devm_kzalloc — is written into the global structure:

// line 202
tegra_machine_mic_jack_gpio.data = machine;
// line 203
tegra_machine_mic_jack_gpio.desc = machine->gpiod_mic_det;

The .data field is then consumed by the GPIO interrupt callback:

// lines 53–63
static int coupled_mic_hp_check(void *data)
{
    struct tegra_machine *machine = (struct tegra_machine *)data;

    if (gpiod_get_value_cansleep(machine->gpiod_hp_det) &&   // UAF dereference
        gpiod_get_value_cansleep(machine->gpiod_mic_det))
        return SND_JACK_MICROPHONE;
    return 0;
}

Use-After-Free Path

  1. Device probes: devm_kzalloc allocates machine; tegra_machine_mic_jack_gpio.data = machine.
  2. snd_soc_jack_add_gpios (line 211) registers a GPIO IRQ that calls coupled_mic_hp_check.
  3. Device is unbound (e.g., driver module removed, or hotplug event).
  4. devm cleanup runs in LIFO order: devm_snd_soc_register_card fires first, beginning card teardown and jack GPIO IRQ removal. However, the IRQ is not guaranteed to be fully quiesced before the devm_kzalloc-managed machine block is freed.
  5. A concurrent or late-arriving GPIO interrupt invokes coupled_mic_hp_check(data) where data points to the now-freed machine struct → use-after-free.

Second UAF Path: Multiple Device Instances

Because all global statics are shared across all driver instances:

  1. Device A probes: tegra_machine_mic_jack_gpio.data = machine_A.
  2. Device B probes: tegra_machine_mic_jack_gpio.data = machine_B — silently overwrites.
  3. Device B is unbound: machine_B freed by devm.
  4. Device A’s GPIO interrupt fires → coupled_mic_hp_check uses freed machine_B → UAF.

Neither instance is aware of the other; the shared global creates an implicit cross-device lifetime dependency with no synchronization.

Impact

  • Type: Use-After-Free (CWE-416) in kernel interrupt context
  • Severity: High — kernel memory corruption from interrupt context; exploitable for privilege escalation (ring 3 → ring 0) by a local unprivileged user who can trigger GPIO events (e.g., via a malicious peripheral or by scripting headphone insertion/removal while racing device unbind)
  • Affected subsystem: ALSA/ASoC Tegra machine driver; impacts NVIDIA Tegra-based boards (Chromebooks, Jetson, Shield) running affected kernels

Relevant Lines

Line Code
24–76 Module-level static jack and jack-GPIO structures
53–63 coupled_mic_hp_check — dereferences .data as freed machine *
202–203 tegra_machine_mic_jack_gpio.data = machine — stores per-device ptr in global
163, 183 Similar pattern for hp_jack_gpio and headset_jack_gpio
436 machine = devm_kzalloc(...) — lifetime tied to device, not module

Fix

Replace the module-level static snd_soc_jack_gpio instances with per-device allocations (e.g., embed them in struct tegra_machine), so their lifetime matches the machine struct they reference. Ensure IRQ teardown is fully completed (e.g., via synchronize_irq) before the machine allocation is freed.