jamulussoftware / jamulussoftware/jamulus

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

Aperta
#3,868 5 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

AI bug Windows
Lingua principale
C
Stelle
1.1k
Fork
248
Merge medio
2g 3h
PR unite (30g)
9

Descrizione

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

Guida per i contributori

Apri la guida per i contributori

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Direzione di ricerca

Inizia in src/sound/asio/sound.cpp, in CSound::LoadAndInitializeDriver(), nello specifico nel ramo di errore di CheckDeviceCapabilities(). Confronta la relativa pulizia con ASIOExit() e con il ramo in cui ASIOInit() fallisce, usando la riproduzione con il driver sintetico descritta nell’issue. Il lavoro è completato quando il percorso di errore non lascia alcun puntatore theAsioDriver pendente e la riproduzione esegue zero chiamate sull’oggetto liberato.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
cpp
Ambito
audio-video-rtc
Tipo di issue
Bug
Difficoltà
2/5
Tempo stimato
1-3 ore
Stato di attività
Ferma
Chiarezza
Specificata chiaramente
Idoneità per principianti
35/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.