Skip to content
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

Challenge 3: Verifying Raw Pointer Arithmetic Operations #76

Open
feliperodri opened this issue Sep 5, 2024 · 9 comments · May be fixed by #212
Open

Challenge 3: Verifying Raw Pointer Arithmetic Operations #76

feliperodri opened this issue Sep 5, 2024 · 9 comments · May be fixed by #212
Assignees
Labels
Challenge Used to tag a challenge

Comments

@feliperodri
Copy link

Tracking issue for Challenge 3: Verifying Raw Pointer Arithmetic Operations.

@feliperodri feliperodri added the Challenge Used to tag a challenge label Sep 5, 2024
@unseenmars
Copy link

Here’s Kai from CMU III. We’re taking on this challenge and will keep you updated on our progress along the way.

@szlee118
Copy link

Problem 1: Defining Valid Input Space for Pointer Parameters (solved)

Challenge:

Our main challenge in writing function contracts is precisely defining the valid input space for pointer parameters. Specifically, we are struggling to express accurate kani::requires attributes for pointer parameters in the functions we need to verify.

For non-pointer inputs like u64, defining the valid input space is straightforward. For example, in the gcd(x, y) function, the valid input space can be represented by a precondition like #[kani::requires(x != 0 && y != 0)]. However, for pointer parameters, defining the valid input space is non-trivial. We need a way to exclude invalid pointers (such as dangling, misaligned, or out-of-bounds pointers) while covering all valid ones to ensure the function contract remains sound. The challenge lies in expressing these conditions using kani::requires attributes.

So far, we haven’t found a feasible solution or a Kani API that directly addresses this problem. Could you provide guidance on how we can define preconditions for pointer parameters in our function contracts?

Answer:

There’s an API called can_dereference that can be used for this purpose: can_dereference API
You can see an example of its usage in this test: valid_ptr.rs

Problem 2: Handling Undefined Behavior in Function Verification (solved)

Challenge:

We are also encountering an issue related to undefined behavior (UB) in the verification of pointer arithmetic functions, such as *const T::offset. If the function contains potential safety issues, such as valid inputs that trigger UB, Kani (as documented here: Kani undefined behavior documentation) cannot perform checks once UB occurs. This creates a dilemma: we need to verify whether the function may cause UB, but Kani’s checks become ineffective in the presence of UB. Could you advise on how we can approach this situation?

Answer:

Kani automatically checks for certain types of UB (e.g. a dereference operation is valid), and verification will fail if one of those types of UB were found in a function. For other properties, such as this one mentioned in the documentation of offset:

the entire memory range between self and the result must be in bounds of that allocated object.

One needs to express this property as a requires clause that the function is annotated with.

@xsxszab
Copy link

xsxszab commented Sep 14, 2024

Problem 3: Determining the valid value range for *const T::offset(self, count: i32)'s count parameter

To write a function contract for this function, we need to add a precondition that defines the valid input range for the count parameter. However, we cannot infer this information solely based on the input pointer. For example, consider the following pointers:

let nums = vec![0i32, 1, 2, 3];
let n = 42;
let ptr1: *const i32 = nums.as_ptr();
let ptr2: *const i32 = ptr1.wrapping_offset(1);
let ptr3: *const i32 = &n;

All of these pointers are of type *const i32—the only information we know when inside the offset function. However, when we call the .offset(count) method on them, the valid range of count differs for each pointer. Thus, it becomes unclear how to properly write the kani::requires attribute for the count parameter.

Is there a way to resolve this issue?

Problem 4: determing whether two raw pointers point to the same object during runtime

To implement function contracts for *const T::offset_from(self, origin: *const T), we need to guarantee that both self and origin point to the same allocated object. Is there any way to access and compare the provenance information of these raw pointers to guarantee that they belong to the same object?

@stogaru
Copy link

stogaru commented Sep 20, 2024

@feliperodri , the other two people working on this issue are @stogaru and @MayureshJoshi25.

@MayureshJoshi25
Copy link

Mayuresh here! Would be working on this challenge and would report any problems/doubts on the way.

@xsxszab
Copy link

xsxszab commented Sep 25, 2024

Problem 5: Calling Pointer Arithmetic Methods on dyn Trait Pointers

Calling pointer arithmetic methods, such as add or sub, requires the pointee type to be sized. However, dyn Trait objects are inherently unsized, resulting in a compile-time error when attempting to call these methods on a pointer to a dyn Trait. For example:

struct ConcreteType {
    size: i32,   
}

trait AbstractTrait {}

impl AbstractTrait for ConcreteType {} 

fn get_box(size: i32) -> Box<dyn AbstractTrait> {
    Box::new(ConcreteType { size })
}

fn test() {
    let rand_box = get_box(42);
    let ptr = Box::into_raw(rand_box);
    unsafe { let ptr1 = ptr.offset(0); } // error
}

In our challenge, we need to verify function contracts for at least one dyn Trait type, but we are blocked by this issue. Is there a workaround for handling this scenario?

@xsxszab
Copy link

xsxszab commented Oct 8, 2024

Problem 6: Precondition #[requires(kani::mem::can_dereference(self))] Fails on function <*mut T>::byte_add

Issue:
When adding the precondition #[requires(kani::mem::can_dereference(self))] to the function byte_add (pub const unsafe fn byte_add(self, count: usize) -> Self), running Kani produces the following error:

error[E0277]: the trait bound `<T as ptr::metadata::Pointee>::Metadata: kani::mem::PtrProperties<T>` is not satisfied
   --> /Users/xsxsz/vscode-projects/verify-rust-std/library/core/src/ptr/mut_ptr.rs:440:43
    |
440 |     #[requires(kani::mem::can_dereference(self))]
    |                -------------------------- ^^^^ the trait `kani::mem::PtrProperties<T>` is not implemented for `<T as ptr::metadata::Pointee>::Metadata`
    |                |
    |                required by a bound introduced by this call
    |
note: required by a bound in `kani::mem::can_dereference`
   --> /Users/xsxsz/vscode-projects/verify-rust-std/library/core/src/lib.rs:426:1
    |
426 | kani_core::kani_lib!(core);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^
    | |
    | required by a bound in this function
    | required by this bound in `can_dereference`
    = note: this error originates in the macro `kani_core::kani_mem` which comes from the expansion of the macro `kani_core::kani_lib` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider further restricting the associated type
    |
444 |     pub const unsafe fn byte_offset(self, count: isize) -> Self where <T as ptr::metadata::Pointee>::Metadata: kani::mem::PtrProperties<T> {
    |                                                                 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

error: aborting due to 1 previous error

Context:
This precondition works for other non-byte pointer methods including add, sub, offset, and offset_from. However, it consistently fails with the same error on all byte-prefixed functions, including byte_add, byte_sub, byte_offset, and byte_offset_from. Additionally, this behavior is consistent on both const and mutpointer methods.

We are currently using a custom-built version of Kani from the features/verify-rust-std branch, not the stable release. This branch is necessary for us as it enables verification on primitive types.

Question:
Is this issue a bug of the branch we are using, or is it a feature that has yet to be implemented in Kani? We're uncertain if this behavior is a bug or an expected result.

@xsxszab
Copy link

xsxszab commented Oct 11, 2024

Hi @feliperodri, after contacting with Team 4, we have a few updates on the issues we've been discussing in today's (Oct. 11) meeting:

  • For Problem 3 and 4, We’ve confirmed that Zyad, Team 4’s POC, is already working on new Kani APIs to address these issues.
  • Regarding the new problem mentioned in today’s meeting: We initially discovered this problem during our joint meeting with Team 4 on Oct. 9, and they have received clarification during their sponsor meeting, so we don't need to post a new Problem here. Specifically, a pointer that points one byte past the end of its allocated object is still valid, but cannot be dereferenced. For example, vec.as_ptr().add(vec.len()) is a valid pointer, but dereferencing it would result in undefined behavior.

@stogaru
Copy link

stogaru commented Nov 14, 2024

It is not possible to verify offset, offset_from, add and sub for the following dynamically sized types:

  • slices
  • dyn Traits

Function signatures:

Observe that the T needs to sized for all pointers *mut T or *const T invoking these functions as per the function signatures.

add:

pub const unsafe fn add(self, count: usize) -> Self
where
        T: Sized

The same applies for sub, offset and offset_from.

Invoking above methods:

Slices

Code:

fn main() {
    let arr = [1, 2, 3, 4, 5];
    let slice = &arr[..];
    let slice_ptr: *const [u32] = slice.as_ptr() as *const [u32];
    let new_slice_ptr: *const [u32] = unsafe { slice_ptr.offset(1) };
}

Error as expected:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `[u32]` cannot be known at compilation time
   --> src/main.rs:29:58
    |
29  |     let new_slice_ptr: *const [u32] = unsafe { slice_ptr.offset(1) };
    |                                                          ^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `[u32]`
note: required by a bound in `std::ptr::const_ptr::<impl *const T>::offset`
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/const_ptr.rs:393:12
    |
391 |     pub const unsafe fn offset(self, count: isize) -> *const T
    |                         ------ required by a bound in this associated function
392 |     where
393 |         T: Sized,
    |            ^^^^^ required by this bound in `std::ptr::const_ptr::<impl *const T>::offset`

dyn Traits

Code:

trait Greet {
    fn greet(&self);
}

struct Person {
    name: &'static str,
}

impl Greet for Person {
    fn greet(&self) {
        println!("Hello, my name is {}!", self.name);
    }
}

fn main() {
    let person = Person { name: "Alice" };
    let greet_ptr: *const dyn Greet = &person as *const dyn Greet;
    unsafe {
        greet_ptr.offset(1);
    }
}

Error as expected:

error[E0277]: the size for values of type `dyn Greet` cannot be known at compilation time
   --> src/main.rs:19:19
    |
19  |         greet_ptr.offset(1);
    |                   ^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `dyn Greet`
note: required by a bound in `std::ptr::const_ptr::<impl *const T>::offset`
   --> /playground/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/const_ptr.rs:393:12
    |
391 |     pub const unsafe fn offset(self, count: isize) -> *const T
    |                         ------ required by a bound in this associated function
392 |     where
393 |         T: Sized,
    |            ^^^^^ required by this bound in `std::ptr::const_ptr::<impl *const T>::offset`

feliperodri pushed a commit that referenced this issue Nov 22, 2024
Towards #76 

### Changes

* Adds contracts for `<*const T>::offset_from`.
* Adds harnesses for the function verifying:
    * All integer types
    * Tuples (composite types)
    * Unit Type
* Accomplishes this using a macro.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.

---------

Co-authored-by: Carolyn Zech <[email protected]>
feliperodri pushed a commit that referenced this issue Nov 27, 2024
Towards #76 

### Changes
* Adds contracts for `<*mut T>::add`, `<*mut T>::sub` and `<*mut
T>::offset`
* Adds proofs for contracts of the above functions verifying the pointee
types:
  * All integer types
  * Tuples (composite type)
  * Unit Type
* Defines a macro for add and sub and another for offset.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.

---------

Co-authored-by: Yifei Wang <[email protected]>
Co-authored-by: MayureshJoshi25 <[email protected]>
Co-authored-by: Yifei Wang <[email protected]>
tautschnig pushed a commit that referenced this issue Dec 3, 2024
Towards #76 

**Summary**
* Adds contracts for <*const T>::add, sub and offset.
* Adds proof for contracts for above methods, verifying following
pointee types:
    * All integer types
    * Tuples (representing composite types)
    * Slices
    * Unit type
carolynzech pushed a commit that referenced this issue Dec 5, 2024
…169)

Towards #76

### Changes
* Adds contracts for `<*mut T>::byte_add`, `<*mut T>::byte_sub` and
`<*mut T>::byte_offset`.
* Adds harnesses for the function verifying the following pointee types:
    * All integer types
    * Tuples (composite types)
    * Unit Type
    * Slices
* Accomplishes this using a few macros.

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.

---------

Co-authored-by: Yifei Wang <[email protected]>
Co-authored-by: Yifei Wang <[email protected]>
feliperodri added a commit that referenced this issue Dec 5, 2024
- Added contracts for offset_from (mut type);
- Accomplished using a macro which generates harnesses;
- Verifies: int types, unit, tuples (composite types).

Towards #76

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 and MIT licenses.

---------

Co-authored-by: stogaru <[email protected]>
Co-authored-by: Carolyn Zech <[email protected]>
Co-authored-by: Felipe R. Monteiro <[email protected]>
feliperodri added a commit that referenced this issue Dec 9, 2024
…nd `byte_offset` (#188)

Towards #76 

This pull request impelments proof for contracts for `byte_add`,
`byte_sub` and `byte_offset` for `<dyn Trait>`. Both `const` and `mut`
versions are included.

It serves as an addition to an existing
[PR](#169) but is
submitted separately to avoid disrupting the ongoing review process for
that PR.

---------

Co-authored-by: Surya Togaru <[email protected]>
Co-authored-by: stogaru <[email protected]>
Co-authored-by: Felipe R. Monteiro <[email protected]>
tautschnig pushed a commit that referenced this issue Dec 10, 2024
Towards #76

This pull request implements proof for contracts for `byte_offset_from`
verifying `dyn Trait` pointee types. Both const and mut versions are
included.
tautschnig pushed a commit that referenced this issue Dec 10, 2024
Towards #76


## Changes
### Contracts Added:

- Added contracts for <*mut T>::add, <*mut T>::sub, and <*mut T>::offset
operations for raw pointers to slices.
### Proofs Implemented:

- Added proofs for the above contracts, verifying safety for the
following slice pointee types using arrays:
 - All integer types (i8, i16, i32, etc.).
 - Tuples (composite types) (e.g., (i8, i8), (i32, f64, bool)).
 - Unit type (()).
 
### Macro Definitions:
- Introduced macros to simplify and automate harness generation:
 - One macro for add and sub operations on slices.
 - Another macro for offset operations on slices.
 
### Array-Based Implementation for Slice Verification:
- Arrays are used as a proxy for slices (as slices do not have a known
length at compile time) to enable proper bounds checking and assumptions
during verification.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Challenge Used to tag a challenge
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants