-
Notifications
You must be signed in to change notification settings - Fork 13.1k
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
Miscompilation when using wrapping_sub/wrapping_add on pointer. #80309
Comments
Here’s a similar miscompilation, using only safe Rust code pub fn ptr(x: *const i8) -> *const i8 {
x.wrapping_offset(-(x as isize)).wrapping_offset(x as isize)
}
pub fn zero(x: *const i8) -> isize {
(ptr(x) as isize).wrapping_add(-(x as isize))
}
pub fn qux(x: &[i8]) -> i8 {
x[zero(x.as_ptr()) as usize]
}
fn main() {
let z = vec![42, 43];
println!("{}", qux(&z));
}
@rustbot modify labels: C-bug, T-compiler |
After a bit of godbolt testing, this seems to happen since 1.16.0, when |
Reduced: https://llvm.godbolt.org/z/qec9oz |
LLVM bug report: https://bugs.llvm.org/show_bug.cgi?id=48577 |
Simplified the pub fn zero(x: usize) -> usize {
std::ptr::null::<i8>().wrapping_add(x) as usize - x
}
pub fn qux(x: &[i8]) -> i8 {
x[zero(x.as_ptr() as usize)]
}
fn main() {
let z = vec![42, 43];
println!("{}", qux(&z));
} |
Assigning |
As per documentation of Given that reasoning the fix that has landed (llvm/llvm-project@899faa5) to LLVM smells like it could be partial and we might still be prone to the same issue if it happens through some other contrived sample code? We could potentially work-around this in Rust as well by implementing these wrapping operations in a way that discards provenance entirely for now (probably via |
Agreed.
AFAIK, the entire reason that |
Well, temporarily work around. I suspect fixing the issue at the root in LLVM might take some time. |
Note that llvm/llvm-project@899faa5 was reverted in llvm/llvm-project@ef2f843: the underlying issue is tracked at https://bugs.llvm.org/show_bug.cgi?id=44403 |
This was later fixed by https://reviews.llvm.org/D93820. |
So can we cherry-pick that into the rustc LLVM fork, or what is the usual process? |
I believe this is resolved by the bump to LLVM 12. |
Reopening this as it does not appear to be fixed yet :( |
https://reviews.llvm.org/rG2ad1f5eb1a47275593bd9baf75dcf3e9c3977473 is the last changeset that is necessary to resolve this fully. Given that promotion to LLVM12 happened fairly recently, we may want to backport the commit. |
Hm, but that optimization doesn't sound wrong, it just sounds bad. Some other actually wrong optimization would be needed to turn this into a miscompilation. |
Well, it effectively manifests a constant ; code corresponds to `x.wrapping_sub(x as usize)`
define i8* @initial(i8* %b) {
%b_ptr = ptrtoint i8* %b to i64
%sub = sub i64 0, %b_ptr
%gep = getelementptr i8, i8* %b, i64 %sub
ret i8* %gep
}
define i8* @step1(i8* %b) { ; no provenances remain in this function, ptrtoint discards that information
%b_ptr = ptrtoint i8* %b to i64
%1 = ptrtoint i8* %b to i64
%2 = sub i64 %1, %b_ptr ; constant 0
%gep = inttoptr i64 %2 to i8* ; becomes a constant `null` pointer, with a provenance of null object
ret i8* %gep
} To complete the original reproducer we then have these two instructions: %wrapping_add = getelementptr i8, i8* null, i64 %b_ptr
%deref = load i8, i8* %wrapping_add, align 1
; becomes=>
%deref = i8 undef ; because we're dereferencing a pointer with a provenance of a null object. So I guess if we call anything else wrong, its probably going to be |
Ah... that is interesting. It sounds potentially problematic. Usually, when I start with a pointer What you are saying is that if the pointer has physical address 0, then this is not true. |
This is fixed on nightly by the LLVM 13 upgrade. Probably worth adding a test for this. |
Dropping priority as the issue is fixed. |
… r=oli-obk Add regression tests for issue 80309 Closes rust-lang#80309 😝 I'm not sure where to put the tests, is `ui/issues` the right place for this kind of tests?
Relevant comment on IRLO. The following code leads to
illegal instruction
in release mode. (It works fine, printing42
in debug mode.)Apparently, leaving the object
x
with awrapping_sup
, then going back into the object withwrapping_add
and dereferencing the resulting pointer is supposed to be safe (although there is still an open issue (#80306) about properly documenting that this is safe).As discussed in the linked IRLO thread, what’s probably happening here is that LLVM realizes that the first
x.wrapping_sub(x as _)
evaluates to the null pointer, and then considers the code equivalent to something like*std::ptr::null().wrapping_add(x as _)
which is then detected as UB (dereferencing some integer offset of the null pointer), hence the illegal instruction.(Playground)
Errors:
The text was updated successfully, but these errors were encountered: