forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 54
align_to and align_to_mut contract and harnesses #405
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
Open
AlexLB99
wants to merge
3
commits into
model-checking:main
Choose a base branch
from
AlexLB99:check_align_to
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,8 +6,12 @@ | |
|
||
#![stable(feature = "rust1", since = "1.0.0")] | ||
|
||
use safety::{ensures, requires}; | ||
|
||
use crate::cmp::Ordering::{self, Equal, Greater, Less}; | ||
use crate::intrinsics::{exact_div, unchecked_sub}; | ||
#[cfg(kani)] | ||
use crate::kani; | ||
use crate::mem::{self, MaybeUninit, SizedTypeProperties}; | ||
use crate::num::NonZero; | ||
use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive}; | ||
|
@@ -4006,6 +4010,39 @@ impl<T> [T] { | |
/// ``` | ||
#[stable(feature = "slice_align_to", since = "1.30.0")] | ||
#[must_use] | ||
//Checks if the part that will be transmuted from type T to U is valid for type U | ||
//reuses most function logic up to use of from_raw_parts, | ||
//where the potentially unsafe transmute occurs | ||
#[requires( | ||
U::IS_ZST || T::IS_ZST || { | ||
let ptr = self.as_ptr(); | ||
let offset = crate::ptr::align_offset(ptr, align_of::<U>()); | ||
offset > self.len() || { | ||
let (_, rest) = self.split_at(offset); | ||
let (us_len, _) = rest.align_to_offsets::<U>(); | ||
let middle = crate::ptr::slice_from_raw_parts(rest.as_ptr() as *const U, us_len); | ||
crate::ub_checks::can_dereference(middle) | ||
} | ||
} | ||
)] | ||
//The following clause guarantees that middle is of maximum size within self | ||
//If U or T are ZSTs, then middle has size zero, so we adapt the check in that case | ||
#[ensures(|(prefix, _, suffix): &(&[T], &[U], &[T])| | ||
((U::IS_ZST || T::IS_ZST) && prefix.len() == self.len()) || ( | ||
(prefix.len() * size_of::<T>() < align_of::<U>()) && | ||
(suffix.len() * size_of::<T>() < size_of::<U>()) | ||
) | ||
)] | ||
//Either align_to just returns self in the prefix, or the 3 returned slices | ||
//should be sequential, contiguous, and have same total length as self | ||
#[ensures(|(prefix, middle, suffix): &(&[T], &[U], &[T])| | ||
prefix.as_ptr() == self.as_ptr() && | ||
(prefix.len() == self.len() || ( | ||
((prefix.as_ptr()).add(prefix.len()) as *const u8 == middle.as_ptr() as *const u8) && | ||
((middle.as_ptr()).add(middle.len()) as *const u8 == suffix.as_ptr() as *const u8) && | ||
((suffix.as_ptr()).add(suffix.len()) == (self.as_ptr()).add(self.len())) ) | ||
) | ||
)] | ||
pub unsafe fn align_to<U>(&self) -> (&[T], &[U], &[T]) { | ||
// Note that most of this function will be constant-evaluated, | ||
if U::IS_ZST || T::IS_ZST { | ||
|
@@ -4114,6 +4151,48 @@ impl<T> [T] { | |
} | ||
} | ||
|
||
//We need the following wrapper because it is not currently possible to write | ||
//contracts for functions that return mut refs to input arguments | ||
//see https://github.com/model-checking/kani/issues/3764 | ||
//---------------------------- | ||
//Checks if the part that will be transmuted from type T to U is valid for type U | ||
//reuses most function logic up to use of from_raw_parts, | ||
//where the potentially unsafe transmute occurs | ||
#[requires( | ||
U::IS_ZST || T::IS_ZST || { | ||
let ptr = self.as_ptr(); | ||
let offset = crate::ptr::align_offset(ptr, align_of::<U>()); | ||
offset > self.len() || { | ||
let (_, rest) = self.split_at(offset); | ||
let (us_len, _) = rest.align_to_offsets::<U>(); | ||
let middle = crate::ptr::slice_from_raw_parts(rest.as_ptr() as *const U, us_len); | ||
crate::ub_checks::can_dereference(middle) | ||
} | ||
} | ||
)] | ||
//The following clause guarantees that middle is of maximum size within self | ||
//If U or T are ZSTs, then middle has size zero, so we adapt the check in that case | ||
#[ensures(|(prefix, _, suffix): &(*const [T], *const [U], *const [T])| | ||
((U::IS_ZST || T::IS_ZST) && prefix.len() == self.len()) || ( | ||
(prefix.len() * size_of::<T>() < align_of::<U>()) && | ||
(suffix.len() * size_of::<T>() < size_of::<U>()) | ||
) | ||
)] | ||
//Either align_to just returns self in the prefix, or the 3 returned slices | ||
//should be sequential, contiguous, and have same total length as self | ||
#[ensures(|(prefix, middle, suffix): &(*const [T], *const [U], *const [T])| | ||
prefix.as_ptr() == self.as_ptr() && | ||
(prefix.len() == self.len() || ( | ||
((prefix.as_ptr()).add(prefix.len()) as *const u8 == middle.as_ptr() as *const u8) && | ||
((middle.as_ptr()).add(middle.len()) as *const u8 == suffix.as_ptr() as *const u8) && | ||
((suffix.as_ptr()).add(suffix.len()) == (self.as_ptr()).add(self.len())) ) | ||
) | ||
)] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see in the ensures clauses the condition that the middle part is correctly aligned. |
||
unsafe fn align_to_mut_wrapper<U>(&mut self) -> (*const [T], *const [U], *const [T]) { | ||
let (prefix_mut, mid_mut, suffix_mut) = self.align_to_mut::<U>(); | ||
(prefix_mut as *const [T], mid_mut as *const [U], suffix_mut as *const [T]) | ||
} | ||
|
||
/// Splits a slice into a prefix, a middle of aligned SIMD types, and a suffix. | ||
/// | ||
/// This is a safe wrapper around [`slice::align_to`], so inherits the same | ||
|
@@ -5291,3 +5370,90 @@ unsafe impl GetDisjointMutIndex for range::RangeInclusive<usize> { | |
RangeInclusive::from(*self).is_overlapping(&RangeInclusive::from(*other)) | ||
} | ||
} | ||
|
||
#[cfg(kani)] | ||
#[unstable(feature = "kani", issue = "none")] | ||
mod verify { | ||
use super::*; | ||
|
||
//generates proof_of_contract harness for align_to given the T (src) and U (dst) types | ||
macro_rules! proof_of_contract_for_align_to { | ||
($harness:ident, $src:ty, $dst:ty) => { | ||
#[kani::proof_for_contract(<[$src]>::align_to)] | ||
fn $harness() { | ||
const ARR_SIZE: usize = 100; | ||
let src_arr: [$src; ARR_SIZE] = kani::any(); | ||
let src_slice = kani::slice::any_slice_of_array(&src_arr); | ||
let dst_slice = unsafe { src_slice.align_to::<$dst>() }; | ||
} | ||
}; | ||
} | ||
|
||
//generates harnesses for align_to where T is a given src type and U is one of the main primitives | ||
macro_rules! gen_align_to_harnesses { | ||
($mod_name:ident, $src_type:ty) => { | ||
mod $mod_name { | ||
use super::*; | ||
|
||
proof_of_contract_for_align_to!(align_to_u8, $src_type, u8); | ||
proof_of_contract_for_align_to!(align_to_u16, $src_type, u16); | ||
proof_of_contract_for_align_to!(align_to_u32, $src_type, u32); | ||
proof_of_contract_for_align_to!(align_to_u64, $src_type, u64); | ||
proof_of_contract_for_align_to!(align_to_u128, $src_type, u128); | ||
proof_of_contract_for_align_to!(align_to_bool, $src_type, bool); | ||
proof_of_contract_for_align_to!(align_to_char, $src_type, char); | ||
proof_of_contract_for_align_to!(align_to_unit, $src_type, ()); | ||
} | ||
}; | ||
} | ||
|
||
gen_align_to_harnesses!(align_to_from_u8, u8); | ||
gen_align_to_harnesses!(align_to_from_u16, u16); | ||
gen_align_to_harnesses!(align_to_from_u32, u32); | ||
gen_align_to_harnesses!(align_to_from_u64, u64); | ||
gen_align_to_harnesses!(align_to_from_u128, u128); | ||
gen_align_to_harnesses!(align_to_from_bool, bool); | ||
gen_align_to_harnesses!(align_to_from_char, char); | ||
gen_align_to_harnesses!(align_to_from_unit, ()); | ||
|
||
//generates proof_of_contract harness for align_to_mut given the T (src) and U (dst) types | ||
//this uses the contract for align_to_mut_wrapper (see comment there for why) | ||
macro_rules! proof_of_contract_for_align_to_mut { | ||
($harness:ident, $src:ty, $dst:ty) => { | ||
#[kani::proof_for_contract(<[$src]>::align_to_mut_wrapper)] | ||
fn $harness() { | ||
const ARR_SIZE: usize = 100; | ||
let mut src_arr: [$src; ARR_SIZE] = kani::any(); | ||
let src_slice = kani::slice::any_slice_of_array_mut(&mut src_arr); | ||
let dst_slice = unsafe { src_slice.align_to_mut_wrapper::<$dst>() }; | ||
} | ||
}; | ||
} | ||
|
||
//generates harnesses between a given src type and all the main primitives | ||
macro_rules! gen_align_to_mut_harnesses { | ||
($mod_name:ident, $src_type:ty) => { | ||
mod $mod_name { | ||
use super::*; | ||
|
||
proof_of_contract_for_align_to_mut!(align_to_mut_u8, $src_type, u8); | ||
proof_of_contract_for_align_to_mut!(align_to_mut_u16, $src_type, u16); | ||
proof_of_contract_for_align_to_mut!(align_to_mut_u32, $src_type, u32); | ||
proof_of_contract_for_align_to_mut!(align_to_mut_u64, $src_type, u64); | ||
proof_of_contract_for_align_to_mut!(align_to_mut_u128, $src_type, u128); | ||
proof_of_contract_for_align_to_mut!(align_to_mut_bool, $src_type, bool); | ||
proof_of_contract_for_align_to_mut!(align_to_mut_char, $src_type, char); | ||
proof_of_contract_for_align_to_mut!(align_to_mut_unit, $src_type, ()); | ||
} | ||
}; | ||
} | ||
|
||
gen_align_to_mut_harnesses!(align_to_mut_from_u8, u8); | ||
gen_align_to_mut_harnesses!(align_to_mut_from_u16, u16); | ||
gen_align_to_mut_harnesses!(align_to_mut_from_u32, u32); | ||
gen_align_to_mut_harnesses!(align_to_mut_from_u64, u64); | ||
gen_align_to_mut_harnesses!(align_to_mut_from_u128, u128); | ||
gen_align_to_mut_harnesses!(align_to_mut_from_bool, bool); | ||
gen_align_to_mut_harnesses!(align_to_mut_from_char, char); | ||
gen_align_to_mut_harnesses!(align_to_mut_from_unit, ()); | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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 see in the ensures clauses the condition that the middle part is correctly aligned.
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.
Actually, this is something I was wondering about -- my impression was that because it's UB to create a misaligned reference, it would be redundant to have a check for the middle part being aligned (since if it isn't, there would already be UB by the time the ensures clause is checked, meaning the check would either return true or something potentially invalid due to previous UB). But of course it's possible that Kani might have some way to circumvent this problem that I'm not familiar with (otherwise the only way I can think of would be to write this check as an assert inside the body of
align_to
, right before where there could potentially be UB). Would be happy to hear your thoughts on this whenever you have a chance!