From d0293b0e35838123c51ca5dfdf468ecafee4398f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 3 Feb 2026 14:40:22 +0100 Subject: [PATCH] Merge commit from fork * Add repro for integer overflow Signed-off-by: Alice Ryhl * Always check overflow in new_cap + offset Signed-off-by: Alice Ryhl --------- Signed-off-by: Alice Ryhl --- ci/miri.sh | 3 +++ src/bytes_mut.rs | 20 ++++++++++++-------- tests/test_bytes.rs | 13 +++++++++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/ci/miri.sh b/ci/miri.sh index 7df29f3..ca7f41d 100755 --- a/ci/miri.sh +++ b/ci/miri.sh @@ -8,3 +8,6 @@ export MIRIFLAGS="-Zmiri-strict-provenance" cargo miri test cargo miri test --target mips64-unknown-linux-gnuabi64 + +# run with wrapping integer overflow instead of panic +cargo miri test --release diff --git a/src/bytes_mut.rs b/src/bytes_mut.rs index 565e91d..15e67d9 100644 --- a/src/bytes_mut.rs +++ b/src/bytes_mut.rs @@ -695,9 +695,15 @@ impl BytesMut { let offset = self.ptr.as_ptr().offset_from(ptr) as usize; + let new_cap_plus_offset = match new_cap.checked_add(offset) { + Some(new_cap_plus_offset) => new_cap_plus_offset, + None if !allocate => return false, + None => panic!("overflow"), + }; + // Compare the condition in the `kind == KIND_VEC` case above // for more details. - if v_capacity >= new_cap + offset { + if v_capacity >= new_cap_plus_offset { self.cap = new_cap; // no copy is necessary } else if v_capacity >= new_cap && offset >= len { @@ -713,14 +719,12 @@ impl BytesMut { if !allocate { return false; } - // calculate offset - let off = (self.ptr.as_ptr() as usize) - (v.as_ptr() as usize); // new_cap is calculated in terms of `BytesMut`, not the underlying // `Vec`, so it does not take the offset into account. // // Thus we have to manually add it here. - new_cap = new_cap.checked_add(off).expect("overflow"); + new_cap = new_cap_plus_offset; // The vector capacity is not sufficient. The reserve request is // asking for more than the initial buffer capacity. Allocate more @@ -742,13 +746,13 @@ impl BytesMut { // the unused capacity of the vector is copied over to the new // allocation, so we need to ensure that we don't have any data we // care about in the unused capacity before calling `reserve`. - debug_assert!(off + len <= v.capacity()); - v.set_len(off + len); + debug_assert!(offset + len <= v.capacity()); + v.set_len(offset + len); v.reserve(new_cap - v.len()); // Update the info - self.ptr = vptr(v.as_mut_ptr().add(off)); - self.cap = v.capacity() - off; + self.ptr = vptr(v.as_mut_ptr().add(offset)); + self.cap = v.capacity() - offset; } return true; diff --git a/tests/test_bytes.rs b/tests/test_bytes.rs index ec9a60e..b9bd5e1 100644 --- a/tests/test_bytes.rs +++ b/tests/test_bytes.rs @@ -1707,3 +1707,16 @@ fn bytes_mut_put_bytes_specialization() { // If allocation is reused, capacity should be equal to original vec capacity. assert_eq!(bytes_mut.capacity(), capacity); } + +#[test] +#[should_panic] +fn bytes_mut_reserve_overflow() { + let mut a = BytesMut::from(&b"hello world"[..]); + let mut b = a.split_off(5); + // Ensure b becomes the unique owner of the backing storage + drop(a); + // Trigger overflow in new_cap + offset inside reserve + b.reserve(usize::MAX - 6); + // This call relies on the corrupted cap and may cause UB & HBO + b.put_u8(b'h'); +}