Auto merge of #95295 - CAD97:layout-isize, r=scottmcm
Enforce that layout size fits in isize in Layout As it turns out, enforcing this _in APIs that already enforce `usize` overflow_ is fairly trivial. `Layout::from_size_align_unchecked` continues to "allow" sizes which (when rounded up) would overflow `isize`, but these are now declared as library UB for `Layout`, meaning that consumers of `Layout` no longer have to check this before making an allocation. (Note that this is "immediate library UB;" IOW it is valid for a future release to make this immediate "language UB," and there is an extant patch to do so, to allow Miri to catch this misuse.) See also #95252, [Zulip discussion](https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Layout.20Isn't.20Enforcing.20The.20isize.3A.3AMAX.20Rule). Fixes https://github.com/rust-lang/rust/issues/95334 Some relevant quotes: `@eddyb,` https://github.com/rust-lang/rust/pull/95252#issuecomment-1078513769 > [B]ecause of the non-trivial presence of both of these among code published on e.g. crates.io: > > 1. **`Layout` "producers" / `GlobalAlloc` "users"**: smart pointers (including `alloc::rc` copies with small tweaks), collections, etc. > 2. **`Layout` "consumers" / `GlobalAlloc` "providers"**: perhaps fewer of these, but anything built on top of OS APIs like `mmap` will expose `> isize::MAX` allocations (on 32-bit hosts) if they lack extra checks > > IMO the only responsible option is to enforce the `isize::MAX` limit in `Layout`, which: > > * makes `Layout` _sound_ in terms of only ever allowing allocations where `(alloc_base_ptr: *mut u8).offset(size)` is never UB > * frees both "producers" and "consumers" of `Layout` from manually reimplementing the checks > * manual checks can be risky, e.g. if the final size passed to the allocator isn't the one being checked > * this applies retroactively, fixing the overall soundness of existing code with zero transition period or _any_ changes required from users (as long as going through `Layout` is mandatory, making a "choke point") > > > Feel free to quote this comment onto any relevant issue, I might not be able to keep track of developments. `@Gankra,` https://github.com/rust-lang/rust/pull/95252#issuecomment-1078556371 > As someone who spent way too much time optimizing libcollections checks for this stuff and tried to splatter docs about it everywhere on the belief that it was a reasonable thing for people to manually take care of: I concede the point, it is not reasonable. I am wholy spiritually defeated by the fact that _liballoc_ of all places is getting this stuff wrong. This isn't throwing shade at the folks who implemented these Rc features, but rather a statement of how impractical it is to expect anyone out in the wider ecosystem to enforce them if _some of the most audited rust code in the library that defines the very notion of allocating memory_ can't even reliably do it. > > We need the nuclear option of Layout enforcing this rule. Code that breaks this rule is _deeply_ broken and any "regressions" from changing Layout's contract is a _correctness_ fix. Anyone who disagrees and is sufficiently motivated can go around our backs but the standard library should 100% refuse to enable them. cc also `@RalfJung` `@rust-lang/wg-allocators.` Even though this technically supersedes #95252, those potential failure points should almost certainly still get nicer panics than just "unwrap failed" (which they would get by this PR). It might additionally be worth recommending to users of the `Layout` API that they should ideally use `.and_then`/`?` to complete the entire layout calculation, and then `panic!` from a single location at the end of `Layout` manipulation, to reduce the overhead of the checks and optimizations preserving the exact location of each `panic` which are conceptually just one failure: allocation too big. Probably deserves a T-lang and/or T-libs-api FCP (this technically solidifies the [objects must be no larger than `isize::MAX`](https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize) rule further, and the UCG document says this hasn't been RFCd) and a crater run. Ideally, no code exists that will start failing with this addition; if it does, it was _likely_ (but not certainly) causing UB. Changes the raw_vec allocation path, thus deserves a perf run as well. I suggest hiding whitespace-only changes in the diff view.
This commit is contained in:
commit
4ec97d991b
4 changed files with 150 additions and 325 deletions
|
@ -693,12 +693,6 @@ fn test_try_reserve() {
|
|||
const MAX_CAP: usize = isize::MAX as usize;
|
||||
const MAX_USIZE: usize = usize::MAX;
|
||||
|
||||
// On 16/32-bit, we check that allocations don't exceed isize::MAX,
|
||||
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
|
||||
// Any platform that succeeds for these requests is technically broken with
|
||||
// ptr::offset because LLVM is the worst.
|
||||
let guards_against_isize = usize::BITS < 64;
|
||||
|
||||
{
|
||||
// Note: basic stuff is checked by test_reserve
|
||||
let mut empty_string: String = String::new();
|
||||
|
@ -712,7 +706,6 @@ fn test_try_reserve() {
|
|||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
|
||||
if guards_against_isize {
|
||||
// Check isize::MAX + 1 does count as overflow
|
||||
assert_matches!(
|
||||
empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
|
@ -726,21 +719,6 @@ fn test_try_reserve() {
|
|||
Err(CapacityOverflow),
|
||||
"usize::MAX should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
// Check isize::MAX + 1 is an OOM
|
||||
assert_matches!(
|
||||
empty_string.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
|
||||
// Check usize::MAX is an OOM
|
||||
assert_matches!(
|
||||
empty_string.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"usize::MAX should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -753,19 +731,13 @@ fn test_try_reserve() {
|
|||
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) {
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
// Should always overflow in the add-to-len
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
||||
|
@ -785,8 +757,6 @@ fn test_try_reserve_exact() {
|
|||
const MAX_CAP: usize = isize::MAX as usize;
|
||||
const MAX_USIZE: usize = usize::MAX;
|
||||
|
||||
let guards_against_isize = usize::BITS < 64;
|
||||
|
||||
{
|
||||
let mut empty_string: String = String::new();
|
||||
|
||||
|
@ -799,7 +769,6 @@ fn test_try_reserve_exact() {
|
|||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
|
||||
if guards_against_isize {
|
||||
assert_matches!(
|
||||
empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
@ -811,19 +780,6 @@ fn test_try_reserve_exact() {
|
|||
Err(CapacityOverflow),
|
||||
"usize::MAX should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
empty_string.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
empty_string.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"usize::MAX should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -839,19 +795,13 @@ fn test_try_reserve_exact() {
|
|||
{
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
|
|
@ -1517,12 +1517,6 @@ fn test_try_reserve() {
|
|||
const MAX_CAP: usize = isize::MAX as usize;
|
||||
const MAX_USIZE: usize = usize::MAX;
|
||||
|
||||
// On 16/32-bit, we check that allocations don't exceed isize::MAX,
|
||||
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
|
||||
// Any platform that succeeds for these requests is technically broken with
|
||||
// ptr::offset because LLVM is the worst.
|
||||
let guards_against_isize = usize::BITS < 64;
|
||||
|
||||
{
|
||||
// Note: basic stuff is checked by test_reserve
|
||||
let mut empty_bytes: Vec<u8> = Vec::new();
|
||||
|
@ -1536,7 +1530,6 @@ fn test_try_reserve() {
|
|||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
|
||||
if guards_against_isize {
|
||||
// Check isize::MAX + 1 does count as overflow
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
|
@ -1550,21 +1543,6 @@ fn test_try_reserve() {
|
|||
Err(CapacityOverflow),
|
||||
"usize::MAX should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
// Check isize::MAX + 1 is an OOM
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
|
||||
// Check usize::MAX is an OOM
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"usize::MAX should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1577,19 +1555,13 @@ fn test_try_reserve() {
|
|||
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) {
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
// Should always overflow in the add-to-len
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
||||
|
@ -1610,19 +1582,13 @@ fn test_try_reserve() {
|
|||
{
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
// Should fail in the mul-by-size
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve(MAX_USIZE - 20).map_err(|e| e.kind()),
|
||||
|
@ -1642,8 +1608,6 @@ fn test_try_reserve_exact() {
|
|||
const MAX_CAP: usize = isize::MAX as usize;
|
||||
const MAX_USIZE: usize = usize::MAX;
|
||||
|
||||
let guards_against_isize = size_of::<usize>() < 8;
|
||||
|
||||
{
|
||||
let mut empty_bytes: Vec<u8> = Vec::new();
|
||||
|
||||
|
@ -1656,7 +1620,6 @@ fn test_try_reserve_exact() {
|
|||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
|
||||
if guards_against_isize {
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
@ -1668,19 +1631,6 @@ fn test_try_reserve_exact() {
|
|||
Err(CapacityOverflow),
|
||||
"usize::MAX should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"usize::MAX should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1696,19 +1646,13 @@ fn test_try_reserve_exact() {
|
|||
{
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
@ -1729,19 +1673,13 @@ fn test_try_reserve_exact() {
|
|||
{
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve_exact(MAX_USIZE - 20).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
|
|
@ -2,7 +2,6 @@ use std::assert_matches::assert_matches;
|
|||
use std::collections::TryReserveErrorKind::*;
|
||||
use std::collections::{vec_deque::Drain, VecDeque};
|
||||
use std::fmt::Debug;
|
||||
use std::mem::size_of;
|
||||
use std::ops::Bound::*;
|
||||
use std::panic::{catch_unwind, AssertUnwindSafe};
|
||||
|
||||
|
@ -1161,12 +1160,6 @@ fn test_try_reserve() {
|
|||
const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1;
|
||||
const MAX_USIZE: usize = usize::MAX;
|
||||
|
||||
// On 16/32-bit, we check that allocations don't exceed isize::MAX,
|
||||
// on 64-bit, we assume the OS will give an OOM for such a ridiculous size.
|
||||
// Any platform that succeeds for these requests is technically broken with
|
||||
// ptr::offset because LLVM is the worst.
|
||||
let guards_against_isize = size_of::<usize>() < 8;
|
||||
|
||||
{
|
||||
// Note: basic stuff is checked by test_reserve
|
||||
let mut empty_bytes: VecDeque<u8> = VecDeque::new();
|
||||
|
@ -1180,7 +1173,6 @@ fn test_try_reserve() {
|
|||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
|
||||
if guards_against_isize {
|
||||
// Check isize::MAX + 1 does count as overflow
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
|
@ -1194,17 +1186,6 @@ fn test_try_reserve() {
|
|||
Err(CapacityOverflow),
|
||||
"usize::MAX should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
// Check isize::MAX is an OOM
|
||||
// VecDeque starts with capacity 7, always adds 1 to the capacity
|
||||
// and also rounds the number to next power of 2 so this is the
|
||||
// furthest we can go without triggering CapacityOverflow
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve(MAX_CAP).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1217,19 +1198,13 @@ fn test_try_reserve() {
|
|||
if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10).map_err(|e| e.kind()) {
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
// Should always overflow in the add-to-len
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve(MAX_USIZE).map_err(|e| e.kind()),
|
||||
|
@ -1250,19 +1225,13 @@ fn test_try_reserve() {
|
|||
{
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
// Should fail in the mul-by-size
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve(MAX_USIZE - 20).map_err(|e| e.kind()),
|
||||
|
@ -1282,8 +1251,6 @@ fn test_try_reserve_exact() {
|
|||
const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1;
|
||||
const MAX_USIZE: usize = usize::MAX;
|
||||
|
||||
let guards_against_isize = size_of::<usize>() < 8;
|
||||
|
||||
{
|
||||
let mut empty_bytes: VecDeque<u8> = VecDeque::new();
|
||||
|
||||
|
@ -1296,7 +1263,6 @@ fn test_try_reserve_exact() {
|
|||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
|
||||
if guards_against_isize {
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve_exact(MAX_CAP + 1).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
@ -1308,17 +1274,6 @@ fn test_try_reserve_exact() {
|
|||
Err(CapacityOverflow),
|
||||
"usize::MAX should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
// Check isize::MAX is an OOM
|
||||
// VecDeque starts with capacity 7, always adds 1 to the capacity
|
||||
// and also rounds the number to next power of 2 so this is the
|
||||
// furthest we can go without triggering CapacityOverflow
|
||||
assert_matches!(
|
||||
empty_bytes.try_reserve_exact(MAX_CAP).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -1334,19 +1289,13 @@ fn test_try_reserve_exact() {
|
|||
{
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_CAP - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
assert_matches!(
|
||||
ten_bytes.try_reserve_exact(MAX_USIZE).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
@ -1367,19 +1316,13 @@ fn test_try_reserve_exact() {
|
|||
{
|
||||
panic!("isize::MAX shouldn't trigger an overflow!");
|
||||
}
|
||||
if guards_against_isize {
|
||||
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
"isize::MAX + 1 should trigger an overflow!"
|
||||
);
|
||||
} else {
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve_exact(MAX_CAP / 4 - 9).map_err(|e| e.kind()),
|
||||
Err(AllocError { .. }),
|
||||
"isize::MAX + 1 should trigger an OOM!"
|
||||
);
|
||||
}
|
||||
|
||||
assert_matches!(
|
||||
ten_u32s.try_reserve_exact(MAX_USIZE - 20).map_err(|e| e.kind()),
|
||||
Err(CapacityOverflow),
|
||||
|
|
|
@ -52,8 +52,8 @@ impl Layout {
|
|||
/// * `align` must be a power of two,
|
||||
///
|
||||
/// * `size`, when rounded up to the nearest multiple of `align`,
|
||||
/// must not overflow (i.e., the rounded value must be less than
|
||||
/// or equal to `usize::MAX`).
|
||||
/// must not overflow isize (i.e., the rounded value must be
|
||||
/// less than or equal to `isize::MAX`).
|
||||
#[stable(feature = "alloc_layout", since = "1.28.0")]
|
||||
#[rustc_const_stable(feature = "const_alloc_layout_size_align", since = "1.50.0")]
|
||||
#[inline]
|
||||
|
@ -76,7 +76,7 @@ impl Layout {
|
|||
//
|
||||
// Above implies that checking for summation overflow is both
|
||||
// necessary and sufficient.
|
||||
if size > usize::MAX - (align - 1) {
|
||||
if size > isize::MAX as usize - (align - 1) {
|
||||
return Err(LayoutError);
|
||||
}
|
||||
|
||||
|
@ -276,8 +276,8 @@ impl Layout {
|
|||
let pad = self.padding_needed_for(self.align());
|
||||
// This cannot overflow. Quoting from the invariant of Layout:
|
||||
// > `size`, when rounded up to the nearest multiple of `align`,
|
||||
// > must not overflow (i.e., the rounded value must be less than
|
||||
// > `usize::MAX`)
|
||||
// > must not overflow isize (i.e., the rounded value must be
|
||||
// > less than or equal to `isize::MAX`)
|
||||
let new_size = self.size() + pad;
|
||||
|
||||
// SAFETY: self.align is already known to be valid and new_size has been
|
||||
|
@ -298,14 +298,13 @@ impl Layout {
|
|||
pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutError> {
|
||||
// This cannot overflow. Quoting from the invariant of Layout:
|
||||
// > `size`, when rounded up to the nearest multiple of `align`,
|
||||
// > must not overflow (i.e., the rounded value must be less than
|
||||
// > `usize::MAX`)
|
||||
// > must not overflow isize (i.e., the rounded value must be
|
||||
// > less than or equal to `isize::MAX`)
|
||||
let padded_size = self.size() + self.padding_needed_for(self.align());
|
||||
let alloc_size = padded_size.checked_mul(n).ok_or(LayoutError)?;
|
||||
|
||||
// SAFETY: self.align is already known to be valid and alloc_size has been
|
||||
// padded already.
|
||||
unsafe { Ok((Layout::from_size_align_unchecked(alloc_size, self.align()), padded_size)) }
|
||||
// The safe constructor is called here to enforce the isize size limit.
|
||||
Layout::from_size_align(alloc_size, self.align()).map(|layout| (layout, padded_size))
|
||||
}
|
||||
|
||||
/// Creates a layout describing the record for `self` followed by
|
||||
|
@ -362,6 +361,7 @@ impl Layout {
|
|||
let offset = self.size().checked_add(pad).ok_or(LayoutError)?;
|
||||
let new_size = offset.checked_add(next.size()).ok_or(LayoutError)?;
|
||||
|
||||
// The safe constructor is called here to enforce the isize size limit.
|
||||
let layout = Layout::from_size_align(new_size, new_align)?;
|
||||
Ok((layout, offset))
|
||||
}
|
||||
|
@ -382,6 +382,7 @@ impl Layout {
|
|||
#[inline]
|
||||
pub fn repeat_packed(&self, n: usize) -> Result<Self, LayoutError> {
|
||||
let size = self.size().checked_mul(n).ok_or(LayoutError)?;
|
||||
// The safe constructor is called here to enforce the isize size limit.
|
||||
Layout::from_size_align(size, self.align())
|
||||
}
|
||||
|
||||
|
@ -395,6 +396,7 @@ impl Layout {
|
|||
#[inline]
|
||||
pub fn extend_packed(&self, next: Self) -> Result<Self, LayoutError> {
|
||||
let new_size = self.size().checked_add(next.size()).ok_or(LayoutError)?;
|
||||
// The safe constructor is called here to enforce the isize size limit.
|
||||
Layout::from_size_align(new_size, self.align())
|
||||
}
|
||||
|
||||
|
@ -405,16 +407,8 @@ impl Layout {
|
|||
#[inline]
|
||||
pub fn array<T>(n: usize) -> Result<Self, LayoutError> {
|
||||
let array_size = mem::size_of::<T>().checked_mul(n).ok_or(LayoutError)?;
|
||||
|
||||
// SAFETY:
|
||||
// - Size: `array_size` cannot be too big because `size_of::<T>()` must
|
||||
// be a multiple of `align_of::<T>()`. Therefore, `array_size`
|
||||
// rounded up to the nearest multiple of `align_of::<T>()` is just
|
||||
// `array_size`. And `array_size` cannot be too big because it was
|
||||
// just checked by the `checked_mul()`.
|
||||
// - Alignment: `align_of::<T>()` will always give an acceptable
|
||||
// (non-zero, power of two) alignment.
|
||||
Ok(unsafe { Layout::from_size_align_unchecked(array_size, mem::align_of::<T>()) })
|
||||
// The safe constructor is called here to enforce the isize size limit.
|
||||
Layout::from_size_align(array_size, mem::align_of::<T>())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue