This is an archive of the discontinued LLVM Phabricator instance.

[ASAN] Create a new UseAfterScopeError
AbandonedPublic

Authored by marxin on Nov 22 2016, 4:22 AM.

Details

Reviewers
kcc
samsonov
beanz
Summary

I've been currently working use-after-scope sanitization in GCC. We see optimization opportunity to rewrite local variables (when poisoned) to SSA_NAMEs
where a usage of such SSA_NAME would lead to use-after-scope error. Compared to the current implementation, there SSA_NAMEs (corresponding to a local variable)
does not have a stack store and thus there's no reserved slot in shadow memory. For these I would like to add a new API function builtin_asan_report_use_after_scope.

Let's consider following sample:
int
main (void)
{

char *ptr;
{
  char my_char;
  ptr = &my_char;
}

return *ptr;

}

which can be transformed to:

main ()
{

char my_char;
int _4;

<bb 2>:
__builtin___asan_report_use_after_scope ("my_char", 1);
_4 = (int) my_char_5(D);
return _4;

}

Corresponding run-time error looks as follows:

16049==ERROR: AddressSanitizer: stack-use-after-scope at pc 0x000000400794 bp 0x000000000001 sp 0x0000004005f3

ACCESS of size 1 for variable 'my_char' thread T0

#0 0x4005f2 in main (/tmp/a.out+0x4005f2)
#1 0x7f883337e290 in __libc_start_main (/lib64/libc.so.6+0x20290)
#2 0x400649 in _start (/tmp/a.out+0x400649)

SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x4005f2) in main

16049==ABORTING

Diff Detail

Event Timeline

marxin updated this revision to Diff 78849.Nov 22 2016, 4:22 AM
marxin retitled this revision from to [ASAN] Create a new UseAfterScopeError.
marxin updated this object.
marxin added reviewers: beanz, samsonov, kcc.
marxin set the repository for this revision to rL LLVM.

I'll let @kcc (or @vitalybuka ?) chime in on the API before an LGTM. I don't see a big problem on my part, if you don't have memory to back that variable. But I don't know much about gcc internals.

compiler-rt/lib/asan/asan_errors.cc
289

clang-format

295

Can the compiler know if it's a read or write in many cases? Or does it now have that information?

297

Does clang-format agree with this?

compiler-rt/lib/asan/asan_errors.h
313

Please run clang-format on this.

348

Please don't change enum values unless there's a huge reason for it. Place this one after Generic.

compiler-rt/lib/asan/asan_report.cc
358

No sense having this comment.

compiler-rt/lib/asan/asan_rtl.cc
258

Why?

vitalybuka added inline comments.Nov 22 2016, 11:02 AM
compiler-rt/lib/asan/asan_errors.cc
295

Would be nice to have report as close to regular one as possible.

Hm... I don't understand this change.
If you are adding a call to __asan_report_use_after_scope it means that you can statically (at compile-time) see the problem,
which in turn means that you can issue a warning/error at compile time and not bother with asan.

In D26965#608664, @kcc wrote:

Hm... I don't understand this change.
If you are adding a call to __asan_report_use_after_scope it means that you can statically (at compile-time) see the problem,
which in turn means that you can issue a warning/error at compile time and not bother with asan.

Yep, I know that at compile time that I emit a call to the new builtin. But similar to all other sanitizers, the code must be reachable at run-time.

marxin updated this revision to Diff 80745.Dec 8 2016, 4:21 AM
marxin edited edge metadata.
marxin removed rL LLVM as the repository for this revision.
marxin added a comment.Dec 8 2016, 4:25 AM

I'm attaching a new version, where I fixed all clang-format issue, trailing include and the error is used just for READ operation.
The error message is very similar to the normal (no stack variables are displayed and address is not printed).

After the patch:

gcc -fsanitize=address -fsanitize-address-use-after-scope /tmp/use-after-scope-20.c  -O2 && ./a.out
=================================================================
==31148==ERROR: AddressSanitizer: stack-use-after-scope at pc 0x000000400764 bp 0x000000000004 sp 0x0000004005d3
READ of size 4 for variable 'my_char' thread T0
    #0 0x4005d2 in main (/tmp/a.out+0x4005d2)
    #1 0x7f7a1dafd290 in __libc_start_main (/lib64/libc.so.6+0x20290)
    #2 0x400629 in _start (/tmp/a.out+0x400629)

SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x4005d2) in main
==31148==ABORTING

Before:

gcc -fsanitize=address -fsanitize-address-use-after-scope /tmp/use-after-scope-20.c  -O0 && ./a.out > /tmp/0
=================================================================
==19162==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7ffc7b54ec10 at pc 0x000000400828 bp 0x7ffc7b54ebc0 sp 0x7ffc7b54ebb8
READ of size 4 at 0x7ffc7b54ec10 thread T0
    #0 0x400827 in main (/tmp/a.out+0x400827)
    #1 0x7fe10ceb5290 in __libc_start_main (/lib64/libc.so.6+0x20290)
    #2 0x400699 in _start (/tmp/a.out+0x400699)

Address 0x7ffc7b54ec10 is located in stack of thread T0 at offset 32 in frame
    #0 0x400756 in main (/tmp/a.out+0x400756)

  This frame has 1 object(s):
    [32, 36) 'my_char' <== Memory access at offset 32 is inside this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
      (longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-use-after-scope (/tmp/a.out+0x400827) in main
Shadow bytes around the buggy address:
  0x10000f6a1d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1d70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1
=>0x10000f6a1d80: f1 f1[f8]f2 f2 f2 f3 f3 f3 f3 00 00 00 00 00 00
  0x10000f6a1d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x10000f6a1dd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==19162==ABORTING
filcab added a comment.Dec 9 2016, 8:24 AM

Minor nits. Otherwise I see no problem with this, if @kcc is ok with adding the error.

libsanitizer/asan/asan_errors.h
313 ↗(On Diff #80745)

Also define this after ErrorGeneric, making it the same order as the enum.
Same thing for the cc file.

vitalybuka edited edge metadata.Dec 9 2016, 10:14 AM

Yep, I know that at compile time that I emit a call to the new builtin. But similar to all other sanitizers, the code must be reachable at run-time.

My understanding is that it's always better to detect stuff in compile time than in runtime.
The goal of sanitizes is to detect bug which are impossible or very hard to detect at compile time.
Seems consistency is not good enough justification for hiding this bugs during compilation.

kcc added a comment.Dec 9 2016, 10:16 AM

Minor nits. Otherwise I see no problem with this, if @kcc is ok with adding the error.

kcc is not ok with this change, as he indicated previously. :(

In D26965#618369, @kcc wrote:

Minor nits. Otherwise I see no problem with this, if @kcc is ok with adding the error.

kcc is not ok with this change, as he indicated previously. :(

I added comment which explains that even though we call __asan_report_use_after_scope function, the call still may be conditional. And possibly can't be never reached. I guess that's nature of sanitizers, where an undefined behavior must be triggered in run-time.

kcc added a comment.Dec 12 2016, 11:00 AM

I am still not convinced this is useful, but if you want to convince me

  • please add a test to this CL (in projects/compiler-rt/test/asan)

and we will discuss the test.

marxin updated this revision to Diff 81195.Dec 13 2016, 1:45 AM
marxin edited edge metadata.

Updated:

  • UseAfterScope::Print is put after ErrorGeneric::Print
  • struct ErrorUseAfterScope : ErrorBase is after ErrorGeneric
  • New test added: compiler-rt/test/asan/TestCases/use-after-scope-conditional.cc
kcc added a comment.Dec 13 2016, 11:24 AM

The code in the example sucks so utterly that it should be fine to issue a compile-time warning on it.
And if you can't issue a warning, you also can't instrument the program in the suggested way.

marxin abandoned this revision.Mar 14 2018, 1:48 AM