Skip to content

Commit 3e73d82

Browse files
committed
fix(alsa): validate StreamConfig for zero channels, sample rate, and buffer size
1 parent cc49e0f commit 3e73d82

2 files changed

Lines changed: 242 additions & 166 deletions

File tree

src/host/alsa/mod.rs

Lines changed: 28 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,25 @@ const DEFAULT_PERIODS: alsa::pcm::Frames = 2;
9494
// Some ALSA plugins (e.g. alsaequal, certain USB drivers) are not reentrant.
9595
static ALSA_OPEN_MUTEX: std::sync::Mutex<()> = std::sync::Mutex::new(());
9696

97+
fn open_pcm(pcm_id: &str, direction: alsa::Direction) -> Result<alsa::pcm::PCM, Error> {
98+
let _guard = ALSA_OPEN_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
99+
alsa::pcm::PCM::new(pcm_id, direction, true).map_err(|e| {
100+
let e = Error::from(e);
101+
if e.kind() == ErrorKind::UnsupportedConfig {
102+
let dir = match direction {
103+
alsa::Direction::Capture => "input",
104+
alsa::Direction::Playback => "output",
105+
};
106+
Error::with_message(
107+
ErrorKind::UnsupportedOperation,
108+
format!("Device does not support {dir}"),
109+
)
110+
} else {
111+
e
112+
}
113+
})
114+
}
115+
97116
// TODO: Not yet defined in rust-lang/libc crate
98117
const LIBC_ENOTSUPP: libc::c_int = 524;
99118

@@ -361,10 +380,9 @@ impl Device {
361380
sample_format: SampleFormat,
362381
stream_type: alsa::Direction,
363382
) -> Result<StreamInner, Error> {
364-
let handle = {
365-
let _guard = ALSA_OPEN_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
366-
alsa::pcm::PCM::new(&self.pcm_id, stream_type, true)?
367-
};
383+
crate::validate_stream_config(&conf)?;
384+
385+
let handle = open_pcm(&self.pcm_id, stream_type)?;
368386

369387
let hw_params = set_hw_params_from_format(&handle, conf, sample_format)?;
370388
let (buffer_size, period_size) = set_sw_params_from_format(&handle, stream_type)?;
@@ -438,10 +456,7 @@ impl Device {
438456
&self,
439457
stream_t: alsa::Direction,
440458
) -> Result<VecIntoIter<SupportedStreamConfigRange>, Error> {
441-
let pcm = {
442-
let _guard = ALSA_OPEN_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
443-
alsa::pcm::PCM::new(&self.pcm_id, stream_t, true)?
444-
};
459+
let pcm = open_pcm(&self.pcm_id, stream_t)?;
445460

446461
let hw_params = alsa::pcm::HwParams::any(&pcm)?;
447462

@@ -520,7 +535,8 @@ impl Device {
520535
const CHANNEL_ENUM_CAP: u32 = 64;
521536
let max_channels = hw_params
522537
.get_channels_max()?
523-
.min(min_channels.max(CHANNEL_ENUM_CAP));
538+
.min(CHANNEL_ENUM_CAP)
539+
.min(ChannelCount::MAX as u32);
524540

525541
let mut output = Vec::new();
526542
let mut seen_formats: Vec<SampleFormat> = Vec::new();
@@ -564,22 +580,9 @@ impl Device {
564580
// ALSA does not offer default stream formats, so instead we compare all supported formats by
565581
// the `SupportedStreamConfigRange::cmp_default_heuristics` order and select the greatest.
566582
fn default_config(&self, stream_t: alsa::Direction) -> Result<SupportedStreamConfig, Error> {
567-
let mut formats: Vec<_> = {
568-
match self.supported_configs(stream_t) {
569-
// EINVAL when querying direction the device does not support (input-only or output-only)
570-
Err(err) if err.kind() == ErrorKind::UnsupportedConfig => {
571-
let dir = match stream_t {
572-
alsa::Direction::Capture => "input",
573-
alsa::Direction::Playback => "output",
574-
};
575-
return Err(Error::with_message(
576-
ErrorKind::UnsupportedOperation,
577-
format!("Device does not support {dir}"),
578-
));
579-
}
580-
Err(err) => return Err(err),
581-
Ok(fmts) => fmts.collect(),
582-
}
583+
let mut formats: Vec<_> = match self.supported_configs(stream_t) {
584+
Err(err) => return Err(err),
585+
Ok(fmts) => fmts.collect(),
583586
};
584587

585588
formats.sort_by(|a, b| a.cmp_default_heuristics(b));
@@ -1574,13 +1577,6 @@ fn set_hw_params_from_format(
15741577
// buffer_size = 2x and period_size = x. This provides consistent low-latency
15751578
// behavior across different ALSA implementations and hardware.
15761579
if let BufferSize::Fixed(period_size) = config.buffer_size {
1577-
if period_size == 0 {
1578-
return Err(Error::with_message(
1579-
ErrorKind::InvalidInput,
1580-
"Buffer size must be greater than 0",
1581-
));
1582-
}
1583-
15841580
let period_size = period_size as alsa::pcm::Frames;
15851581

15861582
// Validate the requested size against the device's supported ranges using the same PCM

0 commit comments

Comments
 (0)