This is an archive of the discontinued LLVM Phabricator instance.

Handle previously ASAN-instrumented IR gracefully when ASAN re-invoked
Needs RevisionPublic

Authored by tejohnson on Dec 13 2017, 7:48 PM.

Details

Reviewers
None
Summary

Fixes the case when invoking -fsanitize=address twice due to serializing
through bitcode or LLVM assembly and then compiling that down to native
code. Without this there are assertion failures from the instrumentation
passes.

Fixes PR32700.

Event Timeline

tejohnson created this revision.Dec 13 2017, 7:48 PM
vsk added a comment.Dec 13 2017, 8:17 PM

Is it possible to reduce the test case s.t the test file doesn't contain instrumented IR? ASan instrumentation is prone to change, so I worry that the test could start passing for invalid reasons. This might happen if 1) __asan_before_dynamic_init is renamed, 2) GlobalsMetadata::doInit is written more defensively, and 3) a non-idempotent ASan transform is introduced later.

I envision something like:

RUN: opt -asan -asan-module < %s -S -o %t.ll
RUN: opt -asan -asan-module < %t.ll -S -o %t.2.ll
RUN: diff %t.ll %t.2.ll

I spent a few minutes trying to come up with a test like this but did not succeed, because my test file didn't trip the assert you hit.

In D41222#954847, @vsk wrote:

Is it possible to reduce the test case s.t the test file doesn't contain instrumented IR? ASan instrumentation is prone to change, so I worry that the test could start passing for invalid reasons. This might happen if 1) __asan_before_dynamic_init is renamed, 2) GlobalsMetadata::doInit is written more defensively, and 3) a non-idempotent ASan transform is introduced later.

I envision something like:

RUN: opt -asan -asan-module < %s -S -o %t.ll
RUN: opt -asan -asan-module < %t.ll -S -o %t.2.ll
RUN: diff %t.ll %t.2.ll

I spent a few minutes trying to come up with a test like this but did not succeed, because my test file didn't trip the assert you hit.

That's a great idea. The reason you weren't able to trigger it is because what is being tripped over the second time is the llvm.asan.globals metadata, which is inserted by clang via SanitizerMetadata::reportGlobalToASan. I was able to get this in the .ll by building the .c file with "-fsanitize=address -emit-llvm -Xclang -disable-llvm-passes". Then running it through opt twice in the manner you describe above hits the assert. I was then able to simplify the initial .ll file even more and still hit the original assert.

Updated test case being uploaded momentarily.

tejohnson updated this revision to Diff 126972.Dec 14 2017, 8:33 AM

Simplify test case

vitalybuka accepted this revision.Dec 14 2017, 10:00 AM

Could you please clang-format the patch for consistency?

This revision is now accepted and ready to land.Dec 14 2017, 10:00 AM
vsk accepted this revision.Dec 14 2017, 10:39 AM

Really nice, lgtm!

Could you please clang-format the patch for consistency?

clang-format didn't find anything. Is there something specific that doesn't look right?

vitalybuka added inline comments.Dec 14 2017, 10:53 AM
lib/Transforms/Instrumentation/AddressSanitizer.cpp
2422

Simple if statements here and below. However it looks like that existing ifs do not follow LLVM style. So please keep the patch as is.

pcc requested changes to this revision.Dec 14 2017, 11:28 AM
pcc added a subscriber: pcc.

If this doesn't even fix the ThinLTO bug I don't see why we should land it. The other scenarios mentioned on the bug are "no warranty" scenarios because they use flags like -emit-llvm which aren't really user facing.

This revision now requires changes to proceed.Dec 14 2017, 11:28 AM
In D41222#955655, @pcc wrote:

If this doesn't even fix the ThinLTO bug I don't see why we should land it. The other scenarios mentioned on the bug are "no warranty" scenarios because they use flags like -emit-llvm which aren't really user facing.

The fix is small though, and seems better than asserting (or a runtime error as in the release build case)?

pcc added a comment.Dec 14 2017, 12:49 PM

The asan pass is more like something like codegenprepare in that we shouldn't really expect it to be run twice. If it does run twice, it most likely indicates a logic error in the pass pipeline, and it would be best to diagnose that with an assertion failure rather than silently ignoring it (which could also hide other unnecessary duplication of passes in the pipeline).

Besides which, this fix is not complete. If a module does not contain dynamically-initialized globals and the globaldce pass happens to run between the two invocations of the asan passes then the reference to the __asan_before_dynamic_init function will be dropped and the passes will still run twice.

If we really wanted to do something about this I think we should be inserting module-level metadata and testing for presence of that metadata (and, ideally, asserting if we see the metadata).