-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
borrowck is unsound in the presence of &'static mut
s
#27616
Comments
Maybe there should be a "strict outlives" requirement for reborrows for |
I think there are two ways of looking at this issue:
@eddyb's suggestions seems reasonable as a general rule, but in this case, isn't the borrow happening from an unsafe pointer, which doesn't have a lifetime? How would it prevent @arielb1's attack? |
You can basically do that with every It doesn't matter how you get them. |
@arielb1 Can you mount the attack on a variable that's actually statically allocated? i.e.: static mut FOO : MyType = ... Isn't this the reason static variables can't have |
Note that this would also make a |
A counter example to my question/claim that @arielb1 came up with on IRC: use std::cell::Cell;
use std::fmt::Debug;
fn transmute<X, Y: Debug>(a: Y, dummy: X, x: &'static mut Result<X, Y>,
y: &'static mut Result<X, Y>) -> &'static mut X {
*x = Ok(dummy);
let foo = x.as_mut().unwrap();
*y = Err(a);
return foo;
}
static mut FOO : Result<Option<&'static Cell<usize>>, usize> = Err(123);
fn main() {
let baz : usize = 0xdeadbeef;
let myfoo = unsafe { &mut FOO };
let addr = &baz as *const usize as usize;
let oops = transmute(addr, None, myfoo, myfoo);
println!("Trying to print at address 0x{0:x}", addr);
oops.map(|o| {
println!("0x{0:x}", o.get());
o.set(0xabcdef);
println!("0x{0:x}", baz);
});
} So aliasing 'static references is just plain problematic, and clearly |
My intuition (and I suspect that of other people) was that global
I believe |
I agree with @eddyb that it's the re-borrow of |
There is no
|
@arielb1 My intuition was that the resulting |
You can always borrow for the outermost function's lifetime - all borrows must intersect it. |
A example of the core problem is: fn foo(x: &'static mut i32) -> (&'static mut i32, &'static mut i32) {
(x, x)
} i.e. |
So the reason for this is clear enough. There is this code in ty::ReStatic => {
// If we get here, an error must have been
// reported in
// `lifetime::guarantee_lifetime()`, because
// the only legal ways to have a borrow with a
// static lifetime should not require
// restrictions. To avoid reporting derived
// errors, we just return here without adding
// any loans.
return;
} I think the assumption here was that |
@nikomatsakis I'm curious what the effects of removing that early return would be. |
@eddyb sorry for being slow to respond. The answer is that I think the resulting restrictions are enough, as long as we extend the loan out to the borders of the fn. I have a patch that does just this. |
fixes #2 With rust-lang/rust#27616 fixed, we're able to do this. Not expected to break any users as &mut is coerced to & without issue. Tests passed unchanged. May want to consider adding tests to check for mutability.
STR
Actual Result
memory corruption - 0wned is printed.
Thanks for alevy on IRC for reporting this.
The text was updated successfully, but these errors were encountered: