Repository navigation
Miscompilation: Equal pointers comparing as unequal #107975
Copy link
Copy link
Open
Labels
A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-external-bugCategory: issue that is caused by bugs in software beyond our controlCategory: issue that is caused by bugs in software beyond our controlI-miscompileIssue: Correct Rust code lowers to incorrect machine codeIssue: Correct Rust code lowers to incorrect machine codeI-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-highHigh priorityHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.WG-llvmWorking group: LLVM backend code generationWorking group: LLVM backend code generation
Description
pub fn foo() -> (usize, usize, bool) {
let a: *const u8;
let b: *const u8;
{
let v: [u8; 16] = [std::hint::black_box(4); 16];
a = &(v[0]);
}
{
let v: [u8; 16] = [std::hint::black_box(4); 16];
b = &(v[0]);
}
(a as usize, b as usize, a == b)
}
fn main() {
println!("{:?}", foo());
}I expected to see this happen: Either the pointers (when cast to integers) are the same and the comparison is true, or they are not the same and the comparison is false.
Instead, this happened: It printed: (140728325198984, 140728325198984, false)
Meta
Reproduced via rustc +nightly -Copt-level=3 test.rs && ./test.
rustc --version --verbose:
rustc 1.69.0-nightly (5b8f28453 2023-02-12)
binary: rustc
commit-hash: 5b8f284536d00ba649ca968584bedab4820d8527
commit-date: 2023-02-12
host: x86_64-unknown-linux-gnu
release: 1.69.0-nightly
LLVM version: 15.0.7
Also reproduces on master.
@rustbot label +I-unsound +T-compiler +A-llvm
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
A-LLVMArea: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues.C-external-bugCategory: issue that is caused by bugs in software beyond our controlCategory: issue that is caused by bugs in software beyond our controlI-miscompileIssue: Correct Rust code lowers to incorrect machine codeIssue: Correct Rust code lowers to incorrect machine codeI-unsoundIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessIssue: A soundness hole (worst kind of bug), see: https://en.wikipedia.org/wiki/SoundnessP-highHigh priorityHigh priorityT-compilerRelevant to the compiler team, which will review and decide on the PR/issue.Relevant to the compiler team, which will review and decide on the PR/issue.WG-llvmWorking group: LLVM backend code generationWorking group: LLVM backend code generation
Activity
JakobDegen commentedon Feb 13, 2023
Godbolt indicates that this is EarlyCSE in LLVM optimizing the pointer comparison to false despite the two stack allocations have non-overlapping lifetime ranges.
zirconium-n commentedon Feb 13, 2023
This seems related to pointer provenance.
JakobDegen commentedon Feb 13, 2023
The bug itself or the cause of the bug? I don't know what the incorrect reasoning that LLVM is using here is, so I can't comment on the cause of the issue. But both Rust and LLVM clearly define pointer comparison to be address based, which means that I don't think we need to think about provenance in order to conclude that this is a miscompilation
DoubleHyphen commentedon Feb 13, 2023
tl;dr even if the docs were changed to take provenance into account when comparing for equality, there would still be a miscompilation, because evaluating the same expression twice leads to different results!
Playground link
This was exactly the train of thought that led me to experiment, thereby uncovering the bug: “Can two pointers compare as equal, even when they belong to different allocations?” I experimented, figured out that they cannot, and tried to refactor my
println!s, whereupon the program started behaving differently. (In the second linked program, uncommenting line 18 changes the output of line 19.)PS: In case it matters, my experiments were on the stable channel.
RalfJung commentedon Feb 13, 2023
In theory they could define pointer comparison to be non-deterministic, and then this would be okay. For a comparison that involves provenance, that is probably the case.
But to my knowledge that's not how LLVM intends their ptr comparison to work.
That last example is really strange though, why would it optimize only one of them? Also I was unable to reproduce this without printing, which is equally strange...
DoubleHyphen commentedon Feb 13, 2023
I thought that was queer, so I experimented further.
What I found is as follows:
println!, the behaviour remains stable.a==balways evaluates tofalse, and even ifaandbare cast to integers and compared, the comparison still returns false. In other words, the code (continued from above)outputs
to
stderr, which –to the best of my knowledge– is not exactly how integers are supposed to work.println!,a==bstarts evaluating totrue. Casting them both tou128before printing them does not cause that; only egprintln!("{a:?}")does.Imberflur commentedon Feb 13, 2023
I tried some alternatives to
println!, it looks likeblack_box(format_args!("{:?}", a))is sufficient for it to start evaluating totrue:RalfJung commentedon Feb 13, 2023
The wild thing is that
black_box(a)doesn't cause this... so format_args is an even blacker box than black_box, or so? black_box is supposed to be a wildcard, and for all LLVM knows it might be calling format_args...In fact looks like adding black_box actually enables the optimization?!?
Imberflur commentedon Feb 13, 2023
Reduced to
black_box(&a)(edit: here is a playground link)JakobDegen commentedon Feb 13, 2023
Yeah, this isn't all too surprising, the difference between
black_box(a)andblack_box(&a)is quite significant112 remaining items
Auto merge of rust-lang#127003 - GrigorenkoPV:107975, r=SparrowLii
Add a bunch of tests for rust-lang#107975
Auto merge of rust-lang#127003 - GrigorenkoPV:107975, r=<try>
Auto merge of rust-lang#127003 - GrigorenkoPV:107975, r=SparrowLii
Add a bunch of tests for rust-lang#107975
Add a bunch of tests for rust-lang#107975
Add a bunch of tests for rust-lang#107975
Auto merge of rust-lang#127003 - GrigorenkoPV:107975, r=SparrowLii
tmiasko commentedon Mar 21, 2025
Two additional upstream issues that break comparison involving stack variables:
A Rust reproducer for the second issue below. It asserts that pointers to two distinct variables live at the same time are not equal. In contrast to the earlier examples, this one makes the comparison when both variables are live.
Edit:
This example also happens to be miscompiled by gcc.
GCC bug report https://gcc.gnu.org/bugzilla/show_bug.cgi?id=119472
[squashme] fix tests
ThomasHabets commentedon May 15, 2025
#141021 is about this code compiling, and producing two
u64of the same value that don't compare equal in--releasemode.