mackron / miniaudio

Audio playback and capture library written in C, in a single source file.
https://miniaud.io
Other
4.07k stars 361 forks source link

Integer divide by zero in ma_device_init #412

Closed Curve closed 2 years ago

Curve commented 2 years ago

Hello again, this bug report is a follow up to #323, the issue seems to appear again for some.

When retrieving the default device the following code crashes with an Integer divide by zero exception in ma_device_init.

int main() {
  std::string defaultName;
  {
    ma_device device;
    ma_device_config deviceConfig =
        ma_device_config_init(ma_device_type_playback);
    auto res = ma_device_init(nullptr, &deviceConfig, &device);
    if (res != MA_SUCCESS) {
      std::cerr << "Got error: " << res << std::endl;
    }

    defaultName = device.playback.name;
    ma_device_uninit(&device);
  }
  std::cout << "Default device is: " << defaultName << std::endl;
  return 0;
} 

This bug appears from (currently tested) 0.11.3 up to 0.11.5.

Executing the aforementioned code results in the following output:

DEBUG: Failed to initialize WASAPI backend.
DEBUG: Failed to initialize DirectSound backend.
DEBUG: Failed to initialize WinMM backend.
DEBUG: Failed to initialize Core Audio backend.
DEBUG: Failed to initialize sndio backend.
DEBUG: Failed to initialize audio(4) backend.
DEBUG: Failed to initialize OSS backend.
DEBUG: Attempting to initialize PulseAudio backend...
DEBUG: Loading library: libpulse.so
DEBUG: Loading symbol: pa_mainloop_new
DEBUG: Loading symbol: pa_mainloop_free
DEBUG: Loading symbol: pa_mainloop_quit
DEBUG: Loading symbol: pa_mainloop_get_api
DEBUG: Loading symbol: pa_mainloop_iterate
DEBUG: Loading symbol: pa_mainloop_wakeup
DEBUG: Loading symbol: pa_threaded_mainloop_new
DEBUG: Loading symbol: pa_threaded_mainloop_free
DEBUG: Loading symbol: pa_threaded_mainloop_start
DEBUG: Loading symbol: pa_threaded_mainloop_stop
DEBUG: Loading symbol: pa_threaded_mainloop_lock
DEBUG: Loading symbol: pa_threaded_mainloop_unlock
DEBUG: Loading symbol: pa_threaded_mainloop_wait
DEBUG: Loading symbol: pa_threaded_mainloop_signal
DEBUG: Loading symbol: pa_threaded_mainloop_accept
DEBUG: Loading symbol: pa_threaded_mainloop_get_retval
DEBUG: Loading symbol: pa_threaded_mainloop_get_api
DEBUG: Loading symbol: pa_threaded_mainloop_in_thread
DEBUG: Loading symbol: pa_threaded_mainloop_set_name
DEBUG: Loading symbol: pa_context_new
DEBUG: Loading symbol: pa_context_unref
DEBUG: Loading symbol: pa_context_connect
DEBUG: Loading symbol: pa_context_disconnect
DEBUG: Loading symbol: pa_context_set_state_callback
DEBUG: Loading symbol: pa_context_get_state
DEBUG: Loading symbol: pa_context_get_sink_info_list
DEBUG: Loading symbol: pa_context_get_source_info_list
DEBUG: Loading symbol: pa_context_get_sink_info_by_name
DEBUG: Loading symbol: pa_context_get_source_info_by_name
DEBUG: Loading symbol: pa_operation_unref
DEBUG: Loading symbol: pa_operation_get_state
DEBUG: Loading symbol: pa_channel_map_init_extend
DEBUG: Loading symbol: pa_channel_map_valid
DEBUG: Loading symbol: pa_channel_map_compatible
DEBUG: Loading symbol: pa_stream_new
DEBUG: Loading symbol: pa_stream_unref
DEBUG: Loading symbol: pa_stream_connect_playback
DEBUG: Loading symbol: pa_stream_connect_record
DEBUG: Loading symbol: pa_stream_disconnect
DEBUG: Loading symbol: pa_stream_get_state
DEBUG: Loading symbol: pa_stream_get_sample_spec
DEBUG: Loading symbol: pa_stream_get_channel_map
DEBUG: Loading symbol: pa_stream_get_buffer_attr
DEBUG: Loading symbol: pa_stream_set_buffer_attr
DEBUG: Loading symbol: pa_stream_get_device_name
DEBUG: Loading symbol: pa_stream_set_write_callback
DEBUG: Loading symbol: pa_stream_set_read_callback
DEBUG: Loading symbol: pa_stream_set_suspended_callback
DEBUG: Loading symbol: pa_stream_set_moved_callback
DEBUG: Loading symbol: pa_stream_is_suspended
DEBUG: Loading symbol: pa_stream_flush
DEBUG: Loading symbol: pa_stream_drain
DEBUG: Loading symbol: pa_stream_is_corked
DEBUG: Loading symbol: pa_stream_cork
DEBUG: Loading symbol: pa_stream_trigger
DEBUG: Loading symbol: pa_stream_begin_write
DEBUG: Loading symbol: pa_stream_write
DEBUG: Loading symbol: pa_stream_peek
DEBUG: Loading symbol: pa_stream_drop
DEBUG: Loading symbol: pa_stream_writable_size
DEBUG: Loading symbol: pa_stream_readable_size
DEBUG: System Architecture:
DEBUG:   Endian: LE
DEBUG:   SSE2:   YES
DEBUG:   AVX2:   NO
DEBUG:   NEON:   NO
INFO: [PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.
INFO: [PulseAudio] Playback attr: maxlength=11520, tlength=3840, prebuf=-1, minreq=-1, fragsize=3840; periodSizeInFrames=480
Exception en point flottant (core dumped)

Related Discussion: https://github.com/Soundux/Soundux/issues/501

mackron commented 2 years ago

I'm not reproducing this one. I've scanned the code and made a possible fix. Are you able to get the user to give that another try? If this fixes the division by zero error, I'm suspecting there's a possibility that audio may still not work because this change is a workaround for something that is extremely unusual and not something I've ever seen before.

mackron commented 2 years ago

Should clarify, this potential fix is in the dev branch.

d-gardes commented 2 years ago

Hello, I'm coming back with deeper investigations:

The problem seems to be intermittent. Let me explain.

I tried the repro test (code in first message of this issue) just after booting with your workaround. It failed. I opened VLC and played a random mp3 and relaunched the test. This time it passed: Note: I launched it several times in a row without any problem.

dorian@Kubuntu:~/Bureau/test-repro/build$ ./repro-test
<...>
DEBUG: Loading symbol: pa_stream_writable_size
DEBUG: Loading symbol: pa_stream_readable_size
DEBUG: System Architecture:
DEBUG:   Endian: LE
DEBUG:   SSE2:   YES
DEBUG:   AVX2:   NO
DEBUG:   NEON:   NO
INFO: [PulseAudio] Playback attr: maxlength=5760, tlength=1920, prebuf=-1, minreq=-1, fragsize=1920; periodSizeInFrames=480
INFO: [PulseAudio] Playback actual attr: maxlength=5760, tlength=1440, prebuf=964, minreq=480, fragsize=1920; internalPeriodSizeInFrames=360
INFO: [PulseAudio]
INFO:   Carte Son Interne (Playback)
INFO:     Format:      16-bit Signed Integer -> 16-bit Signed Integer
INFO:     Channels:    2 -> 2
INFO:     Sample Rate: 48000 -> 48000
INFO:     Buffer Size: 360*4 (1440)
INFO:     Conversion:
INFO:       Pre Format Conversion:  NO
INFO:       Post Format Conversion: NO
INFO:       Channel Routing:        NO
INFO:       Resampling:             NO
INFO:       Passthrough:            YES
Default device is: Carte Son Interne

It seems that MiniAudio crash if the audio card isn't initialized of something like.

So I built Soundux with this version of MiniAudio and tried it. I have the same divide by zero problem than in Soundux/Soundux#501.

If I launch again the repro test it fails (same problem than first message).

<...>
DEBUG: Loading symbol: pa_stream_readable_size
DEBUG: System Architecture:
DEBUG:   Endian: LE
DEBUG:   SSE2:   YES
DEBUG:   AVX2:   NO
DEBUG:   NEON:   NO
INFO: [PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.
INFO: [PulseAudio] Playback attr: maxlength=11520, tlength=3840, prebuf=-1, minreq=-1, fragsize=3840; periodSizeInFrames=480
Exception en point flottant (core dumped)

I tried to restart PulseAudio -> No change

After a reboot (and a random mp3) I launched the repro test and it pass again. If I launch Soundux same divide by zero and test fail until reboot. I didn't tried without your fix or older version of MiniAudio.

I post my researches here because I don't really know if the problem is in MiniAudio, Soundux or both.

Audio is already such a nightmare on this computer !!

Curve commented 2 years ago

If I launch Soundux same divide by zero and test fail until reboot.

That is really weird

mackron commented 2 years ago

I've pushed another attempted fix for this to the dev branch. If this fixes it, miniaudio should abort with an error. It also means the error is in PulseAudio or something with your system and not miniaudio. But hopefully in this case the device initialization will fail and then fall back to ALSA which might work.

d-gardes commented 2 years ago

I just tried your fix.

The repro test pass.

On an another side Sondux fail the audio part BUT dont crashes and breaks nothing (repro test pass after Soundux).

dorian@Kubuntu:~/Bureau/Soundux/build$ ./soundux
[10:56:17] [warning] Config not found
[10:56:17] [success] LibWnck found - Icon support is enabled
[10:56:17] [message] PulseAudio is ready!
[10:56:18] [success] Unloaded left over module 26
[10:56:18] [success] Unloaded left over module 27
[10:56:18] [success] Unloaded left over module 28
[10:56:18] [success] Unloaded left over module 29
[10:56:18] [success] Unloaded left over module 30
[10:56:18] [failure] Failed to create AudioBackend instance

midi_in_dummy: This class provides no functionality.

[10:56:18] [message] Using DISPLAY :0
[10:56:18] [warning] Failed to find iconPath for tray icon
[10:56:21] [message] UI exited
[10:56:21] [success] Config written

To discriminate between MiniAudio and Soundux I built the Miniaudio simple playback example with your fixes. It works with PulseAudio. So another problem seems to be on Soundux. I also tested after restoring the PulseAudio default configuration. No change. And nothing very interesting in verbose PulseAudio logs.

Curve commented 2 years ago

I just tried your fix.

The repro test pass.

On an another side Sondux fail the audio part BUT dont crashes and breaks nothing (repro test pass after Soundux).

dorian@Kubuntu:~/Bureau/Soundux/build$ ./soundux
[10:56:17] [warning] Config not found
[10:56:17] [success] LibWnck found - Icon support is enabled
[10:56:17] [message] PulseAudio is ready!
[10:56:18] [success] Unloaded left over module 26
[10:56:18] [success] Unloaded left over module 27
[10:56:18] [success] Unloaded left over module 28
[10:56:18] [success] Unloaded left over module 29
[10:56:18] [success] Unloaded left over module 30
[10:56:18] [failure] Failed to create AudioBackend instance

midi_in_dummy: This class provides no functionality.

[10:56:18] [message] Using DISPLAY :0
[10:56:18] [warning] Failed to find iconPath for tray icon
[10:56:21] [message] UI exited
[10:56:21] [success] Config written

To discriminate between MiniAudio and Soundux I built the Miniaudio simple playback example with your fixes. It works with PulseAudio. So another problem seems to be on Soundux. I also tested after restoring the PulseAudio default configuration. No change. And nothing very interesting in verbose PulseAudio logs.

I think audio is another problem from our side, at least on pipewire, not sure on pulse

mackron commented 2 years ago

So the issue I fixed is related to pa_stream_get_sample_spec(). This should return the format/channels/rate of the stream which miniaudio needs to know in order to do format conversion. For some reason, it's returning a valid pointer, but the structure is filled with zeros. This in turn results in the call to ma_get_bytes_per_frame() after that to return zero and therefore a division by zero error occurs.

The way miniaudio handles this is to abort with an error. There's nothing else miniaudio can do in this situation because that's critical information that must be accurate. It feels like there's a bug somewhere in the implementation of pa_stream_get_sample_spec(). Whether or not that PulseAudio of Pipewire's PulseAudio emulation layer (if you're using that) I have no idea. In any case, I don't think it's really a bug in miniaudio, but I'm happy to work around it so it aborts cleanly rather than crashing.

mackron commented 2 years ago

This has been released. Thanks for reporting and testing.

Curve commented 2 years ago

Thanks for the quick fix!