Description
rustc generally detects when a static type is larger than isize::MAX
bytes, giving an error that values of the type `...` are too big for the current architecture
. However, if a type has fields at most isize::MAX
bytes long, and additional padding that brings it to isize::MAX + 1
bytes, then this error will not be generated. To illustrate, all four of these print 0x80000000
(i.e., isize::MAX + 1
):
// cargo build --release --target i686-unknown-linux-gnu
// prlimit --stack=unlimited target/i686-unknown-linux-gnu/release/example
use std::{hint, mem::{self, MaybeUninit}, alloc::Layout};
#[repr(C, align(2))]
struct Example(MaybeUninit<[u8; 0x7fffffff]>);
fn main() {
println!("{:#x}", mem::size_of::<Example>());
println!("{:#x}", Layout::new::<Example>().size());
let e = Example(MaybeUninit::uninit());
hint::black_box(&e);
println!("{:#x}", mem::size_of_val(&e));
println!("{:#x}", Layout::for_value(&e).size());
}
(The black_box()
is to ensure that an object of size 0x80000000
is actually created on the stack.)
This is clearly unsound, since it breaks the size invariant of Layout
, and since third-party crates may depend on types being no larger than isize::MAX
for soundness. However, it's nontrivial to observe unexpected behavior from this using only safe APIs in the standard library, since placing the overlarge type within any other type, even a repr(transparent)
wrapper, will result in a compile error as expected, and the standard APIs tend to refer to &[T; 1]
when creating a slice from a reference.
Activity