Skip to content

Commit

Permalink
Work around stack overflow in array_from_fn (#876)
Browse files Browse the repository at this point in the history
It should fix a crash in JikesRVM caused by overflown stack overwriting
objects in the VM space.

Fixes: #874

---------

Co-authored-by: Yi Lin <qinsoon@gmail.com>
  • Loading branch information
wks and qinsoon authored Aug 1, 2023
1 parent 566f2bd commit f780a11
Showing 1 changed file with 16 additions and 6 deletions.
22 changes: 16 additions & 6 deletions src/util/rust_util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,14 +127,24 @@ mod initialize_once_tests {
}

/// This implements `std::array::from_fn` introduced in Rust 1.63.
/// We should replace this with the standard counterpart after bumping MSRV.
pub(crate) fn array_from_fn<T, const N: usize, F>(cb: F) -> [T; N]
/// We should replace this with the standard counterpart after bumping MSRV,
/// but we also need to evaluate whether it would use too much stack space (see code comments).
pub(crate) fn array_from_fn<T, const N: usize, F>(mut cb: F) -> [T; N]
where
F: FnMut(usize) -> T,
{
let mut index_array = [0; N];
for (index, item) in index_array.iter_mut().enumerate() {
*item = index;
// Note on unsafety: An alternative to the unsafe implementation below is creating a fixed
// array of `[0, 1, 2, ..., N-1]` and using the `.map(cb)` method to create the result.
// However, the `array::map` method implemented in the standard library consumes a surprisingly
// large amount of stack space. For VMs that run on confined stacks, such as JikesRVM, that
// would cause stack overflow. Therefore we implement it manually using unsafe primitives.
let mut result_array: MaybeUninit<[T; N]> = MaybeUninit::zeroed();
let array_ptr = result_array.as_mut_ptr();
for index in 0..N {
let item = cb(index);
unsafe {
std::ptr::addr_of_mut!((*array_ptr)[index]).write(item);
}
}
index_array.map(cb)
unsafe { result_array.assume_init() }
}

0 comments on commit f780a11

Please sign in to comment.