I'm running Mini on tests for the first time and after reading multiple other topics here on the forum and various GitHub issues still don't really understand what is the issue here:
test basic ... error: Undefined Behavior: attempting a read access using <316299> at alloc169530[0x10], but that tag does not exist in the borrow stack for this location
--> abundance/crates/contracts/ab-contracts-executor/src/context/ffi_call.rs:128:45
|
128 | let data_ptr = unsafe { data_ptr.as_ptr().read().cast_const() };
| ^^^^^^^^^^^^^^^^^^^^^^^^
| |
| attempting a read access using <316299> at alloc169530[0x10], but that tag does not exist in the borrow stack for this location
| this error occurs as part of an access at alloc169530[0x10..0x18]
|
= 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: <316299> was created by a SharedReadWrite retag at offsets [0x0..0x80]
--> abundance/crates/contracts/ab-contracts-executor/src/context/ffi_call.rs:227:54
|
227 | NonNull::<MaybeUninit<*mut c_void>>::new(internal_args.as_mut_ptr())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: <316299> was later invalidated at offsets [0x0..0x80] by a Unique retag
--> abundance/crates/contracts/ab-contracts-executor/src/context/ffi_call.rs:669:54
|
669 | NonNull::<MaybeUninit<*mut c_void>>::new(self.internal_args.as_mut_ptr())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Essentially I allocate some memory and create an AliasableBox (from crates.io: Rust Package Registry), then write some pointers to data there. Afterwards I call a function with a pointer to that memory so the function can read/write data and later read potentially modified data later.
Since I'm switching to AliasableBox and work with raw pointers, why does highlighted line 669 above results in "Unique retag"? I thought it would remain aliasable since I just copy and cast the pointer.
Doing this method call starts by creating a &mut [MaybeUninit<*mut c_void>] reference by DerefMut on the AliasableBox. Mutable references have unaliased access, so all existing pointers to the same slice are invalidated.
IMO, your example is significantly different; and the error message is different, too, as far as the help messages are concerned:
your out-of-range example says
attempting a read access using <1462> at alloc683[0x4]
and
<1462> was created by a SharedReadOnly retag at offsets [0x0..0x4]
so it’s clearly out of range (0x4) isn’t in 0x0..0x4 (these ranges have exclusive right bounds).
Whereas the OP’s code says
attempting a read access using <316299> at alloc169530[0x10]
and
<316299> was created by a SharedReadWrite retag at offsets [0x0..0x80]
which is completely in-bounds, and read access isn’t an issue either, but the issue is the next part of
<316299> was later invalidated at offsets [0x0..0x80] by a Unique retag
Maybe you didn’t read beyond up to the help message – you also speak of “the first error” when only a single error was shared (and miri stops executing after a single error, anyway).
If you want to still use AliasableBox (which is reasonable if you want to be able to move the box around while holding references into it) you could consider e.g. combining it with UnsafeCell.
I also feel like the use of Pin in your code is almost definitely[1] completely redundant.
Hm... that method is actually provided by [T] impl, not by AliasableBox itself. So it dereferences internal pointer to create a slice and then takes a pointer to a slice.
Though I'm not exactly sure how AliasableBox is useful then if it has no methods on its own And I'm even more confused by AliasableVec in the same crate for the same reason.
That is my goal and I used Pin to indicate that it shouldn't be reallocated after Box creation, though I guess it isn't particularly helpful in that context.
I’ve only seen it used in self-referencing structs so far. Which yours arguably is AFAICT, so that’s still usefull. The use case that AliasableBox supports and Box doesn’t is something like
let mut box1 = Box::new(42);
let ptr: *const i32 = &mut *box1; // or *const and &*box, same story;
// now move the box
let box2 = box1;
// now use the pointer
unsafe {
println!("{}", *ptr);
}
moving around a whole Box by value – in particular also passing it to some other function - can assert unique access to its contents (through that box) and invalidate ptr.
To create multiple references to the target however, it looks like one would generally have to involve shared mutable access via UnsafeCell (or similar types that are wrapping it), at least given the current API of AliasableBox where you really only can do Deref/DerefMut besides the by-value conversions from/to ordinary Box. Maybe that isn’t a terrible idea anyway, at least with UnsafeCell added, the target type becomes invariant.
Actually yes, it is self-referential. Thinking more about this, I can actually work around at least some of issues by storing pointer offset instead of pointers itself, though I'd rather not and I do have other types that may not be able to get away with just offsets.
UnsafeCell is something I have not used yet anywhere.
and then internal_args.get() always gives you a *mut [MaybeUninit<*mut c_void>] raw pointer through which you can have mutable access, without invalidating any other copies of the pointer created from such get() calls (as long as you don’t have unsynchronized access to the same place, or create other references from it that overlap&alias, etc…)
Hm... combination of AliasableBox and UnsafeCell for inner values worked indeed.
I don't think I fully understand why though. Is that because once things reach to UnsafeCell stacked borrows rules no longer apply with what comes out of it?
If a &Type reference points to some Type with some UnsafeCell in fields, then all data/bytes that lives within an UnsafeCell actually offers shared mutable access.
It isn’t that UnsafeCell “disables” stacked-borrows rules, but the exact interaction with / behavior of UnsafeCell is a core part of stacked borrow rules.