[ASan] Enhance libsanitizer support for invalid-pointer-pair.

Following patch adds support of all memory origins in
CheckForInvalidPointerPair function. For small difference of pointers,
it's directly done in shadow memory (the limit was set to 2048B).
Then we search for origin of first pointer and verify that the second
one has the same origin. If so, we verify that it points either to a same
variable (in case of stack memory or a global variable), or to a same
heap segment.

Committing on behanf of marxin and jakubjelinek.

Reviewers: alekseyshl, kcc

Subscribers: llvm-commits

Differential revision: https://reviews.llvm.org/D40600

llvm-svn: 319668
This commit is contained in:
Alex Shlyapnikov 2017-12-04 18:00:24 +00:00
parent e00cd0c408
commit c73d1e28f1
10 changed files with 410 additions and 7 deletions

View File

@ -336,6 +336,26 @@ void GlobalAddressDescription::Print(const char *bug_type) const {
}
}
bool GlobalAddressDescription::PointsInsideTheSameVariable(
const GlobalAddressDescription &other) const {
if (size == 0 || other.size == 0) return false;
for (uptr i = 0; i < size; i++) {
const __asan_global &a = globals[i];
for (uptr j = 0; j < other.size; j++) {
const __asan_global &b = other.globals[j];
if (a.beg == b.beg &&
a.beg <= addr &&
b.beg <= other.addr &&
(addr + access_size) < (a.beg + a.size) &&
(other.addr + other.access_size) < (b.beg + b.size))
return true;
}
}
return false;
}
void StackAddressDescription::Print() const {
Decorator d;
char tname[128];

View File

@ -146,6 +146,10 @@ struct GlobalAddressDescription {
u8 size;
void Print(const char *bug_type = "") const;
// Returns true when this descriptions points inside the same global variable
// as other. Descriptions can have different address within the variable
bool PointsInsideTheSameVariable(const GlobalAddressDescription &other) const;
};
bool GetGlobalAddressInformation(uptr addr, uptr access_size,

View File

@ -298,17 +298,58 @@ static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp,
in_report.ReportError(error);
}
static bool IsInvalidPointerPair(uptr a1, uptr a2) {
if (a1 == a2)
return false;
// 256B in shadow memory can be iterated quite fast
static const uptr kMaxOffset = 2048;
uptr left = a1 < a2 ? a1 : a2;
uptr right = a1 < a2 ? a2 : a1;
uptr offset = right - left;
if (offset <= kMaxOffset)
return __asan_region_is_poisoned(left, offset);
AsanThread *t = GetCurrentThread();
// check whether left is a stack memory pointer
if (uptr shadow_offset1 = t->GetStackVariableShadowStart(left)) {
uptr shadow_offset2 = t->GetStackVariableShadowStart(right);
return shadow_offset2 == 0 || shadow_offset1 != shadow_offset2;
}
// check whether left is a heap memory address
HeapAddressDescription hdesc1, hdesc2;
if (GetHeapAddressInformation(left, 0, &hdesc1) &&
hdesc1.chunk_access.access_type == kAccessTypeInside)
return !GetHeapAddressInformation(right, 0, &hdesc2) ||
hdesc2.chunk_access.access_type != kAccessTypeInside ||
hdesc1.chunk_access.chunk_begin != hdesc2.chunk_access.chunk_begin;
// check whether left is an address of a global variable
GlobalAddressDescription gdesc1, gdesc2;
if (GetGlobalAddressInformation(left, 0, &gdesc1))
return !GetGlobalAddressInformation(right - 1, 0, &gdesc2) ||
!gdesc1.PointsInsideTheSameVariable(gdesc2);
if (t->GetStackVariableShadowStart(right) ||
GetHeapAddressInformation(right, 0, &hdesc2) ||
GetGlobalAddressInformation(right - 1, 0, &gdesc2))
return true;
// At this point we know nothing about both a1 and a2 addresses.
return false;
}
static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
if (!flags()->detect_invalid_pointer_pairs) return;
uptr a1 = reinterpret_cast<uptr>(p1);
uptr a2 = reinterpret_cast<uptr>(p2);
AsanChunkView chunk1 = FindHeapChunkByAddress(a1);
AsanChunkView chunk2 = FindHeapChunkByAddress(a2);
bool valid1 = chunk1.IsAllocated();
bool valid2 = chunk2.IsAllocated();
if (!valid1 || !valid2 || !chunk1.Eq(chunk2)) {
if (IsInvalidPointerPair(a1, a2)) {
GET_CALLER_PC_BP_SP;
return ReportInvalidPointerPair(pc, bp, sp, a1, a2);
ReportInvalidPointerPair(pc, bp, sp, a1, a2);
}
}
// ----------------------- Mac-specific reports ----------------- {{{1

View File

@ -317,7 +317,7 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
access->frame_descr = (const char *)((uptr*)bottom)[1];
return true;
}
uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr.
uptr mem_ptr = RoundDownTo(aligned_addr, SHADOW_GRANULARITY);
u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
u8 *shadow_bottom = (u8*)MemToShadow(bottom);
@ -346,6 +346,29 @@ bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
return true;
}
uptr AsanThread::GetStackVariableShadowStart(uptr addr) {
uptr bottom = 0;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
} else if (has_fake_stack()) {
bottom = fake_stack()->AddrIsInFakeStack(addr);
CHECK(bottom);
} else
return 0;
uptr aligned_addr = RoundDownTo(addr, SANITIZER_WORDSIZE / 8); // align addr.
u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
u8 *shadow_bottom = (u8*)MemToShadow(bottom);
while (shadow_ptr >= shadow_bottom &&
(*shadow_ptr != kAsanStackLeftRedzoneMagic &&
*shadow_ptr != kAsanStackMidRedzoneMagic &&
*shadow_ptr != kAsanStackRightRedzoneMagic))
shadow_ptr--;
return (uptr)shadow_ptr + 1;
}
bool AsanThread::AddrIsInStack(uptr addr) {
const auto bounds = GetStackBounds();
return addr >= bounds.bottom && addr < bounds.top;

View File

@ -90,6 +90,9 @@ class AsanThread {
};
bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access);
// Returns a pointer to the start of the stack variable's shadow memory.
uptr GetStackVariableShadowStart(uptr addr);
bool AddrIsInStack(uptr addr);
void DeleteFakeStack(int tid) {

View File

@ -0,0 +1,54 @@
// RUN: %clangxx_asan -O0 %s -o %t -mllvm -asan-detect-invalid-pointer-pair
// RUN: %env_asan_opts=detect_invalid_pointer_pairs=1 %run %t a 2>&1 | FileCheck %s -check-prefix=OK -allow-empty
// RUN: %env_asan_opts=detect_invalid_pointer_pairs=1 not %run %t b 2>&1 | FileCheck %s -check-prefix=B
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
char *pointers[2];
pthread_barrier_t bar;
void *thread_main(void *n) {
char local;
unsigned long id = (unsigned long)n;
pointers[id] = &local;
pthread_barrier_wait(&bar);
pthread_barrier_wait(&bar);
return NULL;
}
int main(int argc, char **argv) {
assert(argc >= 2);
char t = argv[1][0];
pthread_t threads[2];
pthread_barrier_init(&bar, NULL, 3);
pthread_create(&threads[0], 0, thread_main, (void *)0);
pthread_create(&threads[1], 0, thread_main, (void *)1);
pthread_barrier_wait(&bar);
if (t == 'a') {
// OK-NOT: not handled yet
unsigned r = pointers[0] - pointers[1];
} else {
char local;
char *parent_pointer = &local;
// B: ERROR: AddressSanitizer: invalid-pointer-pair
// B: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-threads.cc:[[@LINE+1]]
unsigned r = parent_pointer - pointers[0];
}
pthread_barrier_wait(&bar);
pthread_join(threads[0], 0);
pthread_join(threads[1], 0);
pthread_barrier_destroy(&bar);
return 0;
}

View File

@ -0,0 +1,103 @@
// RUN: %clangxx_asan -O0 %s -o %t -mllvm -asan-detect-invalid-pointer-pair
// RUN: %env_asan_opts=detect_invalid_pointer_pairs=1:halt_on_error=0 %run %t 2>&1 | FileCheck %s
#include <assert.h>
#include <stdlib.h>
int foo(char *p, char *q) {
return p > q;
}
char global1[100] = {}, global2[100] = {};
char small_global[7] = {};
char large_global[5000] = {};
int main() {
// Heap allocated memory.
char *heap1 = (char *)malloc(42);
char *heap2 = (char *)malloc(42);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(heap1, heap2);
free(heap1);
free(heap2);
heap1 = (char *)malloc(1024);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(heap1, heap1 + 1025);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(heap1 + 1024, heap1 + 1025);
free(heap1);
heap1 = (char *)malloc(4096);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(heap1, heap1 + 4097);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(heap1, 0);
// Global variables.
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(&global1[0], &global2[10]);
char *p = &small_global[0];
foo(p, p); // OK
foo(p, p + 7); // OK
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p, p + 8);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p - 1, p);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p, p - 1);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p - 1, p + 8);
p = &large_global[0];
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p - 1, p);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p, p - 1);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p, &global1[0]);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p, &small_global[0]);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(p, 0);
// Stack variables.
char stack1, stack2;
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(&stack1, &stack2);
// Mixtures.
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(heap1, &stack1);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
foo(heap1, &global1[0]);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
foo(&stack1, &global1[0]);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-errors.cc:[[@LINE+1]]
foo(&stack1, 0);
free(heap1);
return 0;
}

View File

@ -0,0 +1,74 @@
// RUN: %clangxx_asan -O0 %s -o %t -mllvm -asan-detect-invalid-pointer-pair
// RUN: %env_asan_opts=detect_invalid_pointer_pairs=1 %run %t
#include <assert.h>
#include <stdlib.h>
int foo(char *p) {
char *p2 = p + 20;
return p > p2;
}
int bar(char *p, char *q) {
return p <= q;
}
int baz(char *p, char *q) {
return p != 0 && p < q;
}
char global[8192] = {};
char small_global[7] = {};
int main() {
// Heap allocated memory.
char *p = (char *)malloc(42);
int r = foo(p);
free(p);
p = (char *)malloc(1024);
bar(p, p + 1024);
bar(p + 1024, p + 1023);
bar(p + 1, p + 1023);
free(p);
p = (char *)malloc(4096);
bar(p, p + 4096);
bar(p + 10, p + 100);
bar(p + 1024, p + 4096);
bar(p + 4095, p + 4096);
bar(p + 4095, p + 4094);
bar(p + 100, p + 4096);
bar(p + 100, p + 4094);
free(p);
// Global variable.
bar(&global[0], &global[1]);
bar(&global[1], &global[2]);
bar(&global[2], &global[1]);
bar(&global[0], &global[100]);
bar(&global[1000], &global[7000]);
bar(&global[500], &global[10]);
p = &global[0];
bar(p, p + 8192);
p = &global[8000];
bar(p, p + 192);
p = &small_global[0];
bar(p, p + 1);
bar(p, p + 7);
bar(p + 7, p + 1);
bar(p + 6, p + 7);
bar(p + 7, p + 7);
// Stack variable.
char stack[10000];
bar(&stack[0], &stack[100]);
bar(&stack[1000], &stack[9000]);
bar(&stack[500], &stack[10]);
baz(0, &stack[10]);
return 0;
}

View File

@ -0,0 +1,48 @@
// RUN: %clangxx_asan -O0 %s -o %t -mllvm -asan-detect-invalid-pointer-pair
// RUN: %env_asan_opts=detect_invalid_pointer_pairs=1:halt_on_error=0 %run %t 2>&1 | FileCheck %s
#include <assert.h>
#include <stdlib.h>
int foo(char *p, char *q) {
return p - q;
}
char global1[100] = {}, global2[100] = {};
int main() {
// Heap allocated memory.
char *heap1 = (char *)malloc(42);
char *heap2 = (char *)malloc(42);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-errors.cc:[[@LINE+1]]
foo(heap1, heap2);
// Global variables.
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-errors.cc:[[@LINE+1]]
foo(&global1[0], &global2[10]);
// Stack variables.
char stack1, stack2;
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-errors.cc:[[@LINE+1]]
foo(&stack1, &stack2);
// Mixtures.
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-errors.cc:[[@LINE+1]]
foo(heap1, &stack1);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-errors.cc:[[@LINE+1]]
foo(heap1, &global1[0]);
// CHECK: ERROR: AddressSanitizer: invalid-pointer-pair
// CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-errors.cc:[[@LINE+1]]
foo(&stack1, &global1[0]);
free(heap1);
free(heap2);
return 0;
}

View File

@ -0,0 +1,33 @@
// RUN: %clangxx_asan -O0 %s -o %t -mllvm -asan-detect-invalid-pointer-pair
// RUN: %env_asan_opts=detect_invalid_pointer_pairs=1 %run %t
#include <assert.h>
#include <stdlib.h>
int bar(char *p, char *q) {
return p <= q;
}
char global[10000] = {};
int main() {
// Heap allocated memory.
char *p = (char *)malloc(42);
int r = bar(p, p + 20);
free(p);
// Global variable.
bar(&global[0], &global[100]);
bar(&global[1000], &global[9000]);
bar(&global[500], &global[10]);
bar(&global[0], &global[10000]);
// Stack variable.
char stack[10000];
bar(&stack[0], &stack[100]);
bar(&stack[1000], &stack[9000]);
bar(&stack[500], &stack[10]);
return 0;
}