-
Notifications
You must be signed in to change notification settings - Fork 105
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
Implement Ref::{try_as_ref,try_into_ref,try_into_mut}
#1930
base: v0.8.x
Are you sure you want to change the base?
Conversation
// SAFETY: This is sound because `bytes` lives for `'a`. `Self` is | ||
// `IntoByteSlice`, whose `.into_byte_slice()` method is guaranteed to | ||
// produce a `&'a [u8]` with the same address and length as the slice | ||
// obtained by `.deref()` (which is how `bytes` is obtained). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This wigs me out. Maybe we can just use .into_byte_slice()
and then add an unsafe Ref
constructor that reconstitutes the Ref
from the byte slice? That way we can use it to reconstruct the Ref
in the error case and we don't have to do lifetime shenanigans.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's possible without changing the return type — B
is convertible into a &[u8]
, but it's not necessarily itself a &[u8]
.
Why does this wig you out? Lifetimes are fundamentally about the live extent of a referent, and our safety conditions on ByteSlice and IntoByteSlice are all about having a stable referent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well r
only lives as long as the body, so bytes
's lifetime also doesn't outlive the body. I grant that the underlying allocation we're pointing to lives for 'a
, but are we guaranteed that Rust won't make assumptions on the basis that r
becomes unreachable after this scope ends? I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rust will never make optimizations based off of lifetimes because we specifically only computer lower-bound lifetimes in MIR and also erase them by the time we get to codegen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PoC of unsoundness: add the following to the bottom of this file and run ./cargo.sh +nightly miri test unsound --features alloc
:
#[cfg(test)]
#[cfg(feature = "alloc")]
mod unsound_demo {
use alloc::boxed::Box;
use super::*;
#[test]
fn test_try_into_ref_unsound() {
unsafe impl ByteSlice for Box<[u8]> {}
unsafe impl<'a> IntoByteSlice<'a> for Box<[u8]> {
fn into_byte_slice(self) -> &'a [u8] {
Box::leak(self)
}
}
let b: Box<[u8]> = Box::new([0u8; 1]);
let r = Ref::<_, bool>::from_bytes(b).unwrap();
let b = Ref::try_into_ref(r).unwrap();
assert_eq!(b, &false);
}
}
The problem is that we're using the normal Deref
here instead of going through IntoByteSlice::into_byte_slice
, and so we're dropping the B: ByteSlice
before returning. For B
types which have ownership semantics, this can cause unsoundness like it does here.
// SAFETY: This is sound because `bytes` lives for `'a`. `Self` is | ||
// `IntoByteSliceMut`, whose `.into_byte_slice_mut()` method is | ||
// guaranteed to produce a `&'a [u8]` with the same address and length | ||
// as the slice obtained by `.deref()` (which is how `bytes` is | ||
// obtained). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here as above
b528e37
to
311a259
Compare
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v0.8.x #1930 +/- ##
==========================================
+ Coverage 87.59% 87.72% +0.13%
==========================================
Files 16 16
Lines 5988 6103 +115
==========================================
+ Hits 5245 5354 +109
- Misses 743 749 +6 ☔ View full report in Codecov by Sentry. |
Only
Ref::try_as_mut
remains missing, probably pending polonius landing in rustc.Partially fixes #1865
Supersedes #1184
A restricted form of
Ref::try_as_mut
can be implemented now as:...but we don't yet implement
ClonableByteSlice
for anything except&[u8]
, so the method is effectively untestable.