video-audio / va-ts

MPEG-TS muxer/demuxer library for Rust

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`Demuxer` can carry non-Send types across thread boundaries

JOE1994 opened this issue Β· comments

Hello πŸ¦€ , we (Rust group @sslab-gatech) found an undefined-behavior issue in this crate while scanning Rust code on crates.io for potential vulnerabilities.

Issue

Send is unconditionally implemented for Demuxer, so that it is possible for a non-Send implementor of DemuxerEvents trait to be sent across thread boundaries.

unsafe impl<T> Send for Demuxer<T> where T: DemuxerEvents {}

Proof of Concept

Here is a simple program where Demuxer is used to move a MutexGuard(!Send object) to another thread.
It is also possible to create data races by inserting non-Send types like Rc or Cell into Demuxer
(which is not demonstrated in the example code below).

use va_ts::{Demuxer, DemuxerEvents, SubtableID, DemuxedPacket, DemuxedTable};
use std::sync::{Mutex, MutexGuard};
use std::thread::{self, ThreadId};
use std::ops::Drop;

struct X(MutexGuard<'static, u64>, ThreadId);
impl DemuxerEvents for X {
    fn on_table(&mut self, _: SubtableID, _: &DemuxedTable) { }
    fn on_packet(&mut self, _: &DemuxedPacket) { }
}
impl Drop for X {
    fn drop(&mut self) {
        // `MutexGuard` must not be dropped from a thread that didn't lock the `Mutex`.
        //
        // If a thread attempts to unlock a Mutex that it has not locked, it can result in undefined behavior.
        // (https://github.com/rust-lang/rust/issues/23465#issuecomment-82730326)
        assert_eq!(self.1, thread::current().id());
    }
}

fn main() {
    let static_mutex = Box::leak(Box::new(Mutex::new(0xbeefbeef_u64)));
    // MutexGuard is `!Send`
    let mutex_guard = static_mutex.lock().unwrap();
    let tid = thread::current().id();

    let demuxer = Demuxer::new(X(mutex_guard, tid));
    std::thread::spawn(move || {
        let demuxer = demuxer;

        // demuxer is dropped here, along with `MutexGuard`.
    }).join().unwrap();
}

Suggested Fix

Adding a trait bound T: Send as below will allow the compiler to prevent Demuxer from moving !Send types across thread boundaries.

unsafe impl<T> Send for Demuxer<T> where T: DemuxerEvents + Send {}

Thank you for reviewing this issue πŸ‘

Hello!
Thanks a lot!

Thank you for accepting the fix πŸ‘
Would you also mind publishing a new release containing the fix to crates.io ?

No problem :) New release uploaded. Thanks!