From f1fc19827885ebc18c784eeff658e382c9df7d3b Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Mon, 2 Oct 2023 20:07:42 -0400
Subject: [PATCH] audio: Destroy the logical audio device before sending
DEVICE_REMOVED event.
This prevents catastrophe if someone tries to close the device in an event
filter in response to the event.
Note that this means SDL_GetAudioStreamDevice() for any stream on this
device will return 0 during the event filter!
Fixes #8331.
---
src/audio/SDL_audio.c | 31 +++++++++++++++++++------------
src/audio/SDL_audiocvt.c | 6 ++++--
2 files changed, 23 insertions(+), 14 deletions(-)
diff --git a/src/audio/SDL_audio.c b/src/audio/SDL_audio.c
index 9cb16d00a476..078e3793c53f 100644
--- a/src/audio/SDL_audio.c
+++ b/src/audio/SDL_audio.c
@@ -398,8 +398,8 @@ SDL_AudioDevice *SDL_AddAudioDevice(const SDL_bool iscapture, const char *name,
// Post the event, if desired
if (SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_ADDED)) {
SDL_Event event;
+ SDL_zero(event);
event.type = SDL_EVENT_AUDIO_DEVICE_ADDED;
- event.common.timestamp = 0;
event.adevice.which = device->instance_id;
event.adevice.iscapture = iscapture;
SDL_PushEvent(&event);
@@ -412,17 +412,20 @@ SDL_AudioDevice *SDL_AddAudioDevice(const SDL_bool iscapture, const char *name,
// this _also_ destroys the logical device!
static void DisconnectLogicalAudioDevice(SDL_LogicalAudioDevice *logdev)
{
+ SDL_assert(logdev != NULL); // currently, this is always true.
+
+ const SDL_AudioDeviceID instance_id = logdev->instance_id;
+ const SDL_bool iscapture = logdev->physical_device->iscapture;
+ DestroyLogicalAudioDevice(logdev);
+
if (SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_REMOVED)) {
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_AUDIO_DEVICE_REMOVED;
- event.common.timestamp = 0;
- event.adevice.which = logdev->instance_id;
- event.adevice.iscapture = logdev->physical_device->iscapture ? 1 : 0;
+ event.adevice.which = instance_id;
+ event.adevice.iscapture = iscapture ? 1 : 0;
SDL_PushEvent(&event);
}
-
- DestroyLogicalAudioDevice(logdev);
}
// Called when a device is removed from the system, or it fails unexpectedly, from any thread, possibly even the audio device's thread.
@@ -495,22 +498,26 @@ void SDL_AudioDeviceDisconnected(SDL_AudioDevice *device)
// if there's an audio thread, don't free until thread is terminating, otherwise free stuff now.
const SDL_bool should_destroy = SDL_AtomicGet(&device->thread_alive) ? SDL_FALSE : SDL_TRUE;
+
+ const SDL_AudioDeviceID instance_id = device->instance_id;
+ const SDL_bool iscapture = device->iscapture;
+
SDL_UnlockMutex(device->lock);
+ if (should_destroy) {
+ DestroyPhysicalAudioDevice(device);
+ }
+
// Post the event, if we haven't tried to before and if it's desired
if (was_live && SDL_EventEnabled(SDL_EVENT_AUDIO_DEVICE_REMOVED)) {
SDL_Event event;
SDL_zero(event);
event.type = SDL_EVENT_AUDIO_DEVICE_REMOVED;
event.common.timestamp = 0;
- event.adevice.which = device->instance_id;
- event.adevice.iscapture = device->iscapture ? 1 : 0;
+ event.adevice.which = instance_id;
+ event.adevice.iscapture = iscapture ? 1 : 0;
SDL_PushEvent(&event);
}
-
- if (should_destroy) {
- DestroyPhysicalAudioDevice(device);
- }
}
diff --git a/src/audio/SDL_audiocvt.c b/src/audio/SDL_audiocvt.c
index 6e820ba60f89..b2f74f99febc 100644
--- a/src/audio/SDL_audiocvt.c
+++ b/src/audio/SDL_audiocvt.c
@@ -1167,8 +1167,10 @@ void SDL_DestroyAudioStream(SDL_AudioStream *stream)
const SDL_bool simplified = stream->simplified;
if (simplified) {
- SDL_assert(stream->bound_device->simplified);
- SDL_CloseAudioDevice(stream->bound_device->instance_id); // this will unbind the stream.
+ if (stream->bound_device) {
+ SDL_assert(stream->bound_device->simplified);
+ SDL_CloseAudioDevice(stream->bound_device->instance_id); // this will unbind the stream.
+ }
} else {
SDL_UnbindAudioStream(stream);
}