Index: lib/asan/asan_descriptions.h =================================================================== --- lib/asan/asan_descriptions.h +++ lib/asan/asan_descriptions.h @@ -134,6 +134,7 @@ bool GetStackAddressInformation(uptr addr, uptr access_size, StackAddressDescription *descr); +uptr GetStackVariableBeginning(uptr addr); struct GlobalAddressDescription { uptr addr; @@ -145,6 +146,10 @@ u8 size; void Print(const char *bug_type = "") const; + + // Returns true when this descriptions points inside a 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, Index: lib/asan/asan_descriptions.cc =================================================================== --- lib/asan/asan_descriptions.cc +++ lib/asan/asan_descriptions.cc @@ -222,6 +222,13 @@ return true; } +uptr GetStackVariableBeginning(uptr addr) { + if (AsanThread *t = FindThreadByStackAddress(addr)) + return t->GetStackFrameVariableBeginning(addr); + + return 0; +} + static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr, uptr access_size, uptr prev_var_end, uptr next_var_beg) { @@ -335,6 +342,23 @@ } } +bool GlobalAddressDescription::PointsInsideTheSameVariable( + const GlobalAddressDescription &other) const { + if (size == 0 || other.size == 0) return false; + + for (uptr i = 0; i < size; i++) + for (uptr j = 0; j < other.size; j++) { + if (globals[i].beg == other.globals[j].beg && globals[i].beg <= addr && + other.globals[j].beg <= other.addr && + (addr + access_size) < (globals[i].beg + globals[i].size) && + ((other.addr + other.access_size) < + (other.globals[j].beg + other.globals[j].size))) + return true; + } + + return false; +} + void StackAddressDescription::Print() const { Decorator d; char tname[128]; Index: lib/asan/asan_report.cc =================================================================== --- lib/asan/asan_report.cc +++ lib/asan/asan_report.cc @@ -297,17 +297,86 @@ in_report.ReportError(error); } +static bool IsInvalidPointerPair(uptr a1, uptr a2) { + if (a1 == a2) + return false; + + uptr offset = a1 < a2 ? a2 - a1 : a1 - a2; + uptr left = a1 < a2 ? a1 : a2; + uptr right = a1 < a2 ? a2 : a1; + if (offset <= 2048) { + if (__asan_region_is_poisoned(left, offset) == 0) + return false; + else + return true; + } + + uptr shadow_offset1, shadow_offset2; + { + { + ThreadRegistryLock l(&asanThreadRegistry()); + shadow_offset1 = GetStackVariableBeginning(left); + } + + // check whether left is a stack memory pointer + if (shadow_offset1) { + { + ThreadRegistryLock l(&asanThreadRegistry()); + shadow_offset2 = GetStackVariableBeginning(right - 1); + if (shadow_offset1 == shadow_offset2) + return false; + } + + return true; + } + } + + // check whether left is a heap memory address + HeapAddressDescription hdesc1, hdesc2; + if (GetHeapAddressInformation(left, 0, &hdesc1) && + hdesc1.chunk_access.access_type == kAccessTypeInside) { + if (GetHeapAddressInformation(right, 0, &hdesc2) && + hdesc2.chunk_access.access_type == kAccessTypeInside && + (hdesc1.chunk_access.chunk_begin + == hdesc2.chunk_access.chunk_begin)) + return false; + else + return true; + } + // check whether left is an address of a global variable + GlobalAddressDescription gdesc1, gdesc2; + if (GetGlobalAddressInformation(left, 0, &gdesc1)) { + if (GetGlobalAddressInformation(right - 1, 0, &gdesc2) && + gdesc1.PointsInsideTheSameVariable(gdesc2)) + return false; + else + return true; + } + + { + ThreadRegistryLock l(&asanThreadRegistry()); + shadow_offset2 = GetStackVariableBeginning(right - 1); + } + + if (shadow_offset2) + return true; + + if (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(p1); uptr a2 = reinterpret_cast(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 Index: lib/asan/asan_thread.h =================================================================== --- lib/asan/asan_thread.h +++ lib/asan/asan_thread.h @@ -90,6 +90,9 @@ }; bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access); + // Return beginning of a stack variable in shadow memory + uptr GetStackFrameVariableBeginning(uptr addr); + bool AddrIsInStack(uptr addr); void DeleteFakeStack(int tid) { Index: lib/asan/asan_thread.cc =================================================================== --- lib/asan/asan_thread.cc +++ lib/asan/asan_thread.cc @@ -317,7 +317,7 @@ 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,27 @@ return true; } +uptr AsanThread::GetStackFrameVariableBeginning(uptr addr) { + uptr bottom = 0; + if (AddrIsInStack(addr)) { + bottom = stack_bottom(); + } else if (has_fake_stack()) { + bottom = fake_stack()->AddrIsInFakeStack(addr); + CHECK(bottom); + } + 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; +} + bool AsanThread::AddrIsInStack(uptr addr) { const auto bounds = GetStackBounds(); return addr >= bounds.bottom && addr < bounds.top; Index: test/asan/TestCases/invalid-pointer-pairs-compare-errors.cc =================================================================== --- /dev/null +++ test/asan/TestCases/invalid-pointer-pairs-compare-errors.cc @@ -0,0 +1,102 @@ +// 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 +#include + +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-1.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-1.cc:[[@LINE+1]] + foo(heap1, heap1 + 1025); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.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-1.cc:[[@LINE+1]] + foo(heap1, heap1 + 4097); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.cc:[[@LINE+1]] + foo(heap1, 0); + + /* Global variables. */ + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.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-1.cc:[[@LINE+1]] + foo(p, p + 8); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.cc:[[@LINE+1]] + foo(p - 1, p); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.cc:[[@LINE+1]] + foo(p, p - 1); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.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-1.cc:[[@LINE+1]] + foo(p - 1, p); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.cc:[[@LINE+1]] + foo(p, p - 1); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.cc:[[@LINE+1]] + foo(p, &global1[0]); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.cc:[[@LINE+1]] + foo(p, &small_global[0]); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.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-1.cc:[[@LINE+1]] + foo(&stack1, &stack2); + + /* Mixtures. */ + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-compare-1.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-1.cc:[[@LINE+1]] + foo(&stack1, 0); + + return 0; +} Index: test/asan/TestCases/invalid-pointer-pairs-compare-success.cc =================================================================== --- /dev/null +++ test/asan/TestCases/invalid-pointer-pairs-compare-success.cc @@ -0,0 +1,75 @@ +// 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 +#include + +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; +} Index: test/asan/TestCases/invalid-pointer-pairs-subtract-errors.cc =================================================================== --- /dev/null +++ test/asan/TestCases/invalid-pointer-pairs-subtract-errors.cc @@ -0,0 +1,46 @@ +// 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 +#include + +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-1.cc:[[@LINE+1]] + foo(heap1, heap2); + + /* Global variables. */ + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-1.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-1.cc:[[@LINE+1]] + foo(&stack1, &stack2); + + /* Mixtures. */ + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-1.cc:[[@LINE+1]] + foo(heap1, &stack1); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-1.cc:[[@LINE+1]] + foo(heap1, &global1[0]); + // CHECK: ERROR: AddressSanitizer: invalid-pointer-pair + // CHECK: #{{[0-9]+ .*}} in main {{.*}}invalid-pointer-pairs-subtract-1.cc:[[@LINE+1]] + foo(&stack1, &global1[0]); + return 0; +} Index: test/asan/TestCases/invalid-pointer-pairs-subtract-success.cc =================================================================== --- /dev/null +++ test/asan/TestCases/invalid-pointer-pairs-subtract-success.cc @@ -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 +#include + +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]); + + /* Stack variable. */ + char stack[10000]; + bar(&stack[0], &stack[100]); + bar(&stack[1000], &stack[9000]); + bar(&stack[500], &stack[10]); + + return 0; +}