-
Notifications
You must be signed in to change notification settings - Fork 349
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
Miri can't track pointer through atomic bit-flip, which happens as usize #1993
Comments
Related issue for the same situation only in LLVM: Basically we need a way to modify bits of a pointer as long as they only affect things that are not part of the base address. We used to have some of that in miri, but never exposed it to users. In addition to the existing |
For non-atomic pointer manipulation, a solution exists, see this discussion and also here. At least as far as Miri provenance tracking is concerned, that basically handles the cases Oli mentioned: /// Converts 'addr' to a pointer using the provenance of 'prov'.
fn int_to_ptr_with_provenance<T>(addr: usize, prov: *const T) -> *const T {
// Nowhere in this function do we cast an integer to a pointer!
let ptr = prov.cast::<u8>();
ptr.wrapping_add(addr.wrapping_sub(ptr as usize)).cast()
}
let addr = ptr as usize;
// do bit-level stuff with addr to compute new_addr
let new_ptr = int_to_ptr_with_provenance(new_addr, ptr); This code never casts an integer to a pointer and thus avoids all the problems associated with that operation. However, that is likely not enough for atomic pointer manipulation. I'll take a look at this example later to understand what is needed. |
Hmm, I think I could probably apply that to the code that's there today, since it does non atomic manipulation and then compare_exchanges it back. Will do that once I get a chance! However the next optimization I had planned for that piece of code was to swap the compare exchange for a |
Yes, Basically we'd need fetch_or on AtomicPtr to even make this expressible. |
Maybe at least for now an option is to "polyfill" such a fetch_or on Miri by doing a compare_exchange loop instead? Clearly that's not a proper solution though. |
Yeah, it's a good idea — I'll take a look at making that change in haphazard for the time being with a link back to this :) |
So, at one point I considered trying to mock up what the API would look like for a small family of Unfortunately, largely I came to the conclusion it wasn't really viable. That it would just be a lot easier to find a way to either allow pointer->integer conversions, to make the atomic types generic, or to just make the existing atomic types behave this way, even if it means that they have provenance now. That said, the main motivation for this exploration was "how can I CAS a I also have some... reasonably strong feelings about turning the current pointer->integer->pointer casts2 into UB being a stability break too (and personally I think stability is more important3 than allowing aggressive optimizations around pointers, but I know many others disagree with me here, or disagree that it is actually a violation of our stability rules). All that said, I suppose it wouldn't be hard to mock up what adding atomic ops directly to AtomicPtr would look like, as an unstable API. This might require new intrinsics for them, although perhaps the functions are enough, and miri could detect that e.g. the That said, something here is probably needed to keep miri working when we integrate parking_lot into libstd, or more broadly improve our lock impls. For example, https://github.com/Amanieu/parking_lot/blob/78f09f45d6b8b34ec23afdf7a696cbe54d0a469b/core/src/word_lock.rs 4 does bitor on an AtomicUsize that holds a pointer. Footnotes
|
I absolutely hear you. I view this as exploring possibilities.
And that already is assuming that a ptr2int cast performs a sort of "broadcast" of the provenance, meaning transmutes are entirely out. I admit we lack data on how useful these optimizations are in practice. And there always is a slight chance that some clever trick could avoid this conundrum. :D |
This enables Miri to track pointer provenance even for our raw pointers, which allows us to check with `-Zmiri-tag-raw-pointers`. Yay! See rust-lang/miri#1993 for details.
I implemented the suggested workaround, and documented what we'd need to do for |
FWIW here's a convenient wrapper around that cast function that has been proposed several times on Zulip: fn int_to_ptr_with_provenance<T>(addr: usize, prov: *const T) -> *const T {
// Nowhere in this function do we cast an integer to a pointer!
let ptr = prov.cast::<u8>();
ptr.wrapping_add(addr.wrapping_sub(ptr as usize)).cast()
}
fn ptr_map_addr<T>(ptr: *const T, f: impl FnOnce(usize) -> usize) -> *const T {
int_to_ptr_with_provenance(f(ptr as usize), ptr)
}
let x = 12u16;
let tagged_x = ptr_map_addr(&x, |addr| addr | 0x1); |
I actually don't think that we necessarily need the "are not part of the base address" restriction. This restriction is important in C because in C people expect that if they figure out the pointer's address - no matter how - it's ok to use it for an int->ptr cast. But in Rust I don't think that will necessarily be a huge concern, because the rules are sort of more complicated already. People have even talked about having an explicit "pointer to int without broadcasting" (if we do choose semantics that involve broadcasting) operation. |
I hope this will be resolved by #2133. |
There is no |
rust-lang/rust#96935 is suggesting to add some more operations to |
Ah, sorry, yes, I was thinking of |
If you use proper casts to go from a ptr to |
In
haphazard
, I maintain a concurrent linked list of things that can be "locked" through a bit flip in thenext
pointer. The relevant code is here:https://github.com/jonhoo/haphazard/blob/ba8ffcceba6f0a0b117c0e35d2ab05a6c1bb5233/src/domain.rs#L259-L281
It basically looks like this:
I would love to run this code through Miri with
-Zmiri-tag-raw-pointers
(it runs fine without), but then I (as expected) run into "parent tag " due to the int-to-ptr conversion, which matches the README's note saying " You can recognize false positives by<untagged>
occurring in the message -- this indicates a pointer that was cast from an integer, so Miri was unable to track this pointer." You can replicate the issue using:Is there any way for me to "help" Miri realize what's going on here? For reference, the pointer stores happen here:
https://github.com/jonhoo/haphazard/blob/ba8ffcceba6f0a0b117c0e35d2ab05a6c1bb5233/src/domain.rs#L320-L322
and here
https://github.com/jonhoo/haphazard/blob/ba8ffcceba6f0a0b117c0e35d2ab05a6c1bb5233/src/domain.rs#L337-L350
The text was updated successfully, but these errors were encountered: