-
Notifications
You must be signed in to change notification settings - Fork 371
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
Function pointer address compares are misleading #2882
Comments
miri
In miri, every time you create a function pointer for a function, you get a new address. The same function pointer will be equal to itself, but never equal to any other function pointer. This is pessimistic, as there are situations where rustc + llvm can actually behave this way (although usually not as extreme). rust-lang/rust#70861 is related, but I think you can get the same behaviour with comparisons instead of matches. |
Basically Rust makes no guarantees at all when it comes to fn ptr equality: the 'same' function can have multiple addresses (e.g. if it is called from multiple codegen units / crates, leading to multiple monomorphizations) and different functions can have the same address.
Note that debug and release builds are just 2 of the many many possible ways a Rust program can execute, depending on compiler versions, crate structure, etc. If you go into unspecified territory, you can't just consider the 2 cases of debug and release builds. |
Specifically, the In Arguably the first two coercions will always be in the same crate + codegen unit since they occur in the same function, so one could consider guaranteeing that they will yield the same address (but Rust currently documents no such guarantee). However crucially your entire construction relies on the coercion site in |
Ah I see, thank God I checked miri :) Maybe then this would be a follow up design question, how do I model such a thing to work across crates? There might be a case where I might not be able to look at the |
I'm not sure what the established best practices are for type erasure. I'd suggest asking around on Zulip. :) |
Consider the case where I want to take any type
T
and convert it into someOpaque
struct as so, comparing just the function pointers would give me a meaningful way to determine that thisOpaque
was created by theopaquify
function. To ensure that generics that alias because of similar implementations (as shown below) don't cause undefined behavior thetype_name
field is used.This above program executes as expected on debug and release however something weird happens when I introduce
miri
into the mix. Below is the comparisons between outputs on different modes.Debug builds
Here we can see that generic
fptr
s don't alias and behaves as expected.Release mode
Here functions do alias because of presumably an LLVM pass that merges functions but because of the type check
unopaquify
knows when it's safe to convert.Miri
Run with:
MIRIFLAGS="-Zmiri-ignore-leaks" cargo miri run
The
dbg
logs fromopaquify
suggests thatfptr
do NOT alias and should follow the same branches as debug build. However, the firstunopaquify
call has an fptr which has the value completely different than any of the ones returned. I'm not sure what0x000000000003d3a3
or0x00000000000548f9
point to but based on the initial logs it's neitherffi::<usize>
norffi::<u32>
. And hence the tests fail.Is this an expected false positive from miri and can be safely ignored, or is this actual UB that I'm failing to understand?
https://play.rust-lang.org/?version=nightly&mode=release&edition=2021&gist=c7009ed9436d42f9d78d59d8ef95abd5
The text was updated successfully, but these errors were encountered: