Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 20 bytes, but got alloc238 which is only 1 byte from the end of the allocation
⚓ Rust 📅 2026-04-23 👤 surdeus 👁️ 1Hello, I am confused by this error from Miri :
Compiling playground v0.0.1 (/playground)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.45s
Running `/playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo-miri runner target/miri/x86_64-unknown-linux-gnu/debug/playground`
error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by 20 bytes, but got alloc238 which is only 1 byte from the end of the allocation
--> src/main.rs:124:24
|
124 | let next = ptr.add(size);
| ^^^^^^^^^^^^^ Undefined Behavior occurred here
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
help: alloc238 was allocated here:
--> src/main.rs:22:25
|
22 | let start = std::alloc::alloc(layout);
| ^^^^^^^^^^^^^^^^^^^^^^^^^
= note: stack backtrace:
0: <&HeapArena as std::alloc::Allocator>::allocate
at src/main.rs:124:24: 124:37
1: alloc::raw_vec::RawVecInner::<&HeapArena>::try_allocate_in
at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec/mod.rs:465:41: 465:63
2: alloc::raw_vec::RawVecInner::<&HeapArena>::with_capacity_in
at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec/mod.rs:434:15: 434:92
3: alloc::raw_vec::RawVec::<i32, &HeapArena>::with_capacity_in
at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/raw_vec/mod.rs:177:20: 177:77
4: std::vec::Vec::<i32, &HeapArena>::with_capacity_in
at /playground/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/alloc/src/vec/mod.rs:977:20: 977:61
5: main
at src/main.rs:143:23: 143:55
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
error: aborting due to 1 previous error
Standard Output
My code is like this :
#![feature(allocator_api)]
pub struct Block {
ptr: *mut u8,
cursor: *mut u8,
end: *mut u8,
layout: std::alloc::Layout,
next: Option<Box<Block>>
}
pub struct HeapArena {
head: std::cell::UnsafeCell<Block>
}
impl HeapArena {
#[inline(always)]
pub fn new(size: usize) -> Self {
let layout = unsafe { std::alloc::Layout::from_size_align_unchecked(size, std::mem::align_of::<usize>()) };
let start = unsafe { std::alloc::alloc(layout) };
if start.is_null() {
std::alloc::handle_alloc_error(layout);
}
Self {
head: std::cell::UnsafeCell::new(Block {
ptr: start,
cursor: start,
end: unsafe { start.add(size) },
layout: layout,
next: None
})
}
}
unsafe fn grows(&self, min_size: usize) {
println!("grow");
let head = unsafe { &mut *self.head.get() };
let new_size = min_size.max(head.layout.size() * 2);
let layout = std::alloc::Layout::from_size_align(new_size, 8).unwrap();
let ptr = unsafe { std::alloc::alloc(layout) };
if ptr.is_null() { std::alloc::handle_alloc_error(layout); }
let old_block = unsafe { std::ptr::replace(head, Block {
ptr,
layout,
cursor: ptr,
end: ptr.add(new_size),
next: None,
}) };
head.next = Some(Box::new(old_block));
}
#[inline(always)]
pub fn reset(&mut self) {
let mut current = unsafe { &mut *self.head.get() };
loop {
current.cursor = current.ptr;
if let Some(ref mut next_block) = current.next {
current = next_block.as_mut();
} else {
break;
}
}
}
}
impl Drop for HeapArena {
#[inline(always)]
fn drop(&mut self) {
let mut current = unsafe { std::ptr::replace(
self.head.get(),
Block {
ptr: std::ptr::null_mut(),
cursor: std::ptr::null_mut(),
end: std::ptr::null_mut(),
layout: std::alloc::Layout::from_size_align(1, 1).unwrap(),
next: None,
},
) };
loop {
if !current.ptr.is_null() {
unsafe { std::alloc::dealloc(current.ptr, current.layout) };
}
if let Some(next_block) = current.next {
current = *next_block;
} else {
break;
}
}
}
}
unsafe impl std::alloc::Allocator for &HeapArena {
#[inline(always)]
fn allocate(
&self,
layout: std::alloc::Layout,
) -> Result<std::ptr::NonNull<[u8]>, std::alloc::AllocError> {
let head = unsafe { &mut *self.head.get() };
let size = layout.size();
let align = layout.align();
let handle_addr = head.cursor as usize;
let aligned_addr = (handle_addr + (align - 1)) & !(align - 1);
/* let next = aligned_addr + size;
if next > head.end as usize {
self.grows(size);
return self.allocate(layout);
}
*/
// let ptr = aligned_addr as *mut u8;
let ptr = head.cursor.map_addr(|_| aligned_addr);
let next = unsafe { ptr.add(size) };
if next > head.end {
unsafe { self.grows(size) };
return self.allocate(layout);
}
head.cursor = next as *mut u8;
return Ok(unsafe {
std::ptr::NonNull::new_unchecked(
std::ptr::slice_from_raw_parts_mut(ptr, size),
)
});
}
#[inline(always)]
unsafe fn deallocate(&self, _ptr: std::ptr::NonNull<u8>, _layout: std::alloc::Layout) {}
}
fn main() {
let mut arena = HeapArena::new(1);
{let mut vector = Vec::with_capacity_in(5, &arena);
for _ in 0..100 {
vector.push(10);
vector.push(20);
}
println!("{}", vector[0]);
}
arena.reset();
//println!("{}", vector[0]);
}
What is and which line is the cause of why the ptr.add() failed? Why it is failed?
And what is the solution?
Thank you for the helps
8 posts - 3 participants
🏷️ Rust_feed