rust_libloading icon indicating copy to clipboard operation
rust_libloading copied to clipboard

Document unsafety of functions with 'static-containing return types.

Open tormol opened this issue 6 years ago • 2 comments

Return values from functions that contain 'static lifetimes are not reduced to the lifetime of the loaded library, and can outlive it. Preventing this is probably impossible (especially when the 'static is hidden inside a type like Wrapper(&'static u32)), but the docs could warn about it.

Here's one example that segfaults:

Cargo.toml:

[package]
name = "unsafe_libloading"
version = "0.0.0"

[lib]
path = "lib.rs"
name = "loadee"
crate-type = ["dylib"]

[[bin]]
name = "loader"
path = "main.rs"

[dependencies]
libloading = "0.5"

lib.rs:

#[no_mangle]
pub fn get_str() -> &'static str {
    "Hello, world"
}

main.rs:

extern crate libloading;

fn main() -> libloading::Result<()> {
    let lib = libloading::Library::new("libloadee.so")?;
    let return_value: &'static str = {
        let fun: libloading::Symbol<extern fn()->&'static str> = unsafe{ lib.get(b"get_str") }?;
        fun()
    };
    drop(lib);
    println!("return value: {}", return_value);
    Ok(())
}

I assume this issue also applies to loaded global variables, but dereferencing those produced bogus values even without dropping lib:

lib.rs:

#[no_mangle]
pub static REF: &'static u16 = &19;

main.rs:

extern crate libloading;

fn main() -> libloading::Result<()> {
    let lib = libloading::Library::new("libloadee.so")?;
    let var: libloading::Symbol<&'static u16> = unsafe{ lib.get(b"REF") }?;
    let dereferenced: u16 = **ref;
    println!("variable: {} = 0x{:x}", dereferenced, dereferenced);
    Ok(())
}

prints "variable: 27312 = 0x6ab0"

tormol avatar Aug 29 '18 14:08 tormol

Return values from functions that contain 'static lifetimes are not reduced to the lifetime of the loaded library, and can outlive it. Preventing this is probably impossible…

Indeed, I don’t see a way to prevent this, although this is not necessarily invalid (i.e. the value returned by a function is not necessarily 'lib. It might be worthwhile to mention this problem somewhere in the documentation.

I assume this issue also applies to loaded global variables, but dereferencing those produced bogus values even without dropping lib

Type of your Symbol is wrong here (lacking one level of indirection… it might be worthwhile to add a wrapper type for this on the libloading side…). But otherwise, yes, the problem is also present in this case.

This could be fixed by a smart wrapper type libloading::LibraryStatic*<u16>, but that definitely feels… inelegant.

Thanks for the report!

nagisa avatar Aug 29 '18 15:08 nagisa

I didn't realize variables must be loaded via a pointer type, although it makes sense with fn types being pointers under the hood.

With LibraryStatic, do you mean a completely different Library type for accessing statics, or a Symbol variant such as StaticSymbol? I don't see how StaticSymbol could prevent getting a reference to the 'static reference in struct Wrapper(&'static u8), but it would fix #13 for variables and stop this from compiling:

extern crate libloading;

fn main() -> libloading::Result<()> {
    let v: &u16 = {
        let lib = libloading::Library::new("libloadee.so")?;
        let sym: libloading::Symbol<&u16> = unsafe{ lib.get(b"VAR") }?;
        *sym
    };
    println!("{}", *v);
    Ok(())
}

tormol avatar Aug 29 '18 18:08 tormol