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.
Here is the full file: abundance/crates/contracts/ab-contracts-executor/src/context/ffi_call.rs at 1e4381b67d140510b7e7e043d47e0e5acb5b45d9 · nazar-pc/abundance · GitHub
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.
You’re calling <[MaybeUninit<*mut c_void>]>::as_mut_ptr
.
impl<T> [T] {
pub const fn as_mut_ptr(&mut self) -> *mut T;
}
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.
1 Like
I didn't check the full code, but the first error:
this error basically means the pointer is out of the range of its provenance. it's the same error like this example (playground):
EDIT:
never mind. the error can also means the ptr is 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 completely redundant.
thanks for pointing out, my bad, I just saw the "error", didn't pay attention to the "help".
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);
}
(playground)
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.
Right, it shouldn’t cost much effort just to try it out. You can replace your thing with
internal_args: AliasableBox<UnsafeCell<[MaybeUninit<*mut c_void>]>>
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…)
1 Like
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.
3 Likes
I really appreciate all the responses, I learned a lot today and make Miri happy in the process, thank you!