Skip to content

Commit 0ca6469

Browse files
committed
Fix a segfault when get_callback panics
1 parent 4bb6b9a commit 0ca6469

File tree

1 file changed

+27
-36
lines changed

1 file changed

+27
-36
lines changed

src/sdl2/audio.rs

Lines changed: 27 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,15 @@ extern "C" fn audio_callback_marshall<CB: AudioCallback>
373373
use std::slice::from_raw_parts_mut;
374374
use std::mem::size_of;
375375
unsafe {
376-
let cb_userdata: &mut CB = &mut *(userdata as *mut CB);
376+
let cb_userdata: &mut Option<CB> = &mut *(userdata as *mut _);
377377
let buf: &mut [CB::Channel] = from_raw_parts_mut(
378378
stream as *mut CB::Channel,
379379
len as usize / size_of::<CB::Channel>()
380380
);
381381

382-
cb_userdata.callback(buf);
382+
if let Some(cb) = cb_userdata {
383+
cb.callback(buf);
384+
}
383385
}
384386
}
385387

@@ -394,14 +396,13 @@ pub struct AudioSpecDesired {
394396
}
395397

396398
impl AudioSpecDesired {
397-
fn convert_to_ll<CB, F, C, S>(freq: F, channels: C, samples: S, userdata: *mut CB) -> sys::SDL_AudioSpec
399+
fn convert_to_ll<CB, F, C, S>(freq: F, channels: C, samples: S, userdata: *mut Option<CB>) -> sys::SDL_AudioSpec
398400
where
399401
CB: AudioCallback,
400402
F: Into<Option<i32>>,
401403
C: Into<Option<u8>>,
402404
S: Into<Option<u16>>,
403405
{
404-
use std::mem::transmute;
405406

406407
let freq = freq.into();
407408
let channels = channels.into();
@@ -413,22 +414,20 @@ impl AudioSpecDesired {
413414

414415
// A value of 0 means "fallback" or "default".
415416

416-
unsafe {
417-
sys::SDL_AudioSpec {
418-
freq: freq.unwrap_or(0),
419-
format: <CB::Channel as AudioFormatNum>::audio_format().to_ll(),
420-
channels: channels.unwrap_or(0),
421-
silence: 0,
422-
samples: samples.unwrap_or(0),
423-
padding: 0,
424-
size: 0,
425-
callback: Some(audio_callback_marshall::<CB>
426-
as extern "C" fn
427-
(arg1: *mut c_void,
428-
arg2: *mut uint8_t,
429-
arg3: c_int)),
430-
userdata: transmute(userdata)
431-
}
417+
sys::SDL_AudioSpec {
418+
freq: freq.unwrap_or(0),
419+
format: <CB::Channel as AudioFormatNum>::audio_format().to_ll(),
420+
channels: channels.unwrap_or(0),
421+
silence: 0,
422+
samples: samples.unwrap_or(0),
423+
padding: 0,
424+
size: 0,
425+
callback: Some(audio_callback_marshall::<CB>
426+
as extern "C" fn
427+
(arg1: *mut c_void,
428+
arg2: *mut uint8_t,
429+
arg3: c_int)),
430+
userdata: userdata as *mut _,
432431
}
433432
}
434433

@@ -596,7 +595,7 @@ pub struct AudioDevice<CB: AudioCallback> {
596595
device_id: AudioDeviceID,
597596
spec: AudioSpec,
598597
/// Store the callback to keep it alive for the entire duration of `AudioDevice`.
599-
userdata: Box<CB>
598+
userdata: Box<Option<CB>>
600599
}
601600

602601
impl<CB: AudioCallback> AudioDevice<CB> {
@@ -607,14 +606,8 @@ impl<CB: AudioCallback> AudioDevice<CB> {
607606
D: Into<Option<&'a str>>,
608607
{
609608

610-
// SDL_OpenAudioDevice needs a userdata pointer, but we can't initialize the
611-
// callback without the obtained AudioSpec.
612-
// Create an uninitialized box that will be initialized after SDL_OpenAudioDevice.
613-
let userdata: *mut CB = unsafe {
614-
let b: Box<CB> = Box::new(mem::uninitialized());
615-
mem::transmute(b)
616-
};
617-
let desired = AudioSpecDesired::convert_to_ll(spec.freq, spec.channels, spec.samples, userdata);
609+
let mut userdata: Box<Option<CB>> = Box::new(None);
610+
let desired = AudioSpecDesired::convert_to_ll(spec.freq, spec.channels, spec.samples, &mut *userdata);
618611

619612
let mut obtained = unsafe { mem::uninitialized::<sys::SDL_AudioSpec>() };
620613
unsafe {
@@ -636,10 +629,8 @@ impl<CB: AudioCallback> AudioDevice<CB> {
636629
id => {
637630
let device_id = AudioDeviceID::PlaybackDevice(id);
638631
let spec = AudioSpec::convert_from_ll(obtained);
639-
let mut userdata: Box<CB> = mem::transmute(userdata);
640632

641-
let garbage = mem::replace(&mut userdata as &mut CB, get_callback(spec));
642-
mem::forget(garbage);
633+
*userdata = Some(get_callback(spec));
643634

644635
Ok(AudioDevice {
645636
subsystem: a.clone(),
@@ -713,7 +704,7 @@ impl<CB: AudioCallback> AudioDevice<CB> {
713704
/// but the callback data will be dropped.
714705
pub fn close_and_get_callback(self) -> CB {
715706
drop(self.device_id);
716-
*self.userdata
707+
self.userdata.expect("Missing callback")
717708
}
718709
}
719710

@@ -725,11 +716,11 @@ pub struct AudioDeviceLockGuard<'a, CB> where CB: AudioCallback, CB: 'a {
725716

726717
impl<'a, CB: AudioCallback> Deref for AudioDeviceLockGuard<'a, CB> {
727718
type Target = CB;
728-
fn deref(&self) -> &CB { &self.device.userdata }
719+
fn deref(&self) -> &CB { (*self.device.userdata).as_ref().expect("Missing callback") }
729720
}
730721

731722
impl<'a, CB: AudioCallback> DerefMut for AudioDeviceLockGuard<'a, CB> {
732-
fn deref_mut(&mut self) -> &mut CB { &mut self.device.userdata }
723+
fn deref_mut(&mut self) -> &mut CB { (*self.device.userdata).as_mut().expect("Missing callback") }
733724
}
734725

735726
impl<'a, CB: AudioCallback> Drop for AudioDeviceLockGuard<'a, CB> {
@@ -835,7 +826,7 @@ mod test {
835826
assert_eq!(new_buffer.len(), new_buffer_expected.len(), "capacity must be exactly equal to twice the original vec size");
836827

837828
// // this has been commented, see https://discourse.libsdl.org/t/change-of-behavior-in-audiocvt-sdl-convertaudio-from-2-0-5-to-2-0-6/24682
838-
// // to maybe re-enable it someday
829+
// // to maybe re-enable it someday
839830
// assert_eq!(new_buffer, new_buffer_expected);
840831
}
841832
}

0 commit comments

Comments
 (0)