-
Notifications
You must be signed in to change notification settings - Fork 13.3k
(0-1) as *const T triggers horribly stupid behavior #43291
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
Comments
I have a suggestion: an allow by default lint that triggers whenever inference defaults to |
I have no data but I get the impression the i32 fallback fires (legitimately) a bit too frequently for such a lint to be really useful. Besides, if it's allow by default, it doesn't really help migitate this issue, unless we can get unsafe code authors to generally set the lint to warn by default (seems unlikely). A warn-by-default pointing out integer-pointer cases that widen/truncate seems more useful. |
I agree that the lint suggested by @rkruppe would be very useful, but I am afraid it could also "mistakenly" warn on Also, would it be possible to make the integer to pointer conversion perform sign extension? |
@ranma42 In |
At the time when me and @nrc discussed casts, it turned out that |
The behavior of casts to pointers looks like a bug - I certainly intended casts to have the signedness of the source type (like how it works for ints), but I missed it while implementing. Not sure whether we want to fix it. I doubt it will break anyone's code, but it is somewhat scary. Maybe have a lint against it for enough releases and then change the behavior? |
Would a lint for "cast to pointer from type of different size" be something the portability lint RFC can do? |
This is really strange: it appears casting from a signed integer to a pointer doesn't do the same as going through That leaves the fourth Rust evaluator, miri, which @RalfJung says it produces EDIT: ah I see @arielb1 has already mentioned something along these lines in passing, but I can't find the |
With rust-lang/miri#267, miri's behavior is now consistent with rustc for casts that go through usize (well, for the ones I tested^^) and yields |
The lang team discussed this issue today, and believes it's possible to write a lint to detect this issue. We'd like to see that implemented, and do a crater run, then consider fixing the bug. Thus: punting nomination to the compiler team. |
I wrote a lint, but it triggers on |
@oli-obk That doesn't strike me as correct. |
It is correct insofar as the lint should trigger on The question is why doesn't inference pass the suggestion into the |
I mean casting |
It's part of |
Ah, custom pointer values. Just write |
Really? The bad sign extension only makes a difference for negative values, doesn't it? |
I meant in terms of alignment but it seems the |
…<try> Lint casting signed integers smaller than `isize` to raw pointers cc #43291 (keeping open as tracking issue)
Crater came through fine in #43641, so we can fix the bug! To be exact, crater showed some cases of casting from a signed integer to a raw pointer, but they all were positive values or zero. |
@oli-obk Crater doesn't test Windows though, so it didn't catch all the cases in winapi where that bug is actually hit. Fortunately in this case the fix would actually fix those old winapi versions. |
Is there any progress towards fixing this? Both PRs related to lints for this were closed. |
During this issue all examples will be done using x86_64.
When you do
0 as *const T
the type of the integer literal is inferred to beusize
. This is fine.When you do
!0 as *const T
you can observe that the resulting address is0xffffffffffffffff
which is fine.When doing FFI sometimes a handle type will be represented as a raw pointer type and sometimes there will be constants of that handle type initialized to negative values, which is entirely normal.
The user's first choice is to try
-1 as *const T
which will of course fail because the type of the integer literal is inferred to beusize
which cannot be negated because Rust hates me.Ideally Rust would be able to infer the type as
isize
instead ofusize
in this case, but I know better than to expect Rust to be actually helpful.The issue is if the user tries to be clever and do
(0-1) as *const T
which somehow seems to work but in reality they are triggering a combination of two horrible design decisions (or maybe bugs, it can be hard to tell sometimes) resulting in a giant footgun.i32
instead ofusize
. Rust earlier refused to infer the type asisize
instead ofusize
, so why is it now inferring the type asi32
?i32
to*const T
does zero extension instead of sign extension which is different than what C++ does. Add lint for u8 as *mut cast #42915As a result, if you check the resulting address of
(0-1) as *const T
you'll see that it gives0xffffffff
which is wrong.The correct answer is achieved by doing
-1isize as *const T
which results in the correct address of0xffffffffffffffff
.Ideally both of those two points above would be fixed, but at the very least there should be a very loud warning that you are doing something very horribly wrong.
And yes, I found examples of this in winapi so some care will be needed when fixing this in order to not break the ecosystem.
Possible solutions
i32
instead ofusize
.The text was updated successfully, but these errors were encountered: