Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
tormol opened this issue Aug 29, 2018 · 2 comments
Open
Labels

Comments

@tormol
Copy link

tormol commented Aug 29, 2018

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"

@nagisa
Copy link
Owner

nagisa commented Aug 29, 2018

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 nagisa added the bug label Aug 29, 2018
@tormol
Copy link
Author

tormol commented Aug 29, 2018

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(())
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants