Skip to content

Commit 43d07ca

Browse files
authored
Rollup merge of rust-lang#97711 - Nilstrieb:rustc-arena-ub, r=wesleywiser
Improve soundness of rustc_arena Make it runnable in miri by changing the loop iteration count for some tests in miri. Also fix a stacked borrows issue with box.
2 parents 7a9ca42 + 211fb66 commit 43d07ca

File tree

2 files changed

+39
-12
lines changed

2 files changed

+39
-12
lines changed

compiler/rustc_arena/src/lib.rs

+19-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#![feature(rustc_attrs)]
2020
#![cfg_attr(test, feature(test))]
2121
#![feature(strict_provenance)]
22+
#![feature(ptr_const_cast)]
2223

2324
use smallvec::SmallVec;
2425

@@ -27,7 +28,7 @@ use std::cell::{Cell, RefCell};
2728
use std::cmp;
2829
use std::marker::{PhantomData, Send};
2930
use std::mem::{self, MaybeUninit};
30-
use std::ptr;
31+
use std::ptr::{self, NonNull};
3132
use std::slice;
3233

3334
#[inline(never)]
@@ -55,15 +56,24 @@ pub struct TypedArena<T> {
5556

5657
struct ArenaChunk<T = u8> {
5758
/// The raw storage for the arena chunk.
58-
storage: Box<[MaybeUninit<T>]>,
59+
storage: NonNull<[MaybeUninit<T>]>,
5960
/// The number of valid entries in the chunk.
6061
entries: usize,
6162
}
6263

64+
unsafe impl<#[may_dangle] T> Drop for ArenaChunk<T> {
65+
fn drop(&mut self) {
66+
unsafe { Box::from_raw(self.storage.as_mut()) };
67+
}
68+
}
69+
6370
impl<T> ArenaChunk<T> {
6471
#[inline]
6572
unsafe fn new(capacity: usize) -> ArenaChunk<T> {
66-
ArenaChunk { storage: Box::new_uninit_slice(capacity), entries: 0 }
73+
ArenaChunk {
74+
storage: NonNull::new(Box::into_raw(Box::new_uninit_slice(capacity))).unwrap(),
75+
entries: 0,
76+
}
6777
}
6878

6979
/// Destroys this arena chunk.
@@ -72,14 +82,15 @@ impl<T> ArenaChunk<T> {
7282
// The branch on needs_drop() is an -O1 performance optimization.
7383
// Without the branch, dropping TypedArena<u8> takes linear time.
7484
if mem::needs_drop::<T>() {
75-
ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut self.storage[..len]));
85+
let slice = &mut *(self.storage.as_mut());
86+
ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(&mut slice[..len]));
7687
}
7788
}
7889

7990
// Returns a pointer to the first allocated object.
8091
#[inline]
8192
fn start(&mut self) -> *mut T {
82-
MaybeUninit::slice_as_mut_ptr(&mut self.storage)
93+
self.storage.as_ptr() as *mut T
8394
}
8495

8596
// Returns a pointer to the end of the allocated space.
@@ -90,7 +101,7 @@ impl<T> ArenaChunk<T> {
90101
// A pointer as large as possible for zero-sized elements.
91102
ptr::invalid_mut(!0)
92103
} else {
93-
self.start().add(self.storage.len())
104+
self.start().add((*self.storage.as_ptr()).len())
94105
}
95106
}
96107
}
@@ -274,7 +285,7 @@ impl<T> TypedArena<T> {
274285
// If the previous chunk's len is less than HUGE_PAGE
275286
// bytes, then this chunk will be least double the previous
276287
// chunk's size.
277-
new_cap = last_chunk.storage.len().min(HUGE_PAGE / elem_size / 2);
288+
new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / elem_size / 2);
278289
new_cap *= 2;
279290
} else {
280291
new_cap = PAGE / elem_size;
@@ -382,7 +393,7 @@ impl DroplessArena {
382393
// If the previous chunk's len is less than HUGE_PAGE
383394
// bytes, then this chunk will be least double the previous
384395
// chunk's size.
385-
new_cap = last_chunk.storage.len().min(HUGE_PAGE / 2);
396+
new_cap = (*last_chunk.storage.as_ptr()).len().min(HUGE_PAGE / 2);
386397
new_cap *= 2;
387398
} else {
388399
new_cap = PAGE;

compiler/rustc_arena/src/tests.rs

+20-4
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,11 @@ fn test_arena_alloc_nested() {
7979
#[test]
8080
pub fn test_copy() {
8181
let arena = TypedArena::default();
82-
for _ in 0..100000 {
82+
#[cfg(not(miri))]
83+
const N: usize = 100000;
84+
#[cfg(miri)]
85+
const N: usize = 1000;
86+
for _ in 0..N {
8387
arena.alloc(Point { x: 1, y: 2, z: 3 });
8488
}
8589
}
@@ -106,15 +110,23 @@ struct Noncopy {
106110
#[test]
107111
pub fn test_noncopy() {
108112
let arena = TypedArena::default();
109-
for _ in 0..100000 {
113+
#[cfg(not(miri))]
114+
const N: usize = 100000;
115+
#[cfg(miri)]
116+
const N: usize = 1000;
117+
for _ in 0..N {
110118
arena.alloc(Noncopy { string: "hello world".to_string(), array: vec![1, 2, 3, 4, 5] });
111119
}
112120
}
113121

114122
#[test]
115123
pub fn test_typed_arena_zero_sized() {
116124
let arena = TypedArena::default();
117-
for _ in 0..100000 {
125+
#[cfg(not(miri))]
126+
const N: usize = 100000;
127+
#[cfg(miri)]
128+
const N: usize = 1000;
129+
for _ in 0..N {
118130
arena.alloc(());
119131
}
120132
}
@@ -124,7 +136,11 @@ pub fn test_typed_arena_clear() {
124136
let mut arena = TypedArena::default();
125137
for _ in 0..10 {
126138
arena.clear();
127-
for _ in 0..10000 {
139+
#[cfg(not(miri))]
140+
const N: usize = 10000;
141+
#[cfg(miri)]
142+
const N: usize = 100;
143+
for _ in 0..N {
128144
arena.alloc(Point { x: 1, y: 2, z: 3 });
129145
}
130146
}

0 commit comments

Comments
 (0)