-
-
Notifications
You must be signed in to change notification settings - Fork 770
Tock 2.0 - AppSlice: fix unsoundness with arbitrary pointers in slices #2464
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
Tock 2.0 - AppSlice: fix unsoundness with arbitrary pointers in slices #2464
Conversation
Also, to show that this has very drastic implications, take this example: use core::ptr::NonNull;
use core::mem::size_of;
use core::slice;
fn main() {
println!("size_of ptr: {}, Option<ptr>: {}",
size_of::<&[u8]>(),
size_of::<Option<&[u8]>>()
);
let super_unsound = Some(unsafe {
slice::from_raw_parts_mut(0x0 as *mut u8, 0)
});
if let Some(_) = super_unsound {
println!("All fine.");
} else {
println!("Houston, we have a problem!") // This branch should NEVER be taken!
}
let hopefully_sound = Some(unsafe {
slice::from_raw_parts_mut(NonNull::<u8>::dangling().as_ptr(), 0)
});
if let Some(slice) = hopefully_sound {
println!(
"NonNull::dangling works as expected, returned ptr: {:p}",
slice.as_ptr()
);
}
} which produces
|
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.
The code changes look right to me after reading the reference you provided, great catch.
I will leave it to others to decide whether we should duplicate the documentation in the way you have here or use references to that documentation in a single place.
I read through the comments and the code and they look technically correct. One option to consider would be to extract the // appslice_parts_to_slice and appslice_parts_to_slice_mut convert an AppSlice's
// internal (ptr, len) representation into a slice. This will automatically
// convert zero-length appslices into valid zero-sized slices regardless of the
// value of ptr.
//
// Safety: The memory [ptr, ptr + len) must be within a single process' address
// space. If len is nonzero then ptr must be nonzero as well.
fn raw_appslice_to_slice(ptr: *const u8, len: usize) -> &[u8] {
use core::ptr::NonNull;
use core::slice::from_raw_parts;
match len {
0 => unsafe { from_raw_parts(NonNull::dangling().as_ptr(), len) },
_ => unsafe { from_raw_parts(ptr, len) },
}
}
// See docs for appslice_parts_to_slice.
fn raw_appslice_to_slice_mut(ptr: *mut u8, len: usize) -> &mut [u8] {
use core::ptr::NonNull;
use core::slice::from_raw_parts;
match len {
0 => unsafe { from_raw_parts_mut(NonNull::dangling().as_ptr(), len) },
_ => unsafe { from_raw_parts_mut(ptr, len) },
}
} That would remove some of the redundancy. |
d5b6bcb
to
e62de00
Compare
@jrvanwhy Thanks for those recommendations. As suggested, I've split out the actual creation of the Rust slices based on AppSlice information into separate unsafe functions, which also gives us a chance to specify the required safety requirements in a more explicit, encapsulated way. |
Rust has strict pointer validity requirements, which -- in part -- also apply to accesses of zero length. In Tock 2.0, AppSlices are allowed to have arbitrary pointers passed in by userspace as long as their length is set to be 0. However, it is still not automatically safe & sound to construct Rust slices from these pointers. This change ensures that if an AppSlice of length 0 is used (i.e. a Rust slice is created), the pointer is set to the value returned by NonNull::dangling(), which is guaranteed to meet the alignment & other requirements for valid pointers posed by Rust, while not requiring (or ensuring) that the memory which the pointer points to is valid or allocated. Therefore, this is only safe for zero-length accesses (e.g. for zero-length slices). Signed-off-by: Leon Schuermann <[email protected]>
By splitting out the construction of Rust slices from the higher-level AppSlice types, the required safety checks and constraints can be encapsulated and documented at a central location, without code duplication. Signed-off-by: Leon Schuermann <[email protected]>
This adds an documentation on the constructor of ReadWriteAppSlice and ReadOnlyAppSlice which outlines the assumptions and requirements for a safe use of the aforementioned structs. Signed-off-by: Leon Schuermann <[email protected]>
e62de00
to
f88ac17
Compare
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.
Looks good, but took me a while to figure out where from_raw_parts
is defined.
2465: Tock 2.0 - ReadWriteAppSlice: require mutable borrow for ReadWrite::mut_map_or r=bradjc a=lschuermann ### Pull Request Overview Prior to this change, a `ReadWriteAppSlice` could be used to obtain two mutable Rust slices to the same memory region. In Rust, at any given time, one can have either one mutable reference or any number of immutable references to some object / memory, and as such this is incorrect. This changes `ReadWriteAppSlice::mut_map_or` to take a mutable reference to `self` and as such enforces these rules when taking a mutable borrow of the `ReadWriteAppSlice` itself. Furthermore, this changes the way references to a `ReadWriteAppSlice` are handled in capsules such that a call to `ReadWrite::mut_map_or` can take a mutable reference to `self`. The changes were rather mechanical, and while there are possibly more elegant solutions by refactoring larger parts of the capsule code, this is outside of the scope of this PR. ### Testing Strategy This pull request was tested by compiling. ### TODO or Help Wanted N/A ### Documentation Updated - [x] ~Updated the relevant files in `/docs`, or~ no updates are required. ### Formatting - [x] Ran `make prepush`. ### Acknowledgements Thanks to @jrvanwhy for reporting this issue on #2464. Co-authored-by: Leon Schuermann <[email protected]>
bors r+ |
2465: Tock 2.0 - ReadWriteAppSlice: require mutable borrow for ReadWrite::mut_map_or r=bradjc a=lschuermann ### Pull Request Overview Prior to this change, a `ReadWriteAppSlice` could be used to obtain two mutable Rust slices to the same memory region. In Rust, at any given time, one can have either one mutable reference or any number of immutable references to some object / memory, and as such this is incorrect. This changes `ReadWriteAppSlice::mut_map_or` to take a mutable reference to `self` and as such enforces these rules when taking a mutable borrow of the `ReadWriteAppSlice` itself. Furthermore, this changes the way references to a `ReadWriteAppSlice` are handled in capsules such that a call to `ReadWrite::mut_map_or` can take a mutable reference to `self`. The changes were rather mechanical, and while there are possibly more elegant solutions by refactoring larger parts of the capsule code, this is outside of the scope of this PR. ### Testing Strategy This pull request was tested by compiling. ### TODO or Help Wanted N/A ### Documentation Updated - [x] ~Updated the relevant files in `/docs`, or~ no updates are required. ### Formatting - [x] Ran `make prepush`. ### Acknowledgements Thanks to @jrvanwhy for reporting this issue on tock#2464. Co-authored-by: Leon Schuermann <[email protected]>
2464: Tock 2.0 - AppSlice: fix unsoundness with arbitrary pointers in slices r=hudson-ayers a=lschuermann ### Pull Request Overview Rust has strict pointer validity requirements, which -- in part -- also apply to accesses of zero length. In Tock 2.0, `AppSlice`s are allowed to have arbitrary pointers passed in by userspace as long as their length is set to be 0. However, it is still not automatically safe & sound to construct Rust slices from these pointers. This change ensures that if an AppSlice of length 0 is used (i.e. a Rust slice is created), the pointer is set to the value returned by `NonNull::dangling()`, which is guaranteed to meet the alignment & other requirements for valid pointers posed by Rust, while not requiring (or ensuring) that the memory which the pointer points to is valid or allocated. Therefore, this is only safe for zero-length accesses (e.g. for zero-length slices). This duplicates a bunch of documentation. However this is a very important invariant we must uphold and as such, I feel it justifies the additional verbosity and explicit explanation on each of the three occurrences. Signed-off-by: Leon Schuermann <[email protected]> ### Testing Strategy TODO! This pull request needs testing. ### TODO or Help Wanted N/A ### Documentation Updated - [x] ~Updated the relevant files in `/docs`, or~ no updates are required. ### Formatting - [x] Ran `make prepush`. Co-authored-by: Leon Schuermann <[email protected]>
2465: Tock 2.0 - ReadWriteAppSlice: require mutable borrow for ReadWrite::mut_map_or r=bradjc a=lschuermann ### Pull Request Overview Prior to this change, a `ReadWriteAppSlice` could be used to obtain two mutable Rust slices to the same memory region. In Rust, at any given time, one can have either one mutable reference or any number of immutable references to some object / memory, and as such this is incorrect. This changes `ReadWriteAppSlice::mut_map_or` to take a mutable reference to `self` and as such enforces these rules when taking a mutable borrow of the `ReadWriteAppSlice` itself. Furthermore, this changes the way references to a `ReadWriteAppSlice` are handled in capsules such that a call to `ReadWrite::mut_map_or` can take a mutable reference to `self`. The changes were rather mechanical, and while there are possibly more elegant solutions by refactoring larger parts of the capsule code, this is outside of the scope of this PR. ### Testing Strategy This pull request was tested by compiling. ### TODO or Help Wanted N/A ### Documentation Updated - [x] ~Updated the relevant files in `/docs`, or~ no updates are required. ### Formatting - [x] Ran `make prepush`. ### Acknowledgements Thanks to @jrvanwhy for reporting this issue on tock#2464. Co-authored-by: Leon Schuermann <[email protected]>
2464: Tock 2.0 - AppSlice: fix unsoundness with arbitrary pointers in slices r=hudson-ayers a=lschuermann ### Pull Request Overview Rust has strict pointer validity requirements, which -- in part -- also apply to accesses of zero length. In Tock 2.0, `AppSlice`s are allowed to have arbitrary pointers passed in by userspace as long as their length is set to be 0. However, it is still not automatically safe & sound to construct Rust slices from these pointers. This change ensures that if an AppSlice of length 0 is used (i.e. a Rust slice is created), the pointer is set to the value returned by `NonNull::dangling()`, which is guaranteed to meet the alignment & other requirements for valid pointers posed by Rust, while not requiring (or ensuring) that the memory which the pointer points to is valid or allocated. Therefore, this is only safe for zero-length accesses (e.g. for zero-length slices). This duplicates a bunch of documentation. However this is a very important invariant we must uphold and as such, I feel it justifies the additional verbosity and explicit explanation on each of the three occurrences. Signed-off-by: Leon Schuermann <[email protected]> ### Testing Strategy TODO! This pull request needs testing. ### TODO or Help Wanted N/A ### Documentation Updated - [x] ~Updated the relevant files in `/docs`, or~ no updates are required. ### Formatting - [x] Ran `make prepush`. Co-authored-by: Leon Schuermann <[email protected]>
Pull Request Overview
Rust has strict pointer validity requirements, which -- in part -- also apply to accesses of zero length. In Tock 2.0,
AppSlice
s areallowed to have arbitrary pointers passed in by userspace as long as their length is set to be 0. However, it is still not automatically safe & sound to construct Rust slices from these pointers.
This change ensures that if an AppSlice of length 0 is used (i.e. a Rust slice is created), the pointer is set to the value returned by
NonNull::dangling()
, which is guaranteed to meet the alignment & other requirements for valid pointers posed by Rust, while not requiring (or ensuring) that the memory which the pointer points to is valid or allocated. Therefore, this is only safe for zero-length accesses (e.g. for zero-length slices).This duplicates a bunch of documentation. However this is a very important invariant we must uphold and as such, I feel it justifies the additional verbosity and explicit explanation on each of the three occurrences.
Signed-off-by: Leon Schuermann [email protected]
Testing Strategy
TODO! This pull request needs testing.
TODO or Help Wanted
N/A
Documentation Updated
Updated the relevant files inno updates are required./docs
, orFormatting
make prepush
.