-
Notifications
You must be signed in to change notification settings - Fork 13.2k
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
Lifetimes can escape in traits / objects #5723
Comments
Is this bug still relevant? Did the fix get into the new borrowck? cc: @brson |
I suspect it is still relevant. I'll have to dig back into the details to remember. |
I want to elaborate a little for future bug reviewers, because I did not understand what Niko meant by "the lifetime of the High-level explanation:Consider: let s = ~"input"; return with_str_reader(s, consumer); This will invoke In theory, the implementation of Concrete examplefn main() {
fn its97<T>(f: &fn(@Reader) -> T) -> T {
let s = std::str::from_bytes([97]);
std::io::with_str_reader(s, f)
}
fn consumer(r: @std::io::Reader) -> int { r.read_byte() }
println(fmt!("%d", its97(consumer)));
let long_lived_reader = its97(|r| r);
println(fmt!("%d", consumer(long_lived_reader)));
} Output:
(We're "lucky" that it didn't crash, rather than returning the arbitrary value 0.) When this bug is fixed, the above example code should be rejected, as well as any variant that lets the reader flow to the The solutionsNiko's suggested alternative signatures properly reflect the intended implementation. In the first, pub fn with_str_reader<T>(s: &str, f: &fn(&Reader) -> T) -> T {...} the reader is itself passed as a pub fn str_reader<'a, T>(s: &'a str) -> &'a Reader {...} the reader is again a borrowed reference; this time the reader's lifetime explicitly tied to the lifetime of Implementation-oriented details:I had thought, after reading the description and talking to Niko, the implementation of pub fn with_bytes_reader<T>(bytes: &[u8], f: &fn(@Reader) -> T) -> T {
f(@BytesReader {
bytes: bytes,
pos: @mut 0
} as @Reader)
}
pub fn with_str_reader<T>(s: &str, f: &fn(@Reader) -> T) -> T {
with_bytes_reader(s.as_bytes(), f)
} I plan to check with Niko about this. My current hypothesis is that the borrow-checker should be rejecting the code above, and when Niko says "I have a fix for this in a branch", I believe is is referring to a fix to the borrow-checker so that this code is rejected. But since |
One minor correction: the bug is not, technically, in the borrow checker, but rather the regionck code, whose job it is to guarantee that regions do not "leak" into closure and object types. |
See this comment on the cause: https://github.com/mozilla/rust/pull/7248/files#r4805965 |
the best exploit i can do with this is reading bytes off the heap.
i suspect you could get it to crash if you handed it a slice to a vector that was on a page that later becomes unmapped from the address space. almost certainly not coerce. |
Sigh. Some piece of the infrastructure only saw the "fix NNNN" in the phrase "does not fix NNNN". |
wow sighhhh |
triage bump; nothing to add. |
At this point it is just the with_bytes_reader interface that needs to be fixed, and not the type system. |
@bblum really? I can still do this:
|
oh you are right, my mistake. there is still the lifetimes problem, where traits need to be able to express someething like |
This is needed to bootstrap fix for #5723.
Been working on this. Highly related to #8861 |
This seems to be coming up a lot. I'll try to get a writeup of my thoughts shortly. Lots to write lately! |
All trait objects must be annotated with a lifetime. This means that code like this breaks: fn f(x: Box<Trait>) { ... } fn g<'a>(x: &'a Trait) { ... } Change this code to: fn f(x: Box<Trait+'static>) { ... } fn g<'a>(x: &'a Trait<'a>) { ... } This will be eventually addressed by some additions that @nikomatsakis wants. However, the fundamental thrust of this change is necessary to achieve memory safety. Further additions to improve ergonomics will follow. Closes rust-lang#5723.
Closing in favor of #16462 |
Implements rust-lang/rfcs#192. In particular: 1. type parameters can have lifetime bounds and objects can close over borrowed values, presuming that they have suitable bounds. 2. objects must have a bound, though it may be derived from the trait itself or from a `Send` bound. 3. all types must be well-formed. 4. type parameters and lifetime parameters may themselves have lifetimes as bounds. Something like `T:'a` means "the type T outlives 'a`" and something like `'a:'b`" means "'a outlives 'b". Outlives here means "all borrowed data has a lifetime at least as long". This is a [breaking-change]. The most common things you have to fix after this change are: 1. Introduce lifetime bounds onto type parameters if your type (directly or indirectly) contains a reference. Thus a struct like `struct Ref<'a, T> { x: &'a T }` would be changed to `struct Ref<'a, T:'a> { x: &'a T }`. 2. Introduce lifetime bounds onto lifetime parameters if your type contains a double reference. Thus a type like `struct RefWrapper<'a, 'b> { r: &'a Ref<'b, int> }` (where `Ref` is defined as before) would need to be changed to `struct RefWrapper<'a, 'b:'a> { ... }`. 2. Explicitly give object lifetimes in structure definitions. Most commonly, this means changing something like `Box<Reader>` to `Box<Reader+'static>`, so as to indicate that this is a reader without any borrowed data. (Note: you may wish to just change to `Box<Reader+Send>` while you're at it; it's a more restrictive type, technically, but means you can send the reader between threads.) The intuition for points 1 and 2 is that a reference must never outlive its referent (the thing it points at). Therefore, if you have a type `&'a T`, we must know that `T` (whatever it is) outlives `'a`. And so on. Closes #5723.
This code from the
io
library is definitely wrong as the lifetime of the parameters
is lost:The lifetime of the
s
is lost. I have a fix for this in a branch I want to land (currently commented out with a FIXME) but I don't want to turn on the check because to do this right we need&
objects working. I'm not even sure where to insert strategic transmutes to getwith_str_reader
working.This signature for example should really be:
or even
There is a test that was supposed to be defending against this case, but it was basically accidentally reporting an error.
The text was updated successfully, but these errors were encountered: