This is an archive of the discontinued LLVM Phabricator instance.

[Asan] Ensure unpoisonning doesn't get inlined unnecessarily due to small holes in the mask
Needs RevisionPublic

Authored by saudi on Feb 8 2023, 2:43 AM.

Details

Summary

In the case of allocas introduced by the Address Sanitizer, the shadow bytes may contain sparse zeros, missing opportunities for grouping into __asan_set_shadow_00 calls, causing inlining despite the "asan-max-inline-poisoning-size" argument value.

This fix ignores small holes in the shadow bytes when unpoisonning.

Diff Detail

Event Timeline

saudi created this revision.Feb 8 2023, 2:43 AM
saudi requested review of this revision.Feb 8 2023, 2:43 AM
saudi updated this revision to Diff 495791.Feb 8 2023, 3:25 AM
MaskRay added inline comments.
llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
2956

llvm style prefers pre-increment.

rsundahl added inline comments.Feb 9 2023, 9:14 AM
llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
2964

This function is probably not the place to guess that it's ok to ignore the shadow mask because it's assumed that it's being called for un-poisoning. While this may be the case, the shadow mask and shadow data provide high fidelity control over behavior and assuming the intent at this low level is problematic. I realize that at a line 2888 the same assumption is made during inlining, but moving this up to the caller and cleaning up these two functions would be far superior.

llvm/test/Instrumentation/AddressSanitizer/asan-optimize-inline-poisoning.ll
2

Your test when run with -asan-max-inline-poisoning-size=0 still inlines but it doesn't look like it is due to your changes (unless datalayout is contrived for the test and doesn't occur otherwise.) It's more likely that my testing for https://reviews.llvm.org/D136197 was inadequate.

saudi added inline comments.Feb 10 2023, 1:10 PM
llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
2964

I can take a look and try to move the logic to a higher level.
The copyToShadow declaration has a comment attached:

// Copies bytes from ShadowBytes into shadow memory for indexes where
// ShadowMask is not zero. If ShadowMask[i] is zero, we assume that
// ShadowBytes[i] is constantly zero and doesn't need to be overwritten.

Would the solution be to:

  1. remove from copyToShadow / copyToShadowInline the assumption about zero (and remove the second sentence from the comment I mentionned above)
  2. Add a method bridgeUnpoisonningGaps that would optimize the shadow buffer so that the generated instructions are more optimized

?

I'm a bit stuck, as this may make bridgeUnpoisonningGaps assume too much about the optimization details.

What do you think?

llvm/test/Instrumentation/AddressSanitizer/asan-optimize-inline-poisoning.ll
2

I tried this on trunk, but the output didn't seem to contain inlined poisonning. see https://llvm.godbolt.org/z/qWr3P54Ex

The generated stores don't look related with [un]poisonning, as they don't write to shadow memory.

Or did I miss something?

rsundahl added inline comments.Feb 20 2023, 9:08 AM
llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp
2964

An optimization may be a much better place to capture the idea and your proposal seems good provided more senior reviewers are supportive . I have looked at this loopy/call/inline code enough to wish it simpler and if the shadow mask wasn't broken up into these smaller fragments then the code could be simpler and more readable. (This effectively would be equivalent to doing a local pre-scan of the shadowMask and setting it to 1 wherever the shadowData was 0, and then falling into the loop. Might be a good way to test it first.)

llvm/test/Instrumentation/AddressSanitizer/asan-optimize-inline-poisoning.ll
2

I think you'll find the stores at line 69 and line 139 are inline accesses to the shadow memory. That said, I don't think that this is a consequence of your change but more a result of the logic in the loop that's already there.

vitalybuka requested changes to this revision.Aug 27 2023, 10:18 PM

Is this still relevant?

This revision now requires changes to proceed.Aug 27 2023, 10:18 PM
saudi added a comment.Aug 28 2023, 9:26 AM

Is this still relevant?

Hello, yes it still is to us, we are using this in our fork. I didn't have time to address the comments. and I would need some time also to get back into it, understanding the specifics and how to refactor this.

The real-life context that made this optimization necessary:

  • Windows x64 target, with EH enabled
  • a large function with many blocks, most of which contained local variables often with small gaps between them due to alignment etc. (it was a catch2 framework unittest, where a TEST_CASE contained a big hierarchy SECTION)

With EH enabled, a large number of cleanup pads were generated, causing a lot of variables deinitialization code.
Having small gaps between variables, the shadow poisoning would be split into many inlined parts

The result was very large code size output by asan (>100MB object files), most of which was poisoning code.