jamulussoftware / jamulussoftware/jamulus
ASIO driver use-after-free when a device fails the capability check
- Dominant language
- C
- Stars
- 1.1k
- Forks
- 248
- Avg merge
- 2d 3h
- Merged PRs (30d)
- 9
Description
## Summary
**Use-after-free on the ASIO SDK's global `theAsioDriver` pointer when a device
fails the capability check.** On current `main`, a device that fails
`CheckDeviceCapabilities()` during ASIO re-init can leave a dangling global
pointer that a later re-init dereferences. Symptoms are inconsistent — hang,
crash, or silent corruption — which matches the reports in #872 and #305.
## Context
- #872 (closed 2021) reported the "process doesn't get killed / ASIO4ALL red
cross lockout" symptom; the root cause was not identified then.
- Discovered as part of the #3779 investigation (Windows hang with ASIO4ALL),
in which #872's defect is one of several distinct problems.
## Evidence (mcfnord, on Windows — issue #3779 comment 5224437188)
One honest caveat up front: this measurement was done with a **synthetic test
driver** that deliberately fails the capability check, not with ASIO4ALL on real
hardware. With that said:
- This is a **use-after-free, not a leak**: `ASIOExit()` is effectively
`removeCurrentDriver()` + `theAsioDriver = 0`. The current failure branch
only does the first of those.
- Outcome is driver-dependent: **ASIO4ALL's** driver object is static in its
DLL and survives the COM release (→ hang / lockout); **Focusrite USB
ASIO's** object is heap-allocated, so the next call faults (`0xC0000005`).
- With the synthetic driver, the stock build (`4d142945`) made **seven ASIO
calls on the freed object**; with `ASIOExit()`, **zero**.
## How to reproduce
The reliable repro we have is with a synthetic driver that fails
`CheckDeviceCapabilities()` on demand. On real Windows hardware with ASIO4ALL
this is our best guess at hitting the same path — it is **unverified**:
1. Select a device that fails the capability check (e.g. one that does not
support the required sample rate) while a valid device is also available.
2. Trigger re-init (e.g. change the audio device in the client settings).
3. Re-select the previous working device.
The failure branch is hit at `CSound::LoadAndInitializeDriver()`
(src/sound/asio/sound.cpp) — `CheckDeviceCapabilities()` fails, the driver is
released via `asioDrivers->removeCurrentDriver()`, and the global
`theAsioDriver` pointer is left dangling. A subsequent re-init dereferences it.
## Proposed fix
In `CSound::LoadAndInitializeDriver()` (src/sound/asio/sound.cpp), the
`CheckDeviceCapabilities()` failure branch should call **`ASIOExit()`** instead
of `asioDrivers->removeCurrentDriver()`, so the global pointer is nulled. The
`ASIOInit()`-failed branch is left alone — from reading the ASIO SDK it resets
the global pointer on that path, though I haven't instrumented it to be sure.
A ready patch exists in fork PR `ann0see/jamulus#286` (one-line change + comment),
reproducing the 7-vs-0 result. This issue is opened to track the defect so the
fix is not lost if the fork PR is never reopened upstream.
## Requested actions
- Review whether the failure branch should use `ASIOExit()`.
- If agreed, apply the fix on `main` (a small, low-risk change).
## ⚠️ AI-generated issue — please verify
- This text is AI generated, may be wrong, and may contain inaccuracies.
- The hardware verification described below was performed by a human with an AI agent
(mcfnord, on Windows).
Contributor guide
Research direction
Start in src/sound/asio/sound.cpp at CSound::LoadAndInitializeDriver(), specifically the CheckDeviceCapabilities() failure branch. Compare its cleanup with ASIOExit() and the ASIOInit()-failed branch, using the synthetic driver reproduction described in the issue. Done means the failure path leaves no dangling theAsioDriver pointer and the reproduction makes zero calls on the freed object.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- audio-video-rtc
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 35/100