| 1 | //===-- asan_oob_test.cpp -------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file is a part of AddressSanitizer, an address sanity checker. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | #include "asan_test_utils.h" |
| 13 | |
| 14 | #ifdef __sparc__ |
| 15 | // Tests using unaligned accesses cannot work on strict-alignment targets. |
| 16 | #define SKIP_ON_STRICT_ALIGNMENT(x) DISABLED_##x |
| 17 | #else |
| 18 | #define SKIP_ON_STRICT_ALIGNMENT(x) x |
| 19 | #endif |
| 20 | |
| 21 | NOINLINE void asan_write_sized_aligned(uint8_t *p, size_t size) { |
| 22 | EXPECT_EQ(0U, ((uintptr_t)p % size)); |
| 23 | if (size == 1) asan_write((uint8_t*)p); |
| 24 | else if (size == 2) asan_write((uint16_t*)p); |
| 25 | else if (size == 4) asan_write((uint32_t*)p); |
| 26 | else if (size == 8) asan_write((uint64_t*)p); |
| 27 | } |
| 28 | |
| 29 | template<typename T> |
| 30 | NOINLINE void oob_test(int size, int off) { |
| 31 | char *p = (char*)malloc_aaa(size); |
| 32 | // fprintf(stderr, "writing %d byte(s) into [%p,%p) with offset %d\n", |
| 33 | // sizeof(T), p, p + size, off); |
| 34 | asan_write((T*)(p + off)); |
| 35 | free_aaa(p); |
| 36 | } |
| 37 | |
| 38 | static std::string GetLeftOOBMessage(int off) { |
| 39 | char str[100]; |
| 40 | sprintf(s: str, format: "is located.*%d byte.*before" , off); |
| 41 | return str; |
| 42 | } |
| 43 | |
| 44 | static std::string GetRightOOBMessage(int off) { |
| 45 | char str[100]; |
| 46 | #if !defined(_WIN32) |
| 47 | // FIXME: Fix PR42868 and remove SEGV match. |
| 48 | sprintf(s: str, format: "is located.*%d byte.*after|SEGV" , off); |
| 49 | #else |
| 50 | // `|` doesn't work in googletest's regexes on Windows, |
| 51 | // see googletest/docs/advanced.md#regular-expression-syntax |
| 52 | // But it's not needed on Windows anyways. |
| 53 | sprintf(str, "is located.*%d byte.*after" , off); |
| 54 | #endif |
| 55 | return str; |
| 56 | } |
| 57 | |
| 58 | template<typename T> |
| 59 | void OOBTest() { |
| 60 | for (int size = sizeof(T); size < 20; size += 5) { |
| 61 | for (int i = -5; i < 0; i++) |
| 62 | EXPECT_DEATH(oob_test<T>(size, i), GetLeftOOBMessage(-i)); |
| 63 | |
| 64 | for (int i = 0; i < (int)(size - sizeof(T) + 1); i++) |
| 65 | oob_test<T>(size, i); |
| 66 | |
| 67 | for (int i = size - sizeof(T) + 1; i <= (int)(size + 2 * sizeof(T)); i++) { |
| 68 | // we don't catch unaligned partially OOB accesses. |
| 69 | if (i % sizeof(T)) continue; |
| 70 | int off = i >= size ? (i - size) : 0; |
| 71 | EXPECT_DEATH(oob_test<T>(size, i), GetRightOOBMessage(off)); |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | EXPECT_DEATH(oob_test<T>(kLargeMalloc, -1), GetLeftOOBMessage(1)); |
| 76 | EXPECT_DEATH(oob_test<T>(kLargeMalloc, kLargeMalloc), GetRightOOBMessage(0)); |
| 77 | } |
| 78 | |
| 79 | // TODO(glider): the following tests are EXTREMELY slow on Darwin: |
| 80 | // AddressSanitizer.OOB_char (125503 ms) |
| 81 | // AddressSanitizer.OOB_int (126890 ms) |
| 82 | // AddressSanitizer.OOBRightTest (315605 ms) |
| 83 | // AddressSanitizer.SimpleStackTest (366559 ms) |
| 84 | |
| 85 | TEST(AddressSanitizer, OOB_char) { |
| 86 | OOBTest<U1>(); |
| 87 | } |
| 88 | |
| 89 | TEST(AddressSanitizer, SKIP_ON_STRICT_ALIGNMENT(OOB_int)) { |
| 90 | OOBTest<U4>(); |
| 91 | } |
| 92 | |
| 93 | TEST(AddressSanitizer, OOBRightTest) { |
| 94 | size_t max_access_size = SANITIZER_WORDSIZE == 64 ? 8 : 4; |
| 95 | for (size_t access_size = 1; access_size <= max_access_size; |
| 96 | access_size *= 2) { |
| 97 | for (size_t alloc_size = 1; alloc_size <= 8; alloc_size++) { |
| 98 | for (size_t offset = 0; offset <= 8; offset += access_size) { |
| 99 | void *p = malloc(size: alloc_size); |
| 100 | // allocated: [p, p + alloc_size) |
| 101 | // accessed: [p + offset, p + offset + access_size) |
| 102 | uint8_t *addr = (uint8_t*)p + offset; |
| 103 | if (offset + access_size <= alloc_size) { |
| 104 | asan_write_sized_aligned(p: addr, size: access_size); |
| 105 | } else { |
| 106 | int outside_bytes = offset > alloc_size ? (offset - alloc_size) : 0; |
| 107 | EXPECT_DEATH(asan_write_sized_aligned(addr, access_size), |
| 108 | GetRightOOBMessage(outside_bytes)); |
| 109 | } |
| 110 | free(ptr: p); |
| 111 | } |
| 112 | } |
| 113 | } |
| 114 | } |
| 115 | |
| 116 | TEST(AddressSanitizer, LargeOOBRightTest) { |
| 117 | size_t large_power_of_two = 1 << 19; |
| 118 | for (size_t i = 16; i <= 256; i *= 2) { |
| 119 | size_t size = large_power_of_two - i; |
| 120 | char *p = Ident(new char[size]); |
| 121 | EXPECT_DEATH(p[size] = 0, GetRightOOBMessage(0)); |
| 122 | delete [] p; |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | TEST(AddressSanitizer, DISABLED_DemoOOBLeftLow) { |
| 127 | oob_test<U1>(10, -1); |
| 128 | } |
| 129 | |
| 130 | TEST(AddressSanitizer, DISABLED_DemoOOBLeftHigh) { |
| 131 | oob_test<U1>(kLargeMalloc, -1); |
| 132 | } |
| 133 | |
| 134 | TEST(AddressSanitizer, DISABLED_DemoOOBRightLow) { |
| 135 | oob_test<U1>(10, 10); |
| 136 | } |
| 137 | |
| 138 | TEST(AddressSanitizer, DISABLED_DemoOOBRightHigh) { |
| 139 | oob_test<U1>(kLargeMalloc, kLargeMalloc); |
| 140 | } |
| 141 | |