Skip to content

Commit 2ffd9dc

Browse files
committed
Handle zero rate/channels from hardware description gracefully
CoreAudio can report zero sample rate or channel count in the hardware stream description during device transitions (e.g. device unplug, default device change). This is the #1 cubeb-coreaudio crash signature, with ~240 crash pings per 30 days on macOS, 100% in the main (parent) process. Previously, create_stream_description() would panic on zero rate or channels. Now: - Validate rate and channel count in setup() immediately after reading the hardware description, returning an error with diagnostic logging when the values are invalid. This catches the problem at the source, where we have full context (device info, stream pointer). - Convert the asserts in create_stream_description() to debug_assert + error return, so programming errors are still caught in dev builds but users don't crash. - The output path already had a zero-channel check ("observed in the wild"); extend it to also check rate, and add both checks to the input path.
1 parent 6767a85 commit 2ffd9dc

File tree

1 file changed

+32
-6
lines changed

1 file changed

+32
-6
lines changed

src/backend/mod.rs

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -267,8 +267,22 @@ fn create_device_info(devid: AudioDeviceID, devtype: DeviceType) -> Option<devic
267267
}
268268

269269
fn create_stream_description(stream_params: &StreamParams) -> Result<AudioStreamBasicDescription> {
270-
assert!(stream_params.rate() > 0);
271-
assert!(stream_params.channels() > 0);
270+
debug_assert!(stream_params.rate() > 0);
271+
debug_assert!(stream_params.channels() > 0);
272+
if stream_params.rate() == 0 {
273+
cubeb_log!(
274+
"create_stream_description: invalid rate 0 (channels={})",
275+
stream_params.channels()
276+
);
277+
return Err(Error::Error);
278+
}
279+
if stream_params.channels() == 0 {
280+
cubeb_log!(
281+
"create_stream_description: invalid channel count 0 (rate={})",
282+
stream_params.rate()
283+
);
284+
return Err(Error::Error);
285+
}
272286

273287
let mut desc = AudioStreamBasicDescription::default();
274288

@@ -3848,6 +3862,16 @@ impl<'ctx> CoreStreamData<'ctx> {
38483862
self.stm_ptr,
38493863
input_hw_desc
38503864
);
3865+
// These have been observed in the wild.
3866+
if input_hw_desc.mSampleRate <= 0.0 || input_hw_desc.mChannelsPerFrame == 0 {
3867+
cubeb_log!(
3868+
"({:p}) Invalid input hardware description: rate={}, channels={}",
3869+
self.stm_ptr,
3870+
input_hw_desc.mSampleRate,
3871+
input_hw_desc.mChannelsPerFrame
3872+
);
3873+
return Err(Error::Error);
3874+
}
38513875
// Notice: when we are using an aggregate device, input_hw_desc.mChannelsPerFrame is the
38523876
// sum of all input channels of all devices added to the aggregate device.
38533877
// Because we set the input device first on the aggregate device, the input device's
@@ -4056,11 +4080,13 @@ impl<'ctx> CoreStreamData<'ctx> {
40564080
output_hw_desc
40574081
);
40584082

4059-
// This has been observed in the wild.
4060-
if output_hw_desc.mChannelsPerFrame == 0 {
4083+
// These have been observed in the wild.
4084+
if output_hw_desc.mSampleRate <= 0.0 || output_hw_desc.mChannelsPerFrame == 0 {
40614085
cubeb_log!(
4062-
"({:p}) Output hardware description channel count is zero",
4063-
self.stm_ptr
4086+
"({:p}) Invalid output hardware description: rate={}, channels={}",
4087+
self.stm_ptr,
4088+
output_hw_desc.mSampleRate,
4089+
output_hw_desc.mChannelsPerFrame
40644090
);
40654091
return Err(Error::Error);
40664092
}

0 commit comments

Comments
 (0)