servo/media

ServoMedia::get is unsound

Closed this issue · 3 comments

nox commented

The initialization of ServoMedia is very much unsound and should just be using lazy_static.

This is INSTANCE, note how it is initialized as a raw null pointer (that should be using std::ptr::null_mut() btw):

static mut INSTANCE: *mut Mutex<Option<Arc<ServoMedia>>> = 0 as *mut _;

Those are the three methods handling initialization:

media/servo-media/lib.rs

Lines 68 to 90 in a70f024

impl ServoMedia {
pub fn init<B: BackendInit>() {
INITIALIZER.call_once(|| unsafe {
let instance = Arc::new(ServoMedia(B::init()));
INSTANCE = Box::into_raw(Box::new(Mutex::new(Some(instance))));
})
}
pub fn init_with_backend(backend: Box<dyn Backend>) {
INITIALIZER.call_once(|| unsafe {
let instance = Arc::new(ServoMedia(backend));
INSTANCE = Box::into_raw(Box::new(Mutex::new(Some(instance))));
})
}
pub fn get() -> Result<Arc<ServoMedia>, ()> {
let instance = unsafe { &*INSTANCE }.lock().unwrap();
match *instance {
Some(ref instance) => Ok(instance.clone()),
None => Err(()),
}
}
}

Note how calling ServoMedia::get() before ServoMedia::init() or ServoMedia::init_with_backend(backend) will dereference a null pointer.

Please use lazy_static instead of manually using Once and a raw pointer in a static.

nox commented

The raw pointer and Once value was previously soundly used but it was broken when the method init was introduced.

How does one pass Box<dyn Backend> into lazy_static block?