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
MemorySanitizer false positives in optimized code #28428
Comments
Full bitcode for main() at -O3: define i32 @main() #0 { entry.split.us: ; preds = %entry for.body.us.preheader: ; preds = %entry.split.us entry.split: ; preds = %entry for.body.preheader: ; preds = %entry.split for.body.us23.preheader: ; preds = %entry.split if.then6: ; preds = %for.body.us23.preheader, %entry.split.us if.end8: ; preds = %for.body.us.preheader, %for.body.us23.preheader, %if.then6 |
IR test case ... |
This is causing the failures on the sanitizer-bootstrap bot that started in the build with the revision range (271398, 271414], but with this test case I can reproduce the bug a lot earlier, at least at r246152 (Aug 2015). |
Simplified test case: volatile int sink; This loop is unswitched on (y), this creates an unconditional load (and "msan-use") of y independent on the value of x. This is bad for MSan (y may not be initialized if x == false) and for TSan (there could be concurrent writes to y if x == false). My idea is to disable this optimization for MSan and TSan if the unswitch condition does not post-dominate the loop header, i.e. unswitching is safe if the condition is evaluated in all executions. |
Proposed fix: http://reviews.llvm.org/D21165 To correct the last comment, TSan is fine - it would never speculate the load of "y". MSan is fine with speculative loads and stores, but it is unhappy about speculative branch instructions - that's when MSan checks the shadow. In the example below "if (y)" can not be moved out of the loop, even though there are no loads. volatile int sink; |
Here is another example where it is basically impossible for MSan to This loop is unswitched on y, then we have 2 copies of the loop volatile int sink; The root of the problem is in the difference between the LLVM The fix in http://reviews.llvm.org/D21165 should be enough short-term. For a long term fix, these 2 options came up in a discussion with Chandler:
As a big plus of this approach, it would make MSan a lot more On the negative side, MSan will become less useful as a tool to catch |
mentioned in issue llvm/llvm-bugzilla-archive#46144 |
Extended Description
$ cat 1.cc
#include <stdio.h>
#include <sanitizer/msan_interface.h>
struct Map {
unsigned Small;
long Buckets;
long *Ptr;
unsigned NumBuckets;
Map() {
Small = true;
Buckets = 42;
__msan_allocated_memory(&NumBuckets, sizeof(NumBuckets));
}
long *getBuckets() {
return Small ? &Buckets : Ptr;
}
unsigned getNumBuckets() {
return Small ? 1 : NumBuckets;
}
};
Map M;
int main() {
for (int i = 0; i < 1; ++i) {
if (M.getNumBuckets() != 0) {
if (42 == *M.getBuckets())
return 1;
}
}
if (!M.Small)
printf("zz\n");
}
$ clang++ 1.cc -std=c++14 -g -O3 -fsanitize=memory && ./a.out
WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x4879c3 in main 1.cc:30:9
#1 0x7f12aeff9f44 in __libc_start_main
#2 0x419b11 in _start
Looking at the bitcode, the first thing the program does is compare NumBuckets with 0 and branch on the result. That's what MSan is complaining about. The source code reads NumBuckets only when Small == 0, which is impossible.
This happens only at -O3.
$ clang++ 1.cc -std=c++14 -g -O3 -S -emit-llvm -o -
...
%struct.Map = type <{ i32, [4 x i8], i64, i64*, i32, [4 x i8] }>
...
define i32 @main() #0 {
entry:
%0 = load i32, i32* getelementptr inbounds (%struct.Map, %struct.Map* @M, i64 0, i32 0), align 8
%tobool.i20 = icmp eq i32 %0, 0
%1 = load i32, i32* getelementptr inbounds (%struct.Map, %struct.Map* @M, i64 0, i32 4), align 8
%cmp1 = icmp eq i32 %1, 0
%2 = load i64*, i64** getelementptr inbounds (%struct.Map, %struct.Map* @M, i64 0, i32 3), align 8
br i1 %cmp1, label %entry.split.us, label %entry.split
The text was updated successfully, but these errors were encountered: