Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

lschuermann
Copy link
Member

@lschuermann lschuermann commented Mar 4, 2021

Pull Request Overview

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).

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 in /docs, or no updates are required.

Formatting

  • Ran make prepush.

@lschuermann lschuermann added kernel release-blocker Issue or PR that must be resolved before the next release tock-2.0 Issues and PRs related to Tock version 2.0. labels Mar 4, 2021
@lschuermann lschuermann changed the title AppSlice: fix unsoundness with arbitrary pointers in slices Tock 2.0 - AppSlice: fix unsoundness with arbitrary pointers in slices Mar 4, 2021
@lschuermann
Copy link
Member Author

lschuermann commented Mar 4, 2021

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

size_of ptr: 16,  Option<ptr>: 16
Houston, we have a problem!
NonNull::dangling works as expected, returned ptr: 0x1

@lschuermann lschuermann added the bug label Mar 4, 2021
@lschuermann lschuermann mentioned this pull request Mar 4, 2021
18 tasks
Copy link
Contributor

@hudson-ayers hudson-ayers left a 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.

@jrvanwhy
Copy link
Contributor

jrvanwhy commented Mar 4, 2021

I read through the comments and the code and they look technically correct.

One option to consider would be to extract the if len == 0 { ... } else { ... } logic into separate functions, and use a single comment to describe both. Something like:

// 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.

@lschuermann
Copy link
Member Author

@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]>
@lschuermann lschuermann force-pushed the dev/tock-2.0/appslice-soundness-fix branch from e62de00 to f88ac17 Compare March 5, 2021 11:40
Copy link
Contributor

@bradjc bradjc left a 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.

@hudson-ayers hudson-ayers added the last-call Final review period for a pull request. label Mar 8, 2021
bors bot added a commit that referenced this pull request Mar 9, 2021
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]>
@hudson-ayers
Copy link
Contributor

bors r+

@bors
Copy link
Contributor

bors bot commented Mar 9, 2021

@bors bors bot merged commit b9e76c5 into tock:tock-2.0-dev Mar 9, 2021
lschuermann added a commit to lschuermann/tock that referenced this pull request Mar 22, 2021
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]>
lschuermann added a commit to lschuermann/tock that referenced this pull request Mar 22, 2021
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]>
lschuermann added a commit to lschuermann/tock that referenced this pull request Mar 22, 2021
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]>
lschuermann added a commit to lschuermann/tock that referenced this pull request Mar 22, 2021
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]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug kernel last-call Final review period for a pull request. release-blocker Issue or PR that must be resolved before the next release tock-2.0 Issues and PRs related to Tock version 2.0.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants