VYPR
Critical severityNVD Advisory· Published Sep 25, 2019· Updated Aug 5, 2024

CVE-2019-16881

CVE-2019-16881

Description

A use-after-free vulnerability in portaudio-rs crate for Rust allows arbitrary code execution due to missing unwind safety in stream callbacks.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

A use-after-free vulnerability in portaudio-rs crate for Rust allows arbitrary code execution due to missing unwind safety in stream callbacks.

The portaudio-rs crate through version 0.3.1 contains a use-after-free vulnerability in the stream_callback and stream_finished_callback functions. This occurs because the callback types are not marked as unwind safe, allowing a panic during a callback to drop the stream while the callback is still executing, leading to a use-after-free condition [1][2].

Exploitation requires an attacker to cause a panic within the callback, which can be achieved by providing malicious input or triggering an error condition. The attack is remotely exploitable via network if the application processes untrusted audio data, with no authentication or user interaction required [3].

A successful exploit can result in arbitrary code execution, memory corruption, and full compromise of confidentiality, integrity, and availability [3].

The vulnerability is fixed in version 0.3.2 of the crate. Users should upgrade immediately as no workarounds are available [1][3].

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
portaudio-rscrates.io
< 0.3.20.3.2

Affected products

2

Patches

1
7466df019f67

Merge branch 'BartMassey-master'

https://github.com/mvdnes/portaudio-rsMathijs van de NesMay 14, 2020via ghsa
5 files changed · +65 78
  • Cargo.toml+2 2 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "portaudio-rs"
    -version = "0.3.1"
    +version = "0.3.2"
     authors = ["mvdnes <git@mathijs.vd-nes.nl>"]
     description = "PortAudio bindings for Rust"
     license = "MIT"
    @@ -9,6 +9,6 @@ license = "MIT"
     name = "portaudio_rs"
     
     [dependencies]
    -bitflags = "0.3"
    +bitflags = "1"
     libc = "0.2"
     portaudio-sys = { path = "portaudio-sys", version = "0.1" }
    
  • examples/demo.rs+3 3 modified
    @@ -24,11 +24,11 @@ fn print_devs()
     
     fn demo() -> portaudio::PaResult
     {
    -    let stream = try!(portaudio::stream::Stream::open_default(1, 1, 44100.0, portaudio::stream::FRAMES_PER_BUFFER_UNSPECIFIED, None));
    +    let stream = portaudio::stream::Stream::open_default(1, 1, 44100.0, portaudio::stream::FRAMES_PER_BUFFER_UNSPECIFIED, None)?;
     
    -    try!(stream.start());
    +    stream.start()?;
     
    -    let input = try!(stream.read(44100));
    +    let input = stream.read(44100)?;
     
         let mut phase = 0.0f32;
         let mut buffer = Vec::with_capacity(44100 * SECONDS);
    
  • examples/test.rs+24 30 modified
    @@ -15,36 +15,30 @@ fn main()
     
     fn print_info()
     {
    -    match hostapi::get_count()
    +    if let Ok(api_count) = hostapi::get_count()
         {
    -        Ok(api_count) => {
    -            for i in 0 .. api_count
    +        for i in 0 .. api_count
    +        {
    +            let name = match hostapi::get_info(i)
                 {
    -                let name = match hostapi::get_info(i)
    -                {
    -                    None => "???".to_string(),
    -                    Some(ha) => ha.name,
    -                };
    -                println!("api {}: {}", i, name);
    -            }
    -        },
    -        _ => {},
    +                None => "???".to_string(),
    +                Some(ha) => ha.name,
    +            };
    +            println!("api {}: {}", i, name);
    +        }
         }
     
    -    match device::get_count()
    +    if let Ok(device_count) = device::get_count()
         {
    -        Ok(device_count) => {
    -            for i in 0 .. device_count
    +        for i in 0 .. device_count
    +        {
    +            let name = match device::get_info(i)
                 {
    -                let name = match device::get_info(i)
    -                {
    -                    None => "???".to_string(),
    -                    Some(d) => d.name,
    -                };
    -                println!("dev {}: {}", i, name);
    -            }
    -        },
    -        _ => {},
    +                None => "???".to_string(),
    +                Some(d) => d.name,
    +            };
    +            println!("dev {}: {}", i, name);
    +        }
         }
     }
     
    @@ -59,11 +53,11 @@ fn callback_demo()
     {
         let callback = Box::new(|_input: &[f32], output: &mut [f32], _time: stream::StreamTimeInfo, _flags: stream::StreamCallbackFlags| -> stream::StreamCallbackResult
         {
    -        static mut lp: f32 = 0.0;
    -        static mut rp: f32 = 0.0;
    +        static mut LP: f32 = 0.0;
    +        static mut RP: f32 = 0.0;
     
    -        let mut left_phase = unsafe { lp };
    -        let mut right_phase = unsafe { rp };
    +        let mut left_phase = unsafe { LP };
    +        let mut right_phase = unsafe { RP };
     
             for i in 0 .. output.len() / 2
             {
    @@ -77,8 +71,8 @@ fn callback_demo()
                 if right_phase >= 1.0 { right_phase -= 2.0; }
             }
     
    -        unsafe { lp = left_phase; }
    -        unsafe { rp = right_phase; }
    +        unsafe { LP = left_phase; }
    +        unsafe { RP = right_phase; }
     
             stream::StreamCallbackResult::Continue
         });
    
  • src/lib.rs+4 4 modified
    @@ -9,15 +9,15 @@
     //! ```
     //! fn demo() -> portaudio_rs::PaResult
     //! {
    -//!     let stream = try!(portaudio_rs::stream::Stream::open_default(
    +//!     let stream = portaudio_rs::stream::Stream::open_default(
     //!                           0, // input channels
     //!                           1, // output channels
     //!                           44100.0, // sample rate
     //!                           portaudio_rs::stream::FRAMES_PER_BUFFER_UNSPECIFIED,
     //!                           None // no callback
    -//!                      ));
    +//!                  )?;
     //!
    -//!     try!(stream.start());
    +//!     stream.start()?;
     //!
     //!     let mut phase = 0.0f32;
     //!     let mut buffer = Vec::with_capacity(44100);
    @@ -30,7 +30,7 @@
     //!         if phase > 1.0 { phase -= 2.0; }
     //!     }
     //!
    -//!     try!(stream.write(&buffer));
    +//!     stream.write(&buffer)?;
     //!
     //!     Ok(())
     //! }
    
  • src/stream.rs+32 39 modified
    @@ -4,7 +4,6 @@ use ll;
     use pa::{PaError, PaResult};
     use device::DeviceIndex;
     use util::{to_pa_result, pa_time_to_duration, duration_to_pa_time};
    -use std::mem;
     use std::time::Duration;
     use libc::{c_void, c_ulong};
     use std::io::prelude::*;
    @@ -29,10 +28,10 @@ pub enum StreamCallbackResult
     }
     
     /// Callback to consume, process or generate audio
    -pub type StreamCallback<'a, I, O> = FnMut(&[I], &mut [O], StreamTimeInfo, StreamCallbackFlags) -> StreamCallbackResult + 'a;
    +pub type StreamCallback<'a, I, O> = dyn FnMut(&[I], &mut [O], StreamTimeInfo, StreamCallbackFlags) -> StreamCallbackResult + 'a;
     
     /// Callback to be fired when a StreamCallback is stopped
    -pub type StreamFinishedCallback<'a> = FnMut() + 'a;
    +pub type StreamFinishedCallback<'a> = dyn FnMut() + 'a;
     
     struct StreamUserData<'a, I, O>
     {
    @@ -71,41 +70,41 @@ impl StreamTimeInfo
     
     bitflags!(
         #[doc="Flags indicating the status of the callback"]
    -    flags StreamCallbackFlags: u64 {
    +    pub struct StreamCallbackFlags: u64 {
             #[doc="Indicates that the callback has inserted one or more zeroes since not enough data was available"]
    -        const INPUT_UNDERFLOW = 0x01,
    +        const INPUT_UNDERFLOW = 0x01;
     
             #[doc="Indicates that the callback has discarded some data"]
    -        const INPUT_OVERFLOW = 0x02,
    +        const INPUT_OVERFLOW = 0x02;
     
             #[doc="Indicates that extra data was inserted in the output since there was not engough available"]
    -        const OUTPUT_UNDERFLOW = 0x04,
    +        const OUTPUT_UNDERFLOW = 0x04;
     
             #[doc="Indicates that certain data was discarded since there was no room"]
    -        const OUTPUT_OVERFLOW = 0x08,
    +        const OUTPUT_OVERFLOW = 0x08;
     
             #[doc="Some or all of the output data will be used to prime the stream, input data may be zero"]
    -        const PRIMING_OUTPUT = 0x10
    +        const PRIMING_OUTPUT = 0x10;
         }
     );
     
     bitflags!(
         #[doc="Flags used to control the behavior of a stream"]
    -    flags StreamFlags: u64 {
    +    pub struct StreamFlags: u64 {
             #[doc="Disable clipping of out of range samples"]
    -        const CLIP_OFF                                   = 0x00000001,
    +        const CLIP_OFF                                   = 0x0000_0001;
     
             #[doc="Disable dithering"]
    -        const DITHER_OFF                                 = 0x00000002,
    +        const DITHER_OFF                                 = 0x0000_0002;
     
             #[doc="Request that a full duplex stream will not discard overflowed input samples. The frames_per_buffer must be set to unspecified (0)"]
    -        const NEVER_DROP_INPUT                           = 0x00000004,
    +        const NEVER_DROP_INPUT                           = 0x0000_0004;
     
             #[doc="Call the stream callback to fill initial output buffers, rather than priming the buffers with silence"]
    -        const PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK = 0x00000008,
    +        const PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK = 0x0000_0008;
     
             #[doc="Range for platform specific flags. Not all of the upper 16 bits need to be set at the same time."]
    -        const PLATFORM_SPECIFIC                          = 0xFFFF0000
    +        const PLATFORM_SPECIFIC                          = 0xFFFF_0000;
         }
     );
     
    @@ -116,8 +115,8 @@ extern "C" fn stream_callback<I, O>(input: *const c_void,
                                         status_flags: ll::PaStreamCallbackFlags,
                                         user_data: *mut c_void) -> ::libc::c_int
     {
    -    // TODO: use Box::from_raw once it is stable
    -    let mut stream_data: Box<StreamUserData<I, O>> = unsafe { mem::transmute(user_data) };
    +    // We do not want to deallocate this memory since it is owned by other user code. So leak the box.
    +    let stream_data: &mut StreamUserData<I, O> = Box::leak( unsafe { Box::from_raw(user_data as *mut StreamUserData<I, O>) } );
     
         let input_buffer: &[I] = unsafe
         {
    @@ -140,22 +139,17 @@ extern "C" fn stream_callback<I, O>(input: *const c_void,
             None => StreamCallbackResult::Abort,
         };
     
    -    mem::forget(stream_data);
    -
         result as i32
     }
     
     extern "C" fn stream_finished_callback<I, O>(user_data: *mut c_void)
     {
    -    // TODO: use Box::from_raw once it is stable
    -    let mut stream_data: Box<StreamUserData<I, O>> = unsafe { mem::transmute(user_data) };
    -    match stream_data.finished_callback
    +    // We do not want to deallocate this memory since it is owned by other user code. So leak the box.
    +    let stream_data: &mut StreamUserData<I, O> = Box::leak( unsafe { Box::from_raw(user_data as *mut StreamUserData<I, O>) } );
    +    if let Some(ref mut f) = stream_data.finished_callback
         {
    -        Some(ref mut f) => (*f)(),
    -        None => {},
    +         (*f)();
         };
    -
    -    mem::forget(stream_data);
     }
     
     /// Types that are allowed to be used as samples in a Stream
    @@ -167,11 +161,11 @@ pub trait SampleType
         /// Should return the PortAudio flag which corresponds to the type
         fn sample_format() -> u64;
     }
    -impl SampleType for f32 { fn sample_format() -> u64 { 0x00000001 } }
    -impl SampleType for i32 { fn sample_format() -> u64 { 0x00000002 } }
    -impl SampleType for i16 { fn sample_format() -> u64 { 0x00000008 } }
    -impl SampleType for i8 { fn sample_format() -> u64 { 0x00000010 } }
    -impl SampleType for u8 { fn sample_format() -> u64 { 0x00000020 } }
    +impl SampleType for f32 { fn sample_format() -> u64 { 0x0000_0001 } }
    +impl SampleType for i32 { fn sample_format() -> u64 { 0x0000_0002 } }
    +impl SampleType for i16 { fn sample_format() -> u64 { 0x0000_0008 } }
    +impl SampleType for i8 { fn sample_format() -> u64 { 0x0000_0010 } }
    +impl SampleType for u8 { fn sample_format() -> u64 { 0x0000_0020 } }
     
     #[cfg(test)]
     fn get_sample_size<T: SampleType>() -> Result<u32, PaError>
    @@ -226,7 +220,7 @@ impl<'a, T: SampleType> Stream<'a, T, T>
             {
                 num_input: num_input_channels,
                 num_output: num_output_channels,
    -            callback: callback,
    +            callback,
                 finished_callback: None,
             });
             let mut pa_stream = ::std::ptr::null_mut();
    @@ -247,7 +241,7 @@ impl<'a, T: SampleType> Stream<'a, T, T>
     
             match to_pa_result(code)
             {
    -            Ok(()) => Ok(Stream { pa_stream: pa_stream,
    +            Ok(()) => Ok(Stream { pa_stream,
                                       user_data: userdata,
                                       inputs: num_input_channels,
                                       outputs: num_output_channels,
    @@ -299,7 +293,7 @@ impl<'a, I: SampleType, O: SampleType> Stream<'a, I, O>
             {
                 num_input: input_cnt,
                 num_output: output_cnt,
    -            callback: callback,
    +            callback,
                 finished_callback: None,
             });
     
    @@ -320,8 +314,8 @@ impl<'a, I: SampleType, O: SampleType> Stream<'a, I, O>
     
             match to_pa_result(result)
             {
    -            Ok(()) => Ok(Stream { pa_stream: pa_stream,
    -                                  user_data: user_data,
    +            Ok(()) => Ok(Stream { pa_stream,
    +                                  user_data,
                                       inputs: input_cnt,
                                       outputs: output_cnt,
                           }),
    @@ -493,10 +487,9 @@ impl<'a, I: SampleType, O: SampleType> Drop for Stream<'a, I, O>
         fn drop(&mut self)
         {
             debug_assert!(self.user_data.num_output == self.outputs); //userdata should not be garbled
    -        match self.close()
    +        if let Err(v) = self.close()
             {
    -            Err(v) => { let _ = write!(&mut ::std::io::stderr(), "Stream drop error: {:?}\n", v); },
    -            Ok(_) => {},
    +            let _ = writeln!(&mut ::std::io::stderr(), "Stream drop error: {:?}", v);
             };
         }
     }
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.