-
Notifications
You must be signed in to change notification settings - Fork 36
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
Change lifetime bounds to allow copying references #119
base: main
Are you sure you want to change the base?
Change lifetime bounds to allow copying references #119
Conversation
Your example produces these errors: error[E0597]: `s` does not live long enough
--> scratch/src/main.rs:27:5
|
16 | let mut s = CopyRefBuilder {
| ----- binding `s` declared here
...
27 | s.with_mut(|f| {
| ^ borrowed value does not live long enough
| _____|
| |
28 | | *f.ref1 = *f.ref2;
29 | | });
| |______- argument requires that `s` is borrowed for `'static`
...
36 | }
| - `s` dropped here while still borrowed
error[E0499]: cannot borrow `s` as mutable more than once at a time
--> scratch/src/main.rs:30:5
|
27 | s.with_mut(|f| {
| - first mutable borrow occurs here
| _____|
| |
28 | | *f.ref1 = *f.ref2;
29 | | });
| |______- argument requires that `s` is borrowed for `'static`
30 | s.with_mut(|f| {
| ^ second mutable borrow occurs here Try updating your test case to reflect the usage in your example. |
@someguynamedjosh I added a new test case. |
|
My bad, I forgot to switch away from the main branch after cloning your repo. |
With this change, I can do the following: use ouroboros::self_referencing;
#[self_referencing]
struct CopyRef {
data: String,
#[borrows(data)]
#[covariant]
ref1: Option<&'this str>,
other: String,
#[borrows(data, other)]
#[covariant]
ref2: Option<&'this str>,
}
fn main() {
println!("Hello, world!");
let mut s = CopyRefBuilder {
data: "test".to_string(),
ref1_builder: |_| None,
other: "other".to_string(),
ref2_builder: |x, _| Some(x),
}
.build();
s.with_mut(|f| {
*f.ref2 = Some(f.other);
*f.ref1 = *f.ref2;
});
drop(s);
} Which triggers a use-after-free. |
It's worth noting that these lifetime bounds: 'this2: 'this1,
'this1: 'this2, Require that |
@someguynamedjosh I fixed lifetime bounds as follows: If the set of variables borrowed by reference For example: #[self_referencing]
struct CopyRef {
data: String,
#[borrows(data)]
ref1: &'this str,
other: String,
#[borrows(data, other)]
ref2: &'this str,
} In this case, |
Thank you for continuing to look into this. It looks like this update allows the original issue again: use ouroboros::self_referencing;
#[self_referencing]
struct CopyRef {
data: String,
#[borrows(data)]
#[covariant]
ref1: Option<&'this str>,
other: String,
#[borrows(other)]
#[covariant]
ref2: Option<&'this str>,
}
fn main() {
println!("Hello, world!");
let mut s = CopyRefBuilder {
data: "test".to_string(),
ref1_builder: |_| None,
other: "other".to_string(),
ref2_builder: |_| None,
}
.build();
s.with_mut(|f| {
*f.ref1 = Some(f.other);
});
drop(s);
} |
@someguynamedjosh I fixed a bug and added a new fail test. |
Sorry for the delay. Your fix does indeed solve the above issue. I've now done some more testing and found that mutably borrowing a field that itself borrows other fields yields a compiler error since that causes the intermediate field to no longer be considered a "tail" field, excluding it from the #[self_referencing]
struct Chained {
data: String,
#[borrows(data)]
ref_a: &'this str,
#[borrows(mut ref_a)]
ref_b: &'this mut &'this str,
}
If I go in and add a PhantomData whenever this comes up, I am able to do some trickery with mutable references to trigger another use-after-free: #[self_referencing]
struct CopyRef {
#[borrows()]
the_ref: &'this str,
later: String,
#[borrows(later, mut the_ref)]
target: &'this mut &'this str,
}
fn main() {
let mut s = CopyRefBuilder {
the_ref: "static",
later: "test".to_string(),
target_builder: |_, the_ref| the_ref,
}
.build();
s.with_mut(|f| {
**f.target = f.later;
});
drop(s); // "later" dropped before "the_ref".
} This is the field I added to the generated phantom: ::core::marker::PhantomData<(&'this0 mut (), &'this1 mut (), &'this2 mut ())>, |
This branch changes lifetime bounds of
BorrowedMutFields
to allow copying references. (fixes #118)Struct definition:
Generated code:
Test: