What I'm trying to do: Allocate memory, write a header struct at the beginning of the allocation, then treat the remainder of the allocation as an array of bytes. In C, I would do this using a flexible array member at the end of the struct.
What is happening: When running my code with Miri, Miri complains:
error: Undefined Behavior: trying to retag from <3555> for Unique permission at alloc1808[0x8], but that tag does not exist in the borrow stack for this location
(full error in code below)
This seems to only happen if I make a reference for the header struct first. If I avoid making a reference, Miri does not complain.
What I expect: I don't think my code has UB. If it does, I want to be told what specific rule I am breaking.
More context:
rustc 1.68.0-nightly (9c07efe84 2022-12-16)
Reduced example program:
#[repr(C)]
struct ChunkHeader {
len: usize, // Size of the data portion in bytes.
// data follows the struct.
}
fn main() {
unsafe {
// Allocate a ChunkHeader followed by 4096 uninitialized bytes.
let data_size: usize = 4096;
let layout = std::alloc::Layout::from_size_align(
std::mem::size_of::<ChunkHeader>() + data_size,
std::mem::align_of::<ChunkHeader>(),
)
.unwrap();
let chunk_ptr: *mut ChunkHeader = std::alloc::alloc(layout) as *mut ChunkHeader;
std::ptr::write(chunk_ptr, ChunkHeader { len: data_size });
let chunk: &mut ChunkHeader = &mut *chunk_ptr;
// Get a pointer to the beginning of the data portion of the allocation.
//
// If we use technique A, Miri does not complain.
//
// If we use technique B, Miri complains:
//
// error: Undefined Behavior: trying to retag from <3555> for Unique permission at alloc1808[0x8], but that tag does not exist in the borrow stack for this location
// --> [snip]/rustlib/src/rust/library/core/src/slice/raw.rs:145:9
// |
// 145 | &mut *ptr::slice_from_raw_parts_mut(data, len)
// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// | |
// | trying to retag from <3555> for Unique permission at alloc1808[0x8], but that tag does not exist in the borrow stack for this location
// | this error occurs as part of retag at alloc1808[0x8..0x14]
// |
// = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
// = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
// help: <3555> was created by a SharedReadWrite retag at offsets [0x0..0x8]
// --> src/main.rs:48:14
// |
// 48 | (chunk as *mut ChunkHeader as *mut std::mem::MaybeUninit<u8>)
// | ^^^^^
let data_begin = if false {
// Technique A: WORKS
(chunk_ptr as *mut std::mem::MaybeUninit<u8>).add(std::mem::size_of::<ChunkHeader>())
} else {
// Technique B: FAILS
(chunk as *mut ChunkHeader as *mut std::mem::MaybeUninit<u8>)
.add(std::mem::size_of::<ChunkHeader>())
};
// Make a slice for some of the uninitialized data bytes. This possibly triggers the Miri
// error.
let _data: &[std::mem::MaybeUninit<u8>] = std::slice::from_raw_parts_mut(data_begin, 12);
// Intentionally leak chunk_ptr.
}
}