-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[asan] Fix unknown-crash being reported for multi-byte errors, and incorrect memory access addresses being reported
#144480
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -437,8 +437,16 @@ ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr, | |
| bug_descr = "unknown-crash"; | ||
| if (AddrIsInMem(addr)) { | ||
| u8 *shadow_addr = (u8 *)MemToShadow(addr); | ||
| // If we are accessing 16 bytes, look at the second shadow byte. | ||
| if (*shadow_addr == 0 && access_size > ASAN_SHADOW_GRANULARITY) | ||
| u8 *shadow_addr_upper_bound = (u8 *)MEM_TO_SHADOW(addr + access_size); | ||
| // We use the MEM_TO_SHADOW macro for the upper bound above instead of | ||
| // MemToShadow to skip the assertion that (addr + access_size) is within | ||
| // the valid memory range. The validity of the shadow address is checked | ||
| // via AddrIsInShadow in the while loop below. | ||
|
Comment on lines
+441
to
+444
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. dismissible nit - since this mentions the variables above, I would recommend having this before those variables are declared, to make the code easier to read. Otherwise, reading the comments requires backtracking to earlier lines to get the full context. In other words, I recommend having this before the declaration of |
||
|
|
||
| // If the access could span multiple shadow bytes, | ||
| // do a sequential scan and look for the first bad shadow byte. | ||
| while (*shadow_addr == 0 && shadow_addr < shadow_addr_upper_bound && | ||
fmayer marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| AddrIsInShadow((uptr)(shadow_addr + 1))) | ||
| shadow_addr++; | ||
| // If we are in the partial right redzone, look at the next shadow byte. | ||
| if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,31 +52,31 @@ struct AsanInterceptorContext { | |
| // that no extra frames are created, and stack trace contains | ||
| // relevant information only. | ||
| // We check all shadow bytes. | ||
| #define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) \ | ||
| do { \ | ||
| uptr __offset = (uptr)(offset); \ | ||
| uptr __size = (uptr)(size); \ | ||
| uptr __bad = 0; \ | ||
| if (UNLIKELY(__offset > __offset + __size)) { \ | ||
| GET_STACK_TRACE_FATAL_HERE; \ | ||
| ReportStringFunctionSizeOverflow(__offset, __size, &stack); \ | ||
| } \ | ||
| if (UNLIKELY(!QuickCheckForUnpoisonedRegion(__offset, __size)) && \ | ||
| (__bad = __asan_region_is_poisoned(__offset, __size))) { \ | ||
| AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \ | ||
| bool suppressed = false; \ | ||
| if (_ctx) { \ | ||
| suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \ | ||
| if (!suppressed && HaveStackTraceBasedSuppressions()) { \ | ||
| GET_STACK_TRACE_FATAL_HERE; \ | ||
| suppressed = IsStackTraceSuppressed(&stack); \ | ||
| } \ | ||
| } \ | ||
| if (!suppressed) { \ | ||
| GET_CURRENT_PC_BP_SP; \ | ||
| ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false); \ | ||
| } \ | ||
| } \ | ||
| #define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) \ | ||
| do { \ | ||
| uptr __offset = (uptr)(offset); \ | ||
| uptr __size = (uptr)(size); \ | ||
| uptr __bad = 0; \ | ||
| if (UNLIKELY(__offset > __offset + __size)) { \ | ||
| GET_STACK_TRACE_FATAL_HERE; \ | ||
| ReportStringFunctionSizeOverflow(__offset, __size, &stack); \ | ||
| } \ | ||
| if (UNLIKELY(!QuickCheckForUnpoisonedRegion(__offset, __size)) && \ | ||
| (__bad = __asan_region_is_poisoned(__offset, __size))) { \ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose it's fair to remove
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing seems reasonable to me. No reason to have unused variables afaict :-) |
||
| AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \ | ||
| bool suppressed = false; \ | ||
| if (_ctx) { \ | ||
| suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \ | ||
| if (!suppressed && HaveStackTraceBasedSuppressions()) { \ | ||
| GET_STACK_TRACE_FATAL_HERE; \ | ||
| suppressed = IsStackTraceSuppressed(&stack); \ | ||
| } \ | ||
| } \ | ||
| if (!suppressed) { \ | ||
| GET_CURRENT_PC_BP_SP; \ | ||
| ReportGenericError(pc, bp, sp, __offset, isWrite, __size, 0, false); \ | ||
| } \ | ||
| } \ | ||
| } while (0) | ||
|
|
||
| #define ASAN_READ_RANGE(ctx, offset, size) \ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| // RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=8 -DREAD_SIZE=8 | ||
| // RUN: not %run %t 1 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 2 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 7 2>&1 | FileCheck %s | ||
|
|
||
| // RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=16 -DREAD_SIZE=16 | ||
| // RUN: not %run %t 1 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 2 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 7 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 8 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 15 2>&1 | FileCheck %s | ||
|
|
||
| // RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=16 -DREAD_SIZE=15 | ||
| // RUN: not %run %t 2 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 3 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 7 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 8 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 15 2>&1 | FileCheck %s | ||
|
|
||
| // RUN: %clangxx_asan %s -o %t -DSTACK_ALLOC_SIZE=20 -DREAD_SIZE=18 | ||
| // RUN: not %run %t 3 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 7 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 10 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 13 2>&1 | FileCheck %s | ||
| // RUN: not %run %t 19 2>&1 | FileCheck %s | ||
|
|
||
| #include <stdlib.h> | ||
| #include <assert.h> | ||
| #include <stdio.h> | ||
|
|
||
| struct X { | ||
| char bytes[READ_SIZE]; | ||
| }; | ||
|
|
||
| __attribute__((noinline)) struct X out_of_bounds(int offset) { | ||
| volatile char bytes[STACK_ALLOC_SIZE]; | ||
| struct X* x_ptr = (struct X*)(bytes + offset); | ||
| return *x_ptr; | ||
| } | ||
|
|
||
| int main(int argc, char **argv) { | ||
| int offset = atoi(argv[1]); | ||
|
|
||
| // Output READ_SIZE to check against the asan report, | ||
| // and flush it so the output comes out first. | ||
| fprintf(stderr, "Expected READ size: %d\n", READ_SIZE); | ||
| fflush(stderr); | ||
|
|
||
| // We are explicitly testing that we correctly detect and report this error | ||
| // as a *partial stack buffer overflow*. | ||
| assert(offset < STACK_ALLOC_SIZE); | ||
| assert(offset + READ_SIZE > STACK_ALLOC_SIZE); | ||
|
|
||
| struct X x = out_of_bounds(offset); | ||
| int y = 0; | ||
|
|
||
| for (int i = 0; i < READ_SIZE; i++) { | ||
| y ^= x.bytes[i]; | ||
| } | ||
|
|
||
| return y; | ||
| } | ||
|
|
||
| // CHECK: Expected READ size: [[READ_SIZE:[0-9]+]] | ||
| // CHECK: ERROR: AddressSanitizer: stack-buffer-overflow on address | ||
| // CHECK: READ of size [[READ_SIZE]] at {{0x.*}} thread T0 | ||
| // CHECK: {{.* 0x.* in out_of_bounds.*stack-buffer-overflow-partial.cpp:}} | ||
| // CHECK: {{Address 0x.* is located in stack of thread T0 at offset}} | ||
| // CHECK: {{ #0 0x.* in out_of_bounds.*stack-buffer-overflow-partial.cpp:}} | ||
| // CHECK: 'bytes'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable |
Uh oh!
There was an error while loading. Please reload this page.