Debugging a crash in Mixer's Opus support

Update: The problem was a use-after-free bug in OPUS_Delete() and I got very confused because SDL_UnloadObject() is likely unloading the whole function call from underneath myself. The fix seems to be to add a check to see if the Opus library is still loaded.

    if (opus.loaded > 0) {
        opus.op_free(music->of);
    }

A proper fix will have to include calling the unload, though I’m unsure if there will be a leak if the stream isn’t closed before the library is unloaded.


I’ve been writing a game in Rust that uses SDL_Mixer and it’s been great for the most part. Moving from loading music as Wave files to Opus has lead to a crash when Mixer is freed before music, and only on macOS (Linux works fine, Windows I’m unsure about). It’s complicated and I’m going to own the weird Rust part of this problem. Details are here for the curious:

github.(dotcom)/Rust-SDL2/rust-sdl2/issues/1152 #sorry, I’m new and can only post two links. This is the lesser of the three I’m using

What I’m having trouble with right now is my stack trace ends within Mixer within music_opus.c:OPUS_Delete() at the call to opus.op_free(music->of);

  * frame #0: 0x0000000104b6d480
    frame #1: 0x00000001003919e5 libSDL2_mixer-2.0.0.dylib`OPUS_Delete + 53
    frame #2: 0x000000010038e965 libSDL2_mixer-2.0.0.dylib`Mix_FreeMusic + 181
    frame #3: 0x0000000100008360 sound-crash`_$LT$sdl2..mixer..Music$u20$as$u20$core..ops..drop..Drop$GT$::drop::hf81612fc771c9986(self=0x00007ffeefbff500) at mod.rs:774:22
    frame #4: 0x00000001000025d1 sound-crash`core::ptr::drop_in_place$LT$sdl2..mixer..Music$GT$::h4e5003563535f1b6((null)=0x00007ffeefbff500) at mod.rs:192:1

I’ve downloaded the source for opusfile, and added some debug printing but for some reason rebuilding/installing opusfile then rebuilding/installing SDL_mixer isn’t getting me output from opusfile.c:op_free()

I’m on macOS here and am using both lldb on the command line and Visual Studio Code. The problem is that I’m a novice with both. It seems that things are compiling and installing properly as the following printed string is in both static and dynamic builds of opusfile.{a,dylib}

void op_free(OggOpusFile *_of){
  printf("Okay, we're in op_free!");
  fflush(stdout);
  return;

  if(OP_LIKELY(_of!=NULL)){
    op_clear(_of);
    _ogg_free(_of);
  }
}

Update: Is libopusfile is being statically linked?:

mymachine:game me$ otool -L ../target/debug/examples/sound-crash
../target/debug/examples/sound-crash:
	/usr/local/lib/libSDL2-2.0.0.dylib (compatibility version 17.0.0, current version 17.0.0)
	/usr/local/lib/libSDL2_mixer-2.0.0.dylib (compatibility version 3.0.0, current version 3.2.0)
	/usr/lib/libiconv.2.dylib (compatibility version 7.0.0, current version 7.0.0)
	/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1281.100.1)
	/usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0)

Update 2: It seems that choosing to statically link opusfile into Mixer solves my crash. I’m guessing that on Mac, whatever magic is being used to load libopusfile actually unloads it as well while the program is running and the memory that held op_free is now empty?