-
Notifications
You must be signed in to change notification settings - Fork 97
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
Audit for ptr_guaranteed_<cmp>
#1090
Audit for ptr_guaranteed_<cmp>
#1090
Conversation
d036af4
to
17a9819
Compare
docs/src/rust-feature-support.md
Outdated
ptr_guaranteed_eq | Partial | | | ||
ptr_guaranteed_ne | Partial | | | ||
ptr_guaranteed_eq | Yes | Over-approximation: Returns nondet. value even if comparison is true | | ||
ptr_guaranteed_ne | Yes | Over-approximation: Returns nondet. value even if comparison is true | |
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.
Should one of these say "false"
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.
No, these are not symmetric in the sense that if one returns true, the other one returns false. Under some circumstances, both may return false even if one of them would return true using standard (in)equality, which is the desired behavior. See https://doc.rust-lang.org/std/primitive.pointer.html#method.guaranteed_eq for more details.
tests/kani/Repr/main_fixme.rs
Outdated
@@ -17,5 +17,8 @@ fn mmap() -> *mut MyCVoid { | |||
fn main() { | |||
let v = mmap(); | |||
assert!(v != MAP_FAILED); | |||
// The assertion below fails because it must be using `ptr_guaranteed_eq` or |
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.
Why must it?
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 test broke because the intrinsics changed here are less precise now. This is just a hint I wanted to include for when we come back to fix the test.
// equal, which causes the test to pass in the `else` branch. The `if` | ||
// branch is unreachable. |
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.
Should we test that this branch is unreachable, maybe with an assert false?
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.
Because we are assuming ptr1 == ptr2
, the unreachable assertion is in practice assert!(false)
.
if ptr_guaranteed_eq(ptr1, ptr2) { | ||
assert!(ptr1 == ptr2); | ||
} else { | ||
assert!(ptr1 != ptr2); |
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 use expect_fail instead? Tests with kani-verify-fail would succeed even if CBMC crashes.
|
||
#[kani::proof] | ||
fn test_ptr_eq(ptr1: *const u8, ptr2: *const u8) { | ||
kani::assume(ptr1 == ptr2); |
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 would remove this assumption. The assertion on line 14 should still hold.
|
||
#[kani::proof] | ||
fn test_ptr_eq(ptr1: *const u8, ptr2: *const u8) { | ||
kani::assume(ptr1 != ptr2); |
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.
If you use expect_fail()
this test can be merged with the one above.
tests/kani/Repr/main_fixme.rs
Outdated
@@ -17,5 +17,8 @@ fn mmap() -> *mut MyCVoid { | |||
fn main() { | |||
let v = mmap(); | |||
assert!(v != MAP_FAILED); | |||
// The assertion below fails because it must be using `ptr_guaranteed_eq` or | |||
// `ptr_guaranteed_eq`, which now returns a nondet. value if the result of | |||
// the comparison is true | |||
assert!(v.is_null()); |
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.
Humm... let's sync offline. We might have to revert the behavior. We don't want ptr::is_null()
to become nondet. :(
befd424
to
2760dc8
Compare
The PR is ready to review again. We had a discussion where we agreed that these intrinsics should be implemented as regular comparison instead of being over-approximated, otherwise we get nondet. results for comparison like |
@@ -0,0 +1,15 @@ | |||
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | |||
// SPDX-License-Identifier: Apache-2.0 OR MIT | |||
// kani-verify-fail |
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.
Is kani-verify-fail
required?
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 was not needed, but I changed the tests to have (each) two positive harnesses instead.
// are used for regular comparison in `std` when they shouldn't. An earlier | ||
// version that returned nondet. values when the result of the comparison |
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.
because they are used for regular comparison in
std
when they shouldn't
This sounds a bit too strong for me. The documentation (https://doc.rust-lang.org/beta/std/primitive.pointer.html#method.guaranteed_eq) says that this may spuriously return false
in compile-time evaluation, which I understood to mean that it may block some optimizations, etc., but it can never impact correctness since it behaves like ==
/!=
at run-time (which is what we're modeling).
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.
Thanks for making this clear! Removed the comments here and in Kani docs.
fn test_ptr_eq(ptr1: *const u8, ptr2: *const u8) { | ||
kani::assume(ptr1 != ptr2); | ||
assert!(ptr_guaranteed_ne(ptr1, ptr2)); | ||
kani::expect_fail(ptr1 == ptr2, "Pointers aren't equal"); |
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.
Should this use !ptr_guaranteed_ne
? And will it even be reachable given how we turn assert
into assert; assume
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 changed the tests to have (each) two positive harnesses instead, please check.
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.
Thanks @adpaco-aws!
Merging this PR now, but if you have any other concerns please let me know and I'll take care of them. |
Description of changes:
Completes the audit for
ptr_guaranteed_<cmp>
. These behave as regular pointer comparison at runtime.Resolved issues:
Part of #727
Call-outs:
Testing:
How is this change tested? Adds 2 tests.
Is this a refactor change? No.
Checklist
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 and MIT licenses.