Skip to content

Add harnesses for all public functions of Layout #43

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

Merged
merged 23 commits into from
Oct 2, 2024
Merged
Changes from 4 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
96f1603
Add harnesses for all public functions of `Layout`
tautschnig Jul 23, 2024
617ba54
Fix syntax errors
tautschnig Jul 23, 2024
6a6bd64
Add some ensures clauses
tautschnig Jul 26, 2024
08d6f58
Fix syntax errors, add ensures
tautschnig Jul 30, 2024
ac03385
Merge remote-tracking branch 'origin/main' into layout-harnesses
tautschnig Aug 8, 2024
8c21272
Add more ensures clauses
tautschnig Aug 8, 2024
0b62529
Wrong clause was caught
tautschnig Aug 9, 2024
e331419
Avoid multiplication
tautschnig Aug 9, 2024
45505ed
Add assumptions
tautschnig Aug 9, 2024
4156263
Fixed contracts
tautschnig Aug 9, 2024
5231a68
Try disabling one more clause
tautschnig Aug 9, 2024
0428277
Merge remote-tracking branch 'origin/main' into layout-harnesses
tautschnig Aug 20, 2024
f3bf6aa
Merge remote-tracking branch 'origin/main' into layout-harnesses
tautschnig Aug 20, 2024
8e0c75a
Use Arbitrary, add contracts
tautschnig Aug 20, 2024
41635f0
More on for_value, for_value_raw
tautschnig Aug 20, 2024
10493b5
add size constraints
tautschnig Aug 20, 2024
c534529
Update library/core/src/alloc/layout.rs
tautschnig Aug 21, 2024
70a0fcc
Merge remote-tracking branch 'origin/main' into layout-harnesses
tautschnig Aug 21, 2024
e1c598c
Cleanup contracts
tautschnig Aug 21, 2024
3cab463
Merge remote-tracking branch 'origin/main' into layout-harnesses
tautschnig Sep 16, 2024
8013417
Try to use Invariant
tautschnig Sep 16, 2024
2e44a57
Revert "Try to use Invariant"
tautschnig Oct 2, 2024
8acb587
Merge remote-tracking branch 'origin/main' into layout-harnesses
tautschnig Oct 2, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
216 changes: 215 additions & 1 deletion library/core/src/alloc/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// collections, resulting in having to optimize down excess IR multiple times.
// Your performance intuition is useless. Run perf.

use safety::requires;
use safety::{ensures, requires};
use crate::cmp;
use crate::error::Error;
use crate::fmt;
Expand Down Expand Up @@ -71,6 +71,7 @@ impl Layout {
#[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")]
#[inline]
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
#[ensures(|result| result.is_err() || align.is_power_of_two())]
pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError> {
if !align.is_power_of_two() {
return Err(LayoutError);
Expand Down Expand Up @@ -145,6 +146,7 @@ impl Layout {
without modifying the layout"]
#[inline]
#[rustc_allow_const_fn_unstable(ptr_alignment_type)]
#[ensures(|result| result.is_power_of_two())]
pub const fn align(&self) -> usize {
self.align.as_usize()
}
Expand All @@ -169,6 +171,7 @@ impl Layout {
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[must_use]
#[inline]
#[ensures(|result| result.align().is_power_of_two())]
pub const fn for_value<T: ?Sized>(t: &T) -> Self {
let (size, align) = (mem::size_of_val(t), mem::align_of_val(t));
// SAFETY: see rationale in `new` for why this is using the unsafe variant
Expand Down Expand Up @@ -203,6 +206,7 @@ impl Layout {
#[unstable(feature = "layout_for_ptr", issue = "69835")]
#[rustc_const_unstable(feature = "const_alloc_layout", issue = "67521")]
#[must_use]
#[ensures(|result| result.align().is_power_of_two())]
pub const unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self {
// SAFETY: we pass along the prerequisites of these functions to the caller
let (size, align) = unsafe { (mem::size_of_val_raw(t), mem::align_of_val_raw(t)) };
Expand All @@ -220,6 +224,7 @@ impl Layout {
#[rustc_const_unstable(feature = "alloc_layout_extra", issue = "55724")]
#[must_use]
#[inline]
#[ensures(|result| result.is_aligned())]
pub const fn dangling(&self) -> NonNull<u8> {
// SAFETY: align is guaranteed to be non-zero
unsafe { NonNull::new_unchecked(crate::ptr::without_provenance_mut::<u8>(self.align())) }
Expand All @@ -241,6 +246,7 @@ impl Layout {
/// `align` violates the conditions listed in [`Layout::from_size_align`].
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
#[inline]
#[ensures(|result| result.is_err() || result.as_ref().unwrap().align() >= align)]
pub fn align_to(&self, align: usize) -> Result<Self, LayoutError> {
Layout::from_size_align(self.size(), cmp::max(self.align(), align))
}
Expand All @@ -266,6 +272,7 @@ impl Layout {
#[must_use = "this returns the padding needed, \
without modifying the `Layout`"]
#[inline]
#[ensures(|result| *result <= align)]
pub const fn padding_needed_for(&self, align: usize) -> usize {
let len = self.size();

Expand Down Expand Up @@ -302,6 +309,7 @@ impl Layout {
#[must_use = "this returns a new `Layout`, \
without modifying the original"]
#[inline]
#[ensures(|result| result.size() % result.align() == 0)]
pub const fn pad_to_align(&self) -> Layout {
let pad = self.padding_needed_for(self.align());
// This cannot overflow. Quoting from the invariant of Layout:
Expand All @@ -324,6 +332,7 @@ impl Layout {
/// On arithmetic overflow, returns `LayoutError`.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
#[inline]
#[ensures(|result| result.is_err() || result.as_ref().unwrap().1 % n == 0)]
pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
// This cannot overflow. Quoting from the invariant of Layout:
// > `size`, when rounded up to the nearest multiple of `align`,
Expand Down Expand Up @@ -384,6 +393,7 @@ impl Layout {
/// ```
#[stable(feature = "alloc_layout_manipulation", since = "1.44.0")]
#[inline]
#[ensures(|result| result.is_err() || result.as_ref().unwrap().1 <= result.as_ref().unwrap().0.size())]
pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError> {
let new_align = cmp::max(self.align, next.align);
let pad = self.padding_needed_for(next.align());
Expand All @@ -410,6 +420,7 @@ impl Layout {
/// On arithmetic overflow, returns `LayoutError`.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
#[inline]
#[ensures(|result| result.is_err() || result.as_ref().unwrap().size() % n == 0)]
pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {
let size = self.size().checked_mul(n).ok_or(LayoutError)?;
// The safe constructor is called here to enforce the isize size limit.
Expand All @@ -424,6 +435,7 @@ impl Layout {
/// On arithmetic overflow, returns `LayoutError`.
#[unstable(feature = "alloc_layout_extra", issue = "55724")]
#[inline]
#[ensures(|result| result.is_err() || result.as_ref().unwrap().size() <= next.size())]
pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {
let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?;
// The safe constructor is called here to enforce the isize size limit.
Expand Down Expand Up @@ -503,6 +515,22 @@ impl fmt::Display for LayoutError {
mod verify {
use super::*;

// pub const fn from_size_align(size: usize, align: usize) -> Result<Self, LayoutError>
#[kani::proof_for_contract(Layout::from_size_align)]
pub fn check_from_size_align() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

if let Ok(layout) = Layout::from_size_align(s, a) {
assert_eq!(layout.size(), s);
assert_eq!(layout.align(), a);
assert!(a > 0);
assert!(a.is_power_of_two());
assert!(s <= isize::MAX as usize - (a - 1));
}
}

// pub const unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self
#[kani::proof_for_contract(Layout::from_size_align_unchecked)]
pub fn check_from_size_align_unchecked() {
let s = kani::any::<usize>();
Expand All @@ -514,4 +542,190 @@ mod verify {
assert_eq!(layout.align(), a);
}
}

// pub const fn size(&self) -> usize
#[kani::proof]
pub fn check_size() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
assert_eq!(layout.size(), s);
}
}

// pub const fn align(&self) -> usize
#[kani::proof_for_contract(Layout::align)]
pub fn check_align() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
assert!(layout.align().is_power_of_two());
}
}

// pub const fn new<T>() -> Self
#[kani::proof]
pub fn check_new_i32() {
let layout = Layout::new::<i32>();
assert_eq!(layout.size(), 4);
assert!(layout.align().is_power_of_two());
}

// pub const fn for_value<T: ?Sized>(t: &T) -> Self
#[kani::proof_for_contract(Layout::for_value)]
pub fn check_for_value_i32() {
let array : [i32; 2] = [1, 2];
let layout = Layout::for_value::<[i32]>(&array[1 .. 1]);
assert!(layout.align().is_power_of_two());
}

// pub const unsafe fn for_value_raw<T: ?Sized>(t: *const T) -> Self
#[kani::proof_for_contract(Layout::for_value_raw)]
pub fn check_for_value_raw_i32() {
unsafe {
let layout = Layout::for_value_raw::<[i32]>(&[] as *const [i32]);
assert!(layout.align().is_power_of_two());
}
}

// pub const fn dangling(&self) -> NonNull<u8>
#[kani::proof_for_contract(Layout::dangling)]
pub fn check_dangling() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
assert!(layout.dangling().is_aligned());
}
}

// pub fn align_to(&self, align: usize) -> Result<Self, LayoutError>
#[kani::proof_for_contract(Layout::align_to)]
pub fn check_align_to() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
let a2 = kani::any::<usize>();
if let Ok(layout2) = layout.align_to(a2) {
assert!(layout2.align() > 0);
assert!(layout2.align().is_power_of_two());
}
}
}

// pub const fn padding_needed_for(&self, align: usize) -> usize
#[kani::proof_for_contract(Layout::padding_needed_for)]
pub fn check_padding_needed_for() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
let a2 = kani::any::<usize>();
if(a2.is_power_of_two() && a2 <= a) {
let p = layout.padding_needed_for(a2);
assert!(p <= a2);
}
}
}

// pub const fn pad_to_align(&self) -> Layout
#[kani::proof_for_contract(Layout::pad_to_align)]
pub fn check_pad_to_align() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
let layout2 = layout.pad_to_align();
assert_eq!(layout2.size() % a, 0);
assert_eq!(layout.size() + layout.padding_needed_for(layout.align()), layout2.size());
}
}

// pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError>
#[kani::proof_for_contract(Layout::repeat)]
pub fn check_repeat() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
let n = kani::any::<usize>();
if let Ok((layout2, padded_size)) = layout.repeat(n) {
assert!(n == 0 || layout2.size() >= s);
assert_eq!(layout2.size(), n * padded_size);
}
}
}

// pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutError>
#[kani::proof_for_contract(Layout::extend)]
pub fn check_extend() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
let s2 = kani::any::<usize>();
let a2 = kani::any::<usize>();
let layout2 = Layout::from_size_align_unchecked(s2, a2);
if let Ok((layout3, offset)) = layout.extend(layout2) {
assert_eq!(layout3.align(), cmp::max(a, a2));
assert!(layout3.size() >= s + s2);
assert!(offset >= s);
assert!(offset <= layout3.size());
}
}
}

// pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError>
#[kani::proof_for_contract(Layout::repeat_packed)]
pub fn check_repeat_packed() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
let n = kani::any::<usize>();
if let Ok(layout2) = layout.repeat_packed(n) {
assert!(n == 0 || layout2.size() >= s);
}
}
}

// pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError>
#[kani::proof_for_contract(Layout::extend_packed)]
pub fn check_extend_packed() {
let s = kani::any::<usize>();
let a = kani::any::<usize>();

unsafe {
let layout = Layout::from_size_align_unchecked(s, a);
let s2 = kani::any::<usize>();
let a2 = kani::any::<usize>();
let layout2 = Layout::from_size_align_unchecked(s2, a2);
if let Ok(layout3) = layout.extend_packed(layout2) {
assert_eq!(layout3.align(), a);
assert_eq!(layout3.size(), s + s2);
}
}
}

// pub const fn array<T>(n: usize) -> Result<Self, LayoutError>
#[kani::proof]
pub fn check_array_i32() {
let n = kani::any::<usize>();
if let Ok(layout) = Layout::array::<i32>(n) {
assert!(layout.size() >= n * 4);
assert!(layout.align().is_power_of_two());
}
}
}
Loading