See the bug report at https://github.com/google/sanitizers/issues/691. When a dynamic alloca has a constant size, ASan instrumentation will treat it as a regular dynamic alloca (insert calls to poison and unpoison), but the backend will turn it into a regular stack variable. The poisoning/unpoisoning is then broken. This patch will treat such allocas as static.
Details
- Reviewers
kcc ygribov m.ostapenko eugenis - Commits
- rG7d1ebed0c5c3: [asan] fix false dynamic-stack-buffer-overflow report with constantly-sized…
rG7d03ce480a4f: [asan] fix false dynamic-stack-buffer-overflow report with constantly-sized…
rCRT273889: [asan] fix false dynamic-stack-buffer-overflow report with constantly-sized…
rL273889: [asan] fix false dynamic-stack-buffer-overflow report with constantly-sized…
rL273888: [asan] fix false dynamic-stack-buffer-overflow report with constantly-sized…
Diff Detail
Event Timeline
Hm, with this patch I got such a strange error:
$ cat test.c
#include <alloca.h>
#include <stdio.h>
#include <string.h>
void f1() {
char *dynamic_buffer = (char *)alloca(200);
fprintf(stderr, "dynamic_buffer = %p, dynamic_buffer_end = %p\n", dynamic_buffer, dynamic_buffer + 200);
dynamic_buffer[2] = 1;
return;
}
void f2() {
char buf[1024];
fprintf(stderr, "buf = %p, buf_end = %p\n", buf, buf + 1024);
memset(buf, 'x', 1024);
}
int main(int argc, const char *argv[]) {
f1();
f2();
fprintf(stderr, "Done.\n");
return 0;
}$ clang -fsanitize=address test.c && ./a.out
dynamic_buffer = 0x7ffed7c98a80, dynamic_buffer_end = 0x7ffed7c98b48
=================================================================
==15867==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffed7c98a82 at pc 0x0000004dbaaa bp 0x7ffed7c98a50 sp 0x7ffed7c98a48
WRITE of size 1 at 0x7ffed7c98a82 thread T0
#0 0x4dbaa9 in f1 (/home/max/build/llvm/a.out+0x4dbaa9)
#1 0x4dbcda in main (/home/max/build/llvm/a.out+0x4dbcda)
#2 0x7f1a18464ec4 in __libc_start_main /build/eglibc-3GlaMS/eglibc-2.19/csu/libc-start.c:287
#3 0x418b95 in _start (/home/max/build/llvm/a.out+0x418b95)
Address 0x7ffed7c98a82 is located in stack of thread T0 at offset 34 in frame
#0 0x4db95f in f1 (/home/max/build/llvm/a.out+0x4db95f)
This frame has 1 object(s):
[32, 33) '' <== Memory access at offset 34 overflows 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-buffer-overflow (/home/max/build/llvm/a.out+0x4dbaa9) in f1
Shadow bytes around the buggy address:
0x10005af8b100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b120: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b130: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b140: 00 00 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1
=>0x10005af8b150:[01]f3 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b190: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10005af8b1a0: 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
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
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: cbIt seems that dynamic_buffer wasn't even allocated here.
Update patch to correctly calculate the size of the dynamic alloca. Added -fstack-protector to the test cases.
The change looks sane to me (not sure I can push LGTM button though), just a little nit below.
| projects/compiler-rt/test/asan/TestCases/alloca_constant_size.cc | ||
|---|---|---|
| 4 | Do we need to check if the target supports "-fprotector" ? | |
| projects/compiler-rt/test/asan/TestCases/alloca_constant_size.cc | ||
|---|---|---|
| 4 | AFAIK it should be available in all ASan-supported targets. | |
| lib/Transforms/Instrumentation/AddressSanitizer.cpp | ||
|---|---|---|
| 901 | The change above makes getAllocaSizeInBytes valid only for static allocas (otherwise it asserts). This avoids calling getAllocaSizeInBytes for dynamic allocas. | |
| lib/Transforms/Instrumentation/AddressSanitizer.cpp | ||
|---|---|---|
| 466–467 | I was trying to figure out why we have the check for AI.isArrayAllocation() here and I cannot.. Looks like it was added here: | |
| 901 | Ok, Maybe we could get rid of the helper and place this check next to the getAllocaSizeInBytes to make it clear why we call this. | |
I think you don't need two test files, could you combine them to one source file (you'll probably want use ifdefs)?
| lib/Transforms/Instrumentation/AddressSanitizer.cpp | ||
|---|---|---|
| 466–467 |
Yeah, this is a artifact from initial implementation. I don't remember exact reason why I placed this check here, but this probably was a mistake. | |
You probably can get rid of the helper function now.