jamulussoftware / jamulussoftware/jamulus

ASIO driver use-after-free when a device fails the capability check

Offen
#3,868 5 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

AI bug Windows
Vorherrschende Sprache
C
Sterne
1.1k
Forks
248
Ø Merge
2 T. 3 Std.
Gemergte PRs (30 T.)
9

Beschreibung

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

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginne in src/sound/asio/sound.cpp bei CSound::LoadAndInitializeDriver(), genauer im Fehlerzweig von CheckDeviceCapabilities(). Vergleiche dessen Bereinigung mit ASIOExit() und dem fehlgeschlagenen ASIOInit()-Zweig und verwende dabei die im Issue beschriebene Reproduktion mit dem synthetischen Treiber. Als erledigt gilt die Aufgabe, wenn der Fehlerpfad keinen verwaisten theAsioDriver-Zeiger hinterlässt und die Reproduktion null Aufrufe auf dem freigegebenen Objekt ausführt.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
cpp
Bereich
audio-video-rtc
Issue-Typ
Bug
Schwierigkeit
2/5
Geschätzter Aufwand
1-3 Stunden
Aktivitätsstatus
Veraltet
Klarheit
Klar beschrieben
Anfängerfreundlichkeit
35/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.