Skip to content

Commit 7a1617a

Browse files
authored
Fix unsoundness in FromBytes::new_box_slice_zeroed (#63)
Previously, `FromBytes::new_box_slice_zeroed` called `Layout::from_size_align_unchecked` with arguments that could overflow `isize`, which would violate its safety preconditions. In this change, we use the safe variant, which returns a `Result` which we can `.expect()`. Though I haven't benchmarked it, this likely has little impact on performance over the optimal code since optimal code would still need to perform the same bounds check that `from_size_align` is performing.
1 parent 437ba16 commit 7a1617a

File tree

2 files changed

+42
-10
lines changed

2 files changed

+42
-10
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,5 @@ default-features = false
3232

3333
[dev-dependencies]
3434
rand = "0.6"
35+
rustversion = "1.0"
3536
trybuild = "1.0"

src/lib.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -358,17 +358,14 @@ pub unsafe trait FromBytes {
358358
{
359359
// TODO(#2): Use `Layout::repeat` when `alloc_layout_extra` is
360360
// stabilized.
361-
//
362-
// This will intentionally panic if it overflows.
361+
let layout = Layout::from_size_align(
362+
mem::size_of::<Self>()
363+
.checked_mul(len)
364+
.expect("mem::size_of::<Self>() * len overflows `usize`"),
365+
mem::align_of::<Self>(),
366+
)
367+
.expect("total allocation size overflows `isize`");
363368
unsafe {
364-
// SAFETY: `from_size_align_unchecked` is sound because
365-
// slice_len_bytes is guaranteed to be properly aligned (we just
366-
// multiplied it by `size_of::<T>()`, which is guaranteed to be
367-
// aligned).
368-
let layout = Layout::from_size_align_unchecked(
369-
mem::size_of::<Self>().checked_mul(len).unwrap(),
370-
mem::align_of::<Self>(),
371-
);
372369
if layout.size() != 0 {
373370
let ptr = alloc::alloc::alloc_zeroed(layout) as *mut Self;
374371
if ptr.is_null() {
@@ -2204,6 +2201,40 @@ mod alloc_support {
22042201
let s: Box<[()]> = <()>::new_box_slice_zeroed(0);
22052202
assert_eq!(s.len(), 0);
22062203
}
2204+
2205+
#[test]
2206+
#[should_panic(expected = "mem::size_of::<Self>() * len overflows `usize`")]
2207+
fn test_new_box_slice_zeroed_panics_mul_overflow() {
2208+
let _ = u16::new_box_slice_zeroed(usize::MAX);
2209+
}
2210+
2211+
// This test fails on stable <= 1.64.0, but succeeds on 1.65.0-beta.2
2212+
// (2022-09-13). In particular, on stable <= 1.64.0,
2213+
// `new_box_slice_zeroed` attempts to perform the allocation (which of
2214+
// course fails). `Layout::from_size_align` should be returning an error
2215+
// due to `isize` overflow, but it doesn't. I (joshlf) haven't
2216+
// investigated enough to confirm, but my guess is that this was a bug
2217+
// that was fixed recently.
2218+
//
2219+
// Triggering the bug requires calling `FromBytes::new_box_slice_zeroed`
2220+
// with an allocation which overflows `isize`, and all that happens is
2221+
// that the program panics due to a failed allocation. Even on 32-bit
2222+
// platforms, this requires a 2GB allocation. On 64-bit platforms, this
2223+
// requires a 2^63-byte allocation. In both cases, even a slightly
2224+
// smaller allocation that didn't trigger this bug would likely
2225+
// (absolutely certainly in the case of 64-bit platforms) fail to
2226+
// allocate in exactly the same way regardless.
2227+
//
2228+
// Given how minor the impact is, and given that the bug seems fixed in
2229+
// 1.65.0, I've chosen to just release the code as-is and disable the
2230+
// test on buggy toolchains. Once our MSRV is at or above 1.65.0, we can
2231+
// remove this conditional compilation (and this comment) entirely.
2232+
#[rustversion::since(1.65.0)]
2233+
#[test]
2234+
#[should_panic(expected = "total allocation size overflows `isize`: LayoutError")]
2235+
fn test_new_box_slice_zeroed_panics_isize_overflow() {
2236+
let _ = u16::new_box_slice_zeroed((isize::MAX as usize / mem::size_of::<u16>()) + 1);
2237+
}
22072238
}
22082239
}
22092240

0 commit comments

Comments
 (0)