-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
pointer addition semantics are unclear #130211
Comments
This is a bug in const eval and miri. regression in #128482 cc @RalfJung We are just computing rust/compiler/rustc_const_eval/src/interpret/operator.rs Lines 306 to 316 in 6f7229c
@rustbot label -needs-triage -A-docs +C-bug +T-compiler +A-const-eval +regression-from-stable-to-beta |
Surely that is not the case? We want to emit To me the current wording "The computed offset, count * size_of::() bytes, must not overflow isize" implies no overflow in both the unsigned and signed sense. Unsigned because this is an unsigned multiplication, and signed due to the additional restriction that the result must fit in isize as well. |
My interpretation of "computation must not overflow an isize" is "do the multiplication on mathematical values; the result must fit in the value range of an isize".
But you are right that this contradicts the part about "convenience for offset(x as isize)".
Regarding the Miri behavior, it seems the intrinsic at some point got changed to directly accept a usize <#110837> but opsem/miri teams did not get informed so the logic in Miri does not make sense for this way of using the intrinsic.
|
#130229 should fix this. Thanks for catching this, @jwong101 !
Since the resulting offset must fit in an |
I have a clarification question about this example: #[no_mangle]
pub const fn mul() -> i32 {
const {
let x = &[0i32; 2];
let x = x.as_ptr().wrapping_add(1);
unsafe {
// has UB according to MIRI and fails compilation on nightly if you comment this out
// mul nuw i64 -1, 4 is poison
// x.add(!0).read();
// DB and passes compilation
// mul nsw i64 -1, 4 is defined
x.offset(-1).read();
}
x[0]
}
} Where does the |
That's also what I figured by looking at the source code and the reason why I initially thought that Miri/CTFE treating them differently was just a bug. |
It was definitely an accident -- Miri did multiplication and overflow check at "whatever the type of the |
Sorry for the confusion. I was just using
Yeah, we just use
Yeah, I was curious on if it was legal for us to use the new |
Yeah, I was initially confused when I saw that Though now that I think about it, I was probably just using |
Rollup merge of rust-lang#130229 - RalfJung:ptr-offset-unsigned, r=scottmcm ptr::add/sub: do not claim equivalence with `offset(c as isize)` In rust-lang#110837, the `offset` intrinsic got changed to also allow a `usize` offset parameter. The intention is that this will do an unsigned multiplication with the size, and we have UB if that overflows -- and we also have UB if the result is larger than `usize::MAX`, i.e., if a subsequent cast to `isize` would wrap. ~~The LLVM backend sets some attributes accordingly.~~ This updates the docs for `add`/`sub` to match that intent, in preparation for adjusting codegen to exploit this UB. We use this opportunity to clarify what the exact requirements are: we compute the offset using mathematical multiplication (so it's no problem to have an `isize * usize` multiplication, we just multiply integers), and the result must fit in an `isize`. Cc `@rust-lang/opsem` `@nikic` rust-lang#130239 updates Miri to detect this UB. `sub` still has some cases of UB not reflected in the underlying intrinsic semantics (and Miri does not catch): when we subtract `usize::MAX`, then after casting to `isize` that's just `-1` so we end up adding one unit without noticing any UB, but actually the offset we gave does not fit in an `isize`. Miri will currently still not complain for such cases: ```rust fn main() { let x = &[0i32; 2]; let x = x.as_ptr(); // This should be UB, we are subtracting way too much. unsafe { x.sub(usize::MAX).read() }; } ``` However, the LLVM IR we generate here also is UB-free. This is "just" library UB but not language UB. Cc `@saethlin;` might be worth adding precondition checks against overflow on `offset`/`add`/`sub`? Fixes rust-lang#130211
Location
core::ptr::add
rust/library/core/src/ptr/const_ptr.rs
Lines 810 to 854 in 33855f8
core::ptr::offset
rust/library/core/src/ptr/const_ptr.rs
Lines 349 to 393 in 33855f8
Summary
The documentation for
core::ptr::add
currently states that it:rust/library/core/src/ptr/const_ptr.rs
Line 810 in 33855f8
Except it's not a convenience for just doing
.offset(count as isize)
, both according to Miri and the constant interpreter:So one might infer that the following requirement:
rust/library/core/src/ptr/const_ptr.rs
Line 819 in 33855f8
means that the
count * size_of::<T>()
must not overflow in the unsigned sense foradd
and signed sense foroffset
.However, it's unclear if
overflowing isize
means the unsigned multiplication result has to be positive, or if the unsigned multiplication also has to not wrap in the signed sense. Both Miri and the constant interpreter allow the following:However,
x.add(offset)
will also get lowered togetelementptr inbounds i32, %x, i64 %offset
, which has UB according to the LLVM langref.If the
core::ptr::add
does have defined behavior in the above case, then we can't directly usegetelementptr inbounds
for types larger than a byte, without doing amul nuw
beforehand, and then doinggetelementptr inbounds i8
. However, if it's UB, then the docs should make it clear.So
core::ptr::add
still carries the responsibility of preventing signed overflow in the multiplication(at least according to our codegen semantics). However, it's unclear if "must not overflow isize", means that the unsigned result has to fit in[0, isize::MAX]
, as the following passes:To summarize:
count * size_of::<T>()
need to not overflow in both the signed and unsigned sense forcore::ptr::add
?core::ptr::add
, is the resulting offset treated as a signed integer or unsigned integer for the following requirement:rust/library/core/src/ptr/const_ptr.rs
Lines 821 to 824 in 33855f8
Rationale for inquiry:
LLVM recently added a
nuw
attribute to thegetelementptr
instruction in order to inform the optimizer that the offset is non-negative. I was checking thecore::ptr::add
documentation to see if we could use it for that, however, I found some contradictory information w.r.t the docs and the current behavior of the constant interpreter and Miri.The text was updated successfully, but these errors were encountered: