Repository navigation
WASAPI Driver for ASIO 0.9.8
Pre-release
Pre-release
0.9.8 (changes from 0.9.7) - "Hardening"; no new user features
Requested by an external review (Windows audio / MIDI engineering): the driver
must withstand untrusted, buggy or malicious data from host applications and
from its dependencies without crashing or compromising the host process, be
covered by fuzz and unit tests that gate every release, and be observable
with TraceLogging. Valid calls and the audio engine behave as in 0.9.7.
Every finding is listed in CODE-REVIEW.txt ("0.9.8 hardening review",
H1-H32); the design is ARCHITECTURE.txt section 58.
Summary of all changes:
- Exception barrier (H1): every function the host or Windows calls (all
IASIO and IUnknown methods, the class factory and the four DLL exports,
every window procedure, hook and enumeration callback, the device and
volume notification callbacks, every thread procedure, the destructor and
the panel's message loop) catches any C++ exception and returns that
boundary's error (ASE_HWMalfunction / ASE_NoMemory, ASIOFalse, E_FAIL /
E_OUTOFMEMORY, DefWindowProc, ...). Contained exceptions are logged (first
16) and traced. The real-time path allocates nothing; the audio thread is
guarded at its thread procedure only (no cost). - Host argument validation (H3, H4, H13-H17): new src/asiocheck.h (pure,
tested offline) with the call-order rules (Created / Initialized /
Prepared / Running) and every argument rule: null pointers, channel
indices, isInput values, numChannels <= 0 or above the channel count,
duplicate channels, buffer sizes outside 32..4096, missing callbacks,
NaN / infinite / negative / non-standard sample rates, clock sources,
future() selectors with a null or wrong argument. Host structures are read
once and copied (the callbacks too), so changing them concurrently cannot
bypass a check. createBuffers checks everything before it disposes the
previous buffers. Return codes that changed: start() before init and
queries before init -> ASE_NotPresent; setSampleRate / controlPanel from
inside a callback -> ASE_InvalidMode; QueryInterface with a null pointer
-> E_POINTER. disposeBuffers (in any state) and setClockSource(0) keep
answering ASE_OK as in 0.9.7. The test tool's private engine-information
selector now needs a header (a 0.9.7 test tool shows no engine line with
this driver). - Use after free (H2, H19): the device and volume notification objects
detach under a lock, so the driver can be destroyed while Windows delivers
a notification; a release on any thread marks the object as dying. - Untrusted device data (H5-H7, H10): device mix formats, periods,
latencies and buffer sizes are range-checked (no write past a frame, no
undefined float-to-integer conversion, no size overflow on the 32-bit
build); at most 256 capture packets are read per wake-up. - No unbounded waits (H8, H9): start() waits at most 10 s for an exiting
audio thread (then handles it as stuck, like stop()); the audio thread
ends with a reset request after 10 s without a render event while the
device position stays frozen (device stalled without a notification; a
device whose position still moves, e.g. right after a resume from Modern
Standby, keeps the stream) and on a failed wait. Every remaining
wait is listed with its bound in ARCHITECTURE.txt 58.4. - config.ini and other parsers (H11, H12, H23-H25): the value parser,
normalization, UTF-16 decoding, profile lists, host profiles and OutputMap
moved to pure headers (src/parse.h, src/cfgtypes.h) and are fuzzed;
fixed an integer overflow (DirectMonitorInput / Output = -2147483648) and
undefined swscanf overflow in OutputMap; temporary file names include the
thread id; the profile-API lists are walked within the reported length. - Data races removed (H18): getSamplePosition's fallback values, the
callback counter and the init flag are atomics. - TraceLogging (new): the DLL is the ETW provider "WASAPIDriverForASIO"
{52b9f111-957d-5973-b76d-97bdf8f813df} (name-hash GUID, never a Microsoft
name or GUID). Events: driver load / unload / create / destroy, init,
every IASIO call outcome (non-real-time threads), device resolution,
stream opening (mode, rates, period, buffer, format, conversion), resets
and reasons, device notifications, errors with HRESULT, contained
exceptions, a 10 s statistics summary (counters only, from the host
thread), crash protection, panel and update-check results. Never audio
data, paths, user or computer names. Disabled tracing costs one atomic
read per event. Encoder: src/tlg.h (Microsoft's TraceLoggingDynamic.h
does not compile with mingw-w64, see ARCHITECTURE.txt 58.7). - Tracing kit: trace/WasapiDriver.wprp (own EventCollector and buffer budget
for the driver at level 5; Microsoft-Windows-Audio at level 4 in a
separate collector; memory and file profiles), "Start trace.bat" (color
B1) and "Stop trace.bat" (color E1), and TRACING.txt (how to record,
what is and is not recorded, how to attach it to a GitHub issue). The
Setup installs them in "Tracing" with Start menu shortcuts; the release
folder has them in Tools. PRIVACY.txt describes tracing. - Tests: tests/run_all.sh runs all 18 offline tests; new fuzz_test (180000
deterministic iterations over every parser of untrusted data),
asiocheck_test (validation rules, 200000 random host calls, read-once
property) and trace_test (SHA-1, name-hash GUID, exact event bytes), all
with AddressSanitizer + UndefinedBehaviorSanitizer + libstdc++
assertions. WasapiDriverTest.exe: new T11 "Bad host calls" (invalid
arguments and wrong order against the real driver, no audio; runs right
after T1 so every later test shows the driver survived). - CI: build.yml runs tests/run_all.sh; release.yml has a "tests" job that
every other job needs, so nothing is built, signed or published unless all
tests passed. - Dead code removed (H32): unused interface strings S_ABOUT and
S_STATUS_TITLE (5 languages), WasapiBridge::recommendedBuffer, the
never-read member monWas, a duplicated null check in getClockSources,
KnownRate and the swscanf OutputMap loop (replaced); duplicated helpers
now exist once in parse.h. - Portable install.bat / uninstall.bat colors 4A / 8B.
- Independent review of 0.9.8 (CODE-REVIEW.txt R1-R7, all fixed):
getErrorMessage copies only the message and its terminator again;
disposeBuffers and setClockSource(0) are lenient as in 0.9.7; the stall
detection checks that the device position is frozen; install.bat,
uninstall.bat, "Start trace.bat" and "Stop trace.bat" check administrator
rights with fltmc, relaunch themselves elevated only once (marker
argument) and the trace helpers treat any non-zero wpr result (HRESULTs
are negative) as a failure; "Stop trace.bat" saves to the signed-in user's
Desktop (decided before elevation) and prints the full path; the update
worker's trace event is inside its exception barrier; comment fixes.