//===-- asan_mem_test.cc --------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // //===----------------------------------------------------------------------===// #include "asan_test_utils.h" template void MemSetOOBTestTemplate(size_t length) { if (length == 0) return; size_t size = Ident(sizeof(T) * length); T *array = Ident((T*)malloc(size)); int element = Ident(42); int zero = Ident(0); void *(*MEMSET)(void *s, int c, size_t n) = Ident(memset); // memset interval inside array MEMSET(array, element, size); MEMSET(array, element, size - 1); MEMSET(array + length - 1, element, sizeof(T)); MEMSET(array, element, 1); // memset 0 bytes MEMSET(array - 10, element, zero); MEMSET(array - 1, element, zero); MEMSET(array, element, zero); MEMSET(array + length, 0, zero); MEMSET(array + length + 1, 0, zero); // try to memset bytes to the right of array EXPECT_DEATH(MEMSET(array, 0, size + 1), RightOOBWriteMessage(0)); EXPECT_DEATH(MEMSET((char*)(array + length) - 1, element, 6), RightOOBWriteMessage(0)); EXPECT_DEATH(MEMSET(array + 1, element, size + sizeof(T)), RightOOBWriteMessage(0)); // whole interval is to the right EXPECT_DEATH(MEMSET(array + length + 1, 0, 10), RightOOBWriteMessage(sizeof(T))); // try to memset bytes to the left of array EXPECT_DEATH(MEMSET((char*)array - 1, element, size), LeftOOBWriteMessage(1)); EXPECT_DEATH(MEMSET((char*)array - 5, 0, 6), LeftOOBWriteMessage(5)); if (length >= 100) { // Large OOB, we find it only if the redzone is large enough. EXPECT_DEATH(memset(array - 5, element, size + 5 * sizeof(T)), LeftOOBWriteMessage(5 * sizeof(T))); } // whole interval is to the left EXPECT_DEATH(MEMSET(array - 2, 0, sizeof(T)), LeftOOBWriteMessage(2 * sizeof(T))); // try to memset bytes both to the left & to the right EXPECT_DEATH(MEMSET((char*)array - 2, element, size + 4), LeftOOBWriteMessage(2)); free(array); } TEST(AddressSanitizer, MemSetOOBTest) { MemSetOOBTestTemplate(100); MemSetOOBTestTemplate(5); MemSetOOBTestTemplate(256); // We can test arrays of structres/classes here, but what for? } // Try to allocate two arrays of 'size' bytes that are near each other. // Strictly speaking we are not guaranteed to find such two pointers, // but given the structure of asan's allocator we will. static bool AllocateTwoAdjacentArrays(char **x1, char **x2, size_t size) { vector v; bool res = false; for (size_t i = 0; i < 1000U && !res; i++) { v.push_back(reinterpret_cast(new char[size])); if (i == 0) continue; sort(v.begin(), v.end()); for (size_t j = 1; j < v.size(); j++) { assert(v[j] > v[j-1]); if ((size_t)(v[j] - v[j-1]) < size * 2) { *x2 = reinterpret_cast(v[j]); *x1 = reinterpret_cast(v[j-1]); res = true; break; } } } for (size_t i = 0; i < v.size(); i++) { char *p = reinterpret_cast(v[i]); if (res && p == *x1) continue; if (res && p == *x2) continue; delete [] p; } return res; } TEST(AddressSanitizer, LargeOOBInMemset) { for (size_t size = 200; size < 100000; size += size / 2) { char *x1, *x2; if (!Ident(AllocateTwoAdjacentArrays)(&x1, &x2, size)) continue; // fprintf(stderr, " large oob memset: %p %p %zd\n", x1, x2, size); // Do a memset on x1 with huge out-of-bound access that will end up in x2. EXPECT_DEATH(Ident(memset)(x1, 0, size * 2), "is located 0 bytes to the right"); delete [] x1; delete [] x2; return; } assert(0 && "Did not find two adjacent malloc-ed pointers"); } // Same test for memcpy and memmove functions template void MemTransferOOBTestTemplate(size_t length) { if (length == 0) return; size_t size = Ident(sizeof(T) * length); T *src = Ident((T*)malloc(size)); T *dest = Ident((T*)malloc(size)); int zero = Ident(0); // valid transfer of bytes between arrays M::transfer(dest, src, size); M::transfer(dest + 1, src, size - sizeof(T)); M::transfer(dest, src + length - 1, sizeof(T)); M::transfer(dest, src, 1); // transfer zero bytes M::transfer(dest - 1, src, 0); M::transfer(dest + length, src, zero); M::transfer(dest, src - 1, zero); M::transfer(dest, src, zero); // try to change mem to the right of dest EXPECT_DEATH(M::transfer(dest + 1, src, size), RightOOBWriteMessage(0)); EXPECT_DEATH(M::transfer((char*)(dest + length) - 1, src, 5), RightOOBWriteMessage(0)); // try to change mem to the left of dest EXPECT_DEATH(M::transfer(dest - 2, src, size), LeftOOBWriteMessage(2 * sizeof(T))); EXPECT_DEATH(M::transfer((char*)dest - 3, src, 4), LeftOOBWriteMessage(3)); // try to access mem to the right of src EXPECT_DEATH(M::transfer(dest, src + 2, size), RightOOBReadMessage(0)); EXPECT_DEATH(M::transfer(dest, (char*)(src + length) - 3, 6), RightOOBReadMessage(0)); // try to access mem to the left of src EXPECT_DEATH(M::transfer(dest, src - 1, size), LeftOOBReadMessage(sizeof(T))); EXPECT_DEATH(M::transfer(dest, (char*)src - 6, 7), LeftOOBReadMessage(6)); // Generally we don't need to test cases where both accessing src and writing // to dest address to poisoned memory. T *big_src = Ident((T*)malloc(size * 2)); T *big_dest = Ident((T*)malloc(size * 2)); // try to change mem to both sides of dest EXPECT_DEATH(M::transfer(dest - 1, big_src, size * 2), LeftOOBWriteMessage(sizeof(T))); // try to access mem to both sides of src EXPECT_DEATH(M::transfer(big_dest, src - 2, size * 2), LeftOOBReadMessage(2 * sizeof(T))); free(src); free(dest); free(big_src); free(big_dest); } class MemCpyWrapper { public: static void* transfer(void *to, const void *from, size_t size) { return Ident(memcpy)(to, from, size); } }; TEST(AddressSanitizer, MemCpyOOBTest) { MemTransferOOBTestTemplate(100); MemTransferOOBTestTemplate(1024); } class MemMoveWrapper { public: static void* transfer(void *to, const void *from, size_t size) { return Ident(memmove)(to, from, size); } }; TEST(AddressSanitizer, MemMoveOOBTest) { MemTransferOOBTestTemplate(100); MemTransferOOBTestTemplate(1024); } TEST(AddressSanitizer, MemCmpOOBTest) { size_t size = Ident(100); char *s1 = MallocAndMemsetString(size); char *s2 = MallocAndMemsetString(size); // Normal memcmp calls. Ident(memcmp(s1, s2, size)); Ident(memcmp(s1 + size - 1, s2 + size - 1, 1)); Ident(memcmp(s1 - 1, s2 - 1, 0)); // One of arguments points to not allocated memory. EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); // Hit unallocated memory and die. EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); // Zero bytes are not terminators and don't prevent from OOB. s1[size - 1] = '\0'; s2[size - 1] = '\0'; EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); // Even if the buffers differ in the first byte, we still assume that // memcmp may access the whole buffer and thus reporting the overflow here: s1[0] = 1; s2[0] = 123; EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); free(s1); free(s2); }