Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions compiler-rt/lib/asan/asan_errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The 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 u8 *shadow_addr :-)


// 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 &&
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++;
Expand Down
50 changes: 25 additions & 25 deletions compiler-rt/lib/asan/asan_interceptors_memintrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -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))) { \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This __bad is now unused

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it's fair to remove __bad entirely then?

Copy link
Contributor

Choose a reason for hiding this comment

The 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) \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@
int main() {
char *p = new char;
char *dest = new char;
const size_t offset = 0x4567890123456789;
const size_t size = 0x4567890123456789;

// The output here needs to match the output from the sanitizer runtime,
// which includes 0x and prints hex in lower case.
//
// On Windows, %p omits %0x and prints hex characters in upper case,
// so we use PRIxPTR instead of %p.
fprintf(stderr, "Expected bad addr: %#" PRIxPTR "\n",
reinterpret_cast<uintptr_t>(p + offset));
reinterpret_cast<uintptr_t>(p));
// Flush it so the output came out before the asan report.
fflush(stderr);

memmove(dest, p, offset);
memmove(dest, p, size);
return 0;
}

// CHECK: Expected bad addr: [[ADDR:0x[0-9,a-f]+]]
// CHECK: AddressSanitizer: unknown-crash on address [[ADDR]]
// CHECK: Address [[ADDR]] is a wild pointer inside of access range of size 0x4567890123456789
// CHECK: AddressSanitizer: heap-buffer-overflow on address [[ADDR]]
// CHECK: READ of size 5001116549197948809 at [[ADDR]] thread T0
70 changes: 70 additions & 0 deletions compiler-rt/test/asan/TestCases/stack-buffer-overflow-partial.cpp
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
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strcasestr-1.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ int main(int argc, char **argv) {
char s1[4] = "abC";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strcasestr(s1, s2);
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == s1 + 2);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strcasestr-2.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@ int main(int argc, char **argv) {
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strcasestr(s1, s2);
assert(r == 0);
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strcspn-1.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int main(int argc, char **argv) {
char s1[4] = "caB";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strcspn(s1, s2);
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == 1);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strcspn-2.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int main(int argc, char **argv) {
char s2[4] = "abc";
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strcspn(s1, s2);
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == 0);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strpbrk-1.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int main(int argc, char **argv) {
char s1[4] = "cab";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strpbrk(s1, s2);
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == s1 + 1);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strpbrk-2.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int main(int argc, char **argv) {
char s2[4] = "bca";
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strpbrk(s1, s2);
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == s1);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strspn-1.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int main(int argc, char **argv) {
char s1[4] = "acb";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strspn(s1, s2);
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == 1);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strspn-2.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ int main(int argc, char **argv) {
char s2[5] = "abcd";
__asan_poison_memory_region ((char *)&s2[3], 2);
r = strspn(s1, s2);
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r >= 2);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strstr-1.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ int main(int argc, char **argv) {
char s1[4] = "acb";
__asan_poison_memory_region ((char *)&s1[2], 2);
r = strstr(s1, s2);
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} {{partially overflows this variable|is inside this variable}}
// CHECK:'s1'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == s1 + 1);
return 0;
}
2 changes: 1 addition & 1 deletion compiler-rt/test/asan/TestCases/strstr-2.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ int main(int argc, char **argv) {
char s2[4] = "cab";
__asan_poison_memory_region ((char *)&s2[2], 2);
r = strstr(s1, s2);
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK:'s2'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
assert(r == 0);
return 0;
}
8 changes: 4 additions & 4 deletions compiler-rt/test/asan/TestCases/strtok.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ void test1() {
char token_delimiter[2] = "b";
__asan_poison_memory_region ((char *)&token_delimiter[1], 2);
token = strtok(s, token_delimiter);
// CHECK1: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK1: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
}

// Check that we find overflows in the delimiters on the second call (str == NULL)
Expand All @@ -48,7 +48,7 @@ void test2() {
assert(strcmp(token, "a") == 0);
__asan_poison_memory_region ((char *)&token_delimiter[1], 2);
token = strtok(NULL, token_delimiter);
// CHECK2: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK2: 'token_delimiter'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
}

// Check that we find overflows in the string (only on the first call) with strict_string_checks.
Expand All @@ -58,7 +58,7 @@ void test3() {
char token_delimiter[2] = "b";
__asan_poison_memory_region ((char *)&s[3], 2);
token = strtok(s, token_delimiter);
// CHECK3: 's'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK3: 's'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
}

// Check that we do not crash when strtok returns NULL with strict_string_checks.
Expand All @@ -78,7 +78,7 @@ void test5() {
__asan_poison_memory_region ((char *)&s[2], 2);
__asan_poison_memory_region ((char *)&token_delimiter[1], 2);
token = strtok(s, token_delimiter);
// CHECK5: 's'{{.*}} <== Memory access at offset {{[0-9]+}} partially overflows this variable
// CHECK5: 's'{{.*}} <== Memory access at offset {{[0-9]+}} is inside this variable
}

// Check that we find overflows in the delimiters (only on the first call) with !strict_string_checks.
Expand Down
Loading