-
Notifications
You must be signed in to change notification settings - Fork 352
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Box
in custom allocator fails even with -Zmiri-tree-borrows
#3341
Comments
This is odd, the problem is with accessing the allocator state itself, not the memory that stores the data. The backtraces are not very helpful since the actual action is in the standard library. Something Box/Vec do with the allocator itself makes Miri unhappy. Note that there is an unrelated UB issue here let delta = unsafe { next.byte_offset_from(self.memory.get()) }; This is UB if |
I can't reproduce this locally: when I run your file with
EDIT: oh your code is the non-failing one. That's confusing... |
This line is the culprit: let their_alloc = ptr.as_ptr().map_addr(|addr| addr & !127).cast::<Self>(); Even with Tree Borrows, you can't just "get out" of the This seems pretty fundamental; if we want to assume that |
What I don't understand is why you use |
Thanks for your reply!
Here is a more realistic example with some explanatory comments: Rust playground. In short, finding the corresponding bin is somewhat expensive in my real implementation, so I chose to use pointer arithmetics.
I think the uniqueness of
Oops XD. The newer example has fixed it. |
There's no way to have a pointer that's only unique for some of memory, that would defeat the entire purpose of Making uniqueness temporary in scope is possible, but here this doesn't really help. Consider a function like: fn foo(a: Box<i32, MyAlloc>, b: Box<i32, MyAlloc>) {
drop(a); drop(b),
} LLVM does limit the scope of the I think the only way to allow such accesses is to not have |
Things being it here, why not give |
No there is no such special treatment. Foreign functions are usually "unknown" to the compiler so it has to be very conservative, but then Rust supports cross-language LTO so e.g. inlining C functions into Rust may actually happen.
|
Okay I get it now. Seems like this issue cannot be resolved for a few days. I'll leave this open for now. |
only set noalias on Box with the global allocator As discovered in rust-lang/miri#3341, `noalias` and custom allocators don't go well together. rustc can now check whether a Box uses the global allocator. This replaces the previous ad-hoc and rather unprincipled check for a zero-sized allocator. This is the rustc part of fixing that; Miri will also need a patch.
only set noalias on Box with the global allocator As discovered in rust-lang/miri#3341, `noalias` and custom allocators don't go well together. rustc can now check whether a Box uses the global allocator. This replaces the previous ad-hoc and rather unprincipled check for a zero-sized allocator. This is the rustc part of fixing that; Miri will also need a patch.
only set noalias on Box with the global allocator As discovered in rust-lang/miri#3341, `noalias` and custom allocators don't go well together. rustc can now check whether a Box uses the global allocator. This replaces the previous ad-hoc and rather unprincipled check for a zero-sized allocator. This is the rustc part of fixing that; Miri will also need a patch.
Rollup merge of rust-lang#122018 - RalfJung:box-custom-alloc, r=oli-obk only set noalias on Box with the global allocator As discovered in rust-lang/miri#3341, `noalias` and custom allocators don't go well together. rustc can now check whether a Box uses the global allocator. This replaces the previous ad-hoc and rather unprincipled check for a zero-sized allocator. This is the rustc part of fixing that; Miri will also need a patch.
Little status update: with rust-lang/rust#122018, code like yours is no longer causing UB in the generated LLVM IR. Miri still needs to be updated to support such code though. Note that this is not a stable guarantee that the code you are writing is sound. You are using nightly features so their requirements are subject to change. Whether that pattern you are using is permitted is tracked at rust-lang/wg-allocators#122. |
@js2xxx you write
I don't understand this. Why is the thread relevant? If all Currently you are storing this entire pointer, |
Oops, I was not very clear about that. Sorry for the confusion. The "switching provenance trick" is doing something like this: let their_bin = (&*self.bins as *const [MyBin; 1])
.with_addr(ptr.as_ptr().addr() & !127)
.cast::<MyBin>(); This trick tries to match the address range and the provenance of some bin. While the collection of bins is simple (a 1-element array) in this example, it can be a large & finely crafted collection that is logically complex but contiguous in memory. But in the example above:
Edit: The trick actually passes the miri test using Tree Borrows, but is not what I want in the end. |
There's a ptr indirection here, so it's no longer the pointer in |
Btw I think there is still a bug in this code if top == 32 {
return None;
}
let ptr = unsafe { NonNull::new_unchecked(self.memory.get().add(top)) };
self.top.set(top + 1);
Some(ptr.cast()) There is only space for 8 usize in |
Oh, yes, indeed. That example is just for demonstration though, and that mistake doesn't relate to the issue we are concerned about. By the way, my allocator is nearly complete. Check it out here if you are interested. |
Rollup merge of rust-lang#122233 - RalfJung:custom-alloc-box, r=oli-obk miri: do not apply aliasing restrictions to Box with custom allocator This is the Miri side of rust-lang#122018. The "intrinsics with body" made this much more pleasant. :) Fixes rust-lang/miri#3341. r? `@oli-obk`
Your original testcase should now pass in Miri (with tomorrow's nightly) both with Stacked Borrows and Tree Borrows. I am curious what happens when you run the full allocator! |
miri: do not apply aliasing restrictions to Box with custom allocator This is the Miri side of rust-lang/rust#122018. The "intrinsics with body" made this much more pleasant. :) Fixes #3341. r? `@oli-obk`
Unfortunately, there is another problem with Code diff on the test @@ -48,6 +48,7 @@
let end = start + BIN_SIZE * mem::size_of::<usize>();
let addr = ptr.addr().get();
assert!((start..end).contains(&addr));
+ println!("{}", self.top.get());
}
}
@@ -109,7 +109,9 @@
// Make sure to involve `Box` in allocating these,
// as that's where `noalias` may come from.
fn v<T, A: Allocator>(t: T, a: A) -> Vec<T, A> {
- (Box::new_in([t], a) as Box<[T], A>).into_vec()
+ let vec = (Box::new_in([t], a) as Box<[T], A>).into_vec();
+ vec.into_boxed_slice().into_vec() // <------------------------ This one fails
+ // vec // <------------------------ This one works
}
fn main() { However, if the implementation of // Box creation and then converting to another Box.
fn v<T, A: Allocator>(t: T, a: A) -> Box<[T], A> {
Box::new_in([t], a) as Box<[T], A>
}
In summary, it seems that More precisely, Miri with Tree Borrows only fails for access to The output of miri using Stacked Borrows
The output of miri using Tree Borrows
|
That code is buggy. |
That's not the source code of the standard library shows: pub fn into_boxed_slice(mut self) -> Box<[T], A> {
unsafe {
self.shrink_to_fit();
let me = ManuallyDrop::new(self);
let buf = ptr::read(&me.buf);
let len = me.len();
buf.into_box(len).assume_init()
}
} It clearly returns with the same allocator from the original |
Oh, sorry, you are right. The issue is here: unsafe {
let slice = slice::from_raw_parts_mut(me.ptr() as *mut MaybeUninit<T>, len);
Box::from_raw_in(slice, ptr::read(&me.alloc))
} That creates a reference, which generates noalias assumptions and restricts the provenance to just that memory region the reference points to. That should probably use EDIT: Should be fixed by rust-lang/rust#122298. |
Thanks! I think all the problems will be fixed by that PR. |
RawVec::into_box: avoid unnecessary intermediate reference Fixes the problem described [here](rust-lang/miri#3341 (comment)).
RawVec::into_box: avoid unnecessary intermediate reference Fixes the problem described [here](rust-lang/miri#3341 (comment)).
Rollup merge of rust-lang#122298 - RalfJung:raw-vec-into-box, r=cuviper RawVec::into_box: avoid unnecessary intermediate reference Fixes the problem described [here](rust-lang/miri#3341 (comment)).
Just FYI, except |
Great, thanks for trying that. :) |
RawVec::into_box: avoid unnecessary intermediate reference Fixes the problem described [here](#3341 (comment)).
I'm writing an arena-like memory allocator that uses pointer arithmetic to calculate the associated bin address of the allocated pointer. Some of the structures look like this:
Example on the playground
When I tested my allocator with miri, it told me some undefined behavior occurred.
At first, I thought it was just a duplicate of #2104 and rust-lang/unsafe-code-guidelines#402 when I ran the example above with
-Zmiri-stack-borrows
, so I followed the suggestions in the comments there and switched to-Zmiri-tree-borrows
.However, when switched to
-Zmiri-tree-borrows
, the error stops happening if I useVec::with_capacity_in(1, alloc)
, but occurs again for a different reason if I use(Box::new_in([1], alloc) as Box<[u32], A>).into_vec()
(commented out in the example above), which indicates something wrong withBox
once again.The output of miri says:
It seems a Reserved region is affected by foreign writing access and thus becomes Disabled. I wonder what "magic"
Box
puts in here, and whether there is some way to solve it (or get rid of it).The text was updated successfully, but these errors were encountered: