-
Notifications
You must be signed in to change notification settings - Fork 41
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
transmute_unchecked contracts and harnesses #185
base: main
Are you sure you want to change the base?
Conversation
hi @celinval and @feliperodri! Thoughts most welcome. |
Can the CI failures please be addressed? |
I guess that we can't trigger the workflow @tautschnig |
You might unintentionally have reverted submodule changes? |
b163ae0
to
7e8a03d
Compare
Is it supposed to say "this workflow requires approval from a maintainer"? |
I believe the issue may have been a merge conflict caused by a recent upstream commit that moved intrinsic.rs (which is the file being modified here) to another location. I think in theory the problem should be fixed now @tautschnig |
Yes, we have opted for those rules out of caution. |
It looks like those are still present. |
Ah yes I see what you meant, it looks like there were some changes to library/stdarch. I've reverted those changes, so I think everything should be good to go now 👍 |
@tautschnig can we get the ci to run? |
CI run has been approved and is in progress. |
Requesting a review again... |
library/core/src/intrinsics/mod.rs
Outdated
@@ -4537,6 +4537,32 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize | |||
) | |||
} | |||
|
|||
#[requires(crate::mem::size_of::<T>() >= crate::mem::size_of::<U>())] //U cannot be larger than T | |||
#[ensures(|ret: &U| (ret as *const U as usize) % crate::mem::align_of::<U>() == 0)] //check that the output has expected alignment | |||
pub unsafe fn transmute_unchecked_wrapper<T,U>(input: T) -> U { |
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.
Can you please add a comment on why we need this and make this function private?
library/core/src/intrinsics/mod.rs
Outdated
//This requires means [output is char implies input is valid unicode value] | ||
#[requires(type_name::<U>() != type_name::<char>() || (input <= T::from(0xD7FF) || (input >= T::from(0xE000) && input <= T::from(0x10FFFF)) ))] | ||
#[ensures(|ret: &U| (ret as *const U as usize) % crate::mem::align_of::<U>() == 0)] | ||
pub unsafe fn transmute_unchecked_from_u32<T,U>(input: T) -> U |
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 we want to generate type specific wrappers.
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.
Thank you for your comments! @celinval
One thing I was wondering, we couldn't find any ways to verify type-specific properties without putting constraints on the input/output types (hence the type-specific wrappers). In that case, would it be better to leave all type-specific validity checks to the "-Z valid-value-checks
" logic, and to only verify generic properties in the generic wrapper (e.g., #[ensures(|ret: &U| (ub_checks::can_dereference(ret as *const U)))]
)?
library/core/src/intrinsics/mod.rs
Outdated
@@ -4537,6 +4537,32 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize | |||
) | |||
} | |||
|
|||
#[requires(crate::mem::size_of::<T>() >= crate::mem::size_of::<U>())] //U cannot be larger than T |
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.
We don't need to size requirement since this is a compilation error according to the transmute
documentation. We do need to state that input T
is a valid U
value. One possibility would be to use the can_dereference
method from ub_checks
.
However, with Kani, the value validity checks are still incomplete (see model-checking/kani#3203) for current limitations.
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 created some new commits with these suggestions in mind. The only thing I left unchanged for now is the check for equality in the sizes of the input and output, since according to the documentation of transmute_unchecked
, it seems that the compiler allows the inputs and outputs to have different sizes (as opposed to transmute
), but considers this to be UB.
b86e230
to
5da586f
Compare
@AlexLB99 Was closing this PR an intentional act? |
Yes, temporarily -- I was resolving some merge conflicts (so I started by deleting the previous commits, which automatically closes the pr). I also pushed some new changes to reflect the previous comments. |
Hi, I'm just following up to see if anyone would be able to review this pr, to see if everything is on the right track (@celinval or anyone else interested). Thanks in advance! |
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.
It looks much better. I think the main adjustment is to add a value validity clause as a pre-condition.
library/core/src/intrinsics/mod.rs
Outdated
@@ -4592,6 +4592,14 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize | |||
) | |||
} | |||
|
|||
//We need this wrapper because transmute_unchecked is an intrinsic, for which Kani does not currently support contracts |
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, we could move this to be inside the verify module, right?
library/core/src/intrinsics/mod.rs
Outdated
@@ -4592,6 +4592,14 @@ pub(crate) const fn miri_promise_symbolic_alignment(ptr: *const (), align: usize | |||
) | |||
} | |||
|
|||
//We need this wrapper because transmute_unchecked is an intrinsic, for which Kani does not currently support contracts | |||
#[requires(crate::mem::size_of::<T>() == crate::mem::size_of::<U>())] //T and U have same size (transmute_unchecked does not guarantee this) | |||
#[ensures(|ret: &U| (ub_checks::can_dereference(ret as *const U)))] //output can be deref'd as value of type U |
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.
You actually want check value validity as a precondition. Since the return is by value, it would be immediate UB to return an invalid value.
library/core/src/intrinsics/mod.rs
Outdated
#[kani::proof_for_contract(transmute_unchecked_wrapper)] | ||
fn transmute_u32_to_char() { | ||
let num: u32 = kani::any(); | ||
kani::assume((num <= 0xD7FF) || (num >= 0xE000 && num <= 0x10FFFF)); |
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.
Can you please remove this assumption. This should be part of the precondition of the function.
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.
Be aware that the value validity check is not currently enabled in this repository.
} | ||
|
||
//this doesn't compile, A and B have different sizes due to padding | ||
/*#[kani::proof_for_contract(transmute_unchecked_wrapper)] |
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 believe this is a bug in Kani. The following test compiles with rustc.
#![feature(core_intrinsics)]
use std::intrinsics::transmute_unchecked;
#[repr(C)]
struct A {
x: u8,
y: u16,
z: u8,
}
#[repr(C)]
struct B {
x: u8,
y: u8,
z: u16,
}
//this doesn't compile, A and B have different sizes due to padding
fn main() {
let a = A { x: 0, y: 0, z: 0 };
let x = a.x;
let b: B = unsafe { transmute_unchecked(a) };
assert!(b.x == x);
}
Can you please create an issue in Kani and update this comment?
z: u8, | ||
} | ||
|
||
#[repr(C)] |
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.
Are you able to catch issues where the structure representation is not C
?
This is a draft pull request towards solving #19.
Changes
transmute_unchecked()
Note: the reason we write wrappers for
transmute_unchecked()
and we annotate those wrappers is that function contracts do not appear to be currently supported for compiler intrinsics (as discussed in #3345). Also, rather than using a single wrapper fortransmute_unchecked()
, we write several with different constraints on the input (since leaving the function parameters completely generic severely restricts what we can do in the contracts, e.g., testing for equality).This is not intended to be a complete solution for verifying
transmute_unchecked()
, but instead a proof of concept to see how aligned this is with the expected solution. Any feedback would be greatly appreciated -- thank you!By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.