Skip to content

Commit

Permalink
Move test_shrink_to_unwind to its own file.
Browse files Browse the repository at this point in the history
This way, no other test can be tripped up by `test_shrink_to_unwind` changing the alloc error hook.
  • Loading branch information
Sp00ph committed May 7, 2024
1 parent ffe8510 commit 5cb53bc
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 55 deletions.
4 changes: 4 additions & 0 deletions library/alloc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ rand_xorshift = "0.3.0"
name = "alloctests"
path = "tests/lib.rs"

[[test]]
name = "vec_deque_alloc_error"
path = "tests/vec_deque_alloc_error.rs"

[[bench]]
name = "allocbenches"
path = "benches/lib.rs"
Expand Down
55 changes: 1 addition & 54 deletions library/alloc/src/collections/vec_deque/tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
#![feature(alloc_error_hook)]

use crate::alloc::{AllocError, Layout};
use core::{iter::TrustedLen, ptr::NonNull};
use std::{
alloc::{set_alloc_error_hook, take_alloc_error_hook, System},
panic::{catch_unwind, AssertUnwindSafe},
};
use core::iter::TrustedLen;

use super::*;

Expand Down Expand Up @@ -797,52 +790,6 @@ fn test_shrink_to() {
}
}

#[test]
fn test_shrink_to_unwind() {
// This tests that `shrink_to` leaves the deque in a consistent state when
// the call to `RawVec::shrink_to_fit` unwinds. The code is adapted from #123369
// but changed to hopefully not have any UB even if the test fails.

struct BadAlloc;

unsafe impl Allocator for BadAlloc {
fn allocate(&self, l: Layout) -> Result<NonNull<[u8]>, AllocError> {
// We allocate zeroed here so that the whole buffer of the deque
// is always initialized. That way, even if the deque is left in
// an inconsistent state, no uninitialized memory should be accessed.
System.allocate_zeroed(l)
}

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
unsafe { System.deallocate(ptr, layout) }
}

unsafe fn shrink(
&self,
_ptr: NonNull<u8>,
_old_layout: Layout,
_new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
Err(AllocError)
}
}

// preserve the old error hook just in case.
let old_error_hook = take_alloc_error_hook();
set_alloc_error_hook(|_| panic!("alloc error"));

let mut v = VecDeque::with_capacity_in(15, BadAlloc);
v.push_back(1);
v.push_front(2);
// This should unwind because it calls `BadAlloc::shrink` and then `handle_alloc_error` which unwinds.
assert!(catch_unwind(AssertUnwindSafe(|| v.shrink_to_fit())).is_err());
// This should only pass if the deque is left in a consistent state.
assert_eq!(v, [2, 1]);

// restore the old error hook.
set_alloc_error_hook(old_error_hook);
}

#[test]
fn test_shrink_to_fit() {
// This test checks that every single combination of head and tail position,
Expand Down
1 change: 0 additions & 1 deletion library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@
// tidy-alphabetical-start
#![cfg_attr(not(no_global_oom_handling), feature(const_alloc_error))]
#![cfg_attr(not(no_global_oom_handling), feature(const_btree_len))]
#![cfg_attr(test, feature(alloc_error_hook))]
#![cfg_attr(test, feature(is_sorted))]
#![cfg_attr(test, feature(new_uninit))]
#![feature(alloc_layout_extra)]
Expand Down
49 changes: 49 additions & 0 deletions library/alloc/tests/vec_deque_alloc_error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#![feature(alloc_error_hook, allocator_api)]

use std::{
alloc::{set_alloc_error_hook, AllocError, Allocator, Layout, System},
collections::VecDeque,
panic::{catch_unwind, AssertUnwindSafe},
ptr::NonNull,
};

#[test]
fn test_shrink_to_unwind() {
// This tests that `shrink_to` leaves the deque in a consistent state when
// the call to `RawVec::shrink_to_fit` unwinds. The code is adapted from #123369
// but changed to hopefully not have any UB even if the test fails.

struct BadAlloc;

unsafe impl Allocator for BadAlloc {
fn allocate(&self, l: Layout) -> Result<NonNull<[u8]>, AllocError> {
// We allocate zeroed here so that the whole buffer of the deque
// is always initialized. That way, even if the deque is left in
// an inconsistent state, no uninitialized memory should be accessed.
System.allocate_zeroed(l)
}

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
unsafe { System.deallocate(ptr, layout) }
}

unsafe fn shrink(
&self,
_ptr: NonNull<u8>,
_old_layout: Layout,
_new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> {
Err(AllocError)
}
}

set_alloc_error_hook(|_| panic!("alloc error"));

let mut v = VecDeque::with_capacity_in(15, BadAlloc);
v.push_back(1);
v.push_front(2);
// This should unwind because it calls `BadAlloc::shrink` and then `handle_alloc_error` which unwinds.
assert!(catch_unwind(AssertUnwindSafe(|| v.shrink_to_fit())).is_err());
// This should only pass if the deque is left in a consistent state.
assert_eq!(v, [2, 1]);
}

0 comments on commit 5cb53bc

Please sign in to comment.