1 //===-- sanitizer_quarantine_test.cc --------------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This file is a part of ThreadSanitizer/AddressSanitizer runtime. 11 // 12 //===----------------------------------------------------------------------===// 13 #include "sanitizer_common/sanitizer_common.h" 14 #include "sanitizer_common/sanitizer_quarantine.h" 15 #include "gtest/gtest.h" 16 17 #include <stdlib.h> 18 19 namespace __sanitizer { 20 21 struct QuarantineCallback { 22 void Recycle(void *m) {} 23 void *Allocate(uptr size) { 24 return malloc(size); 25 } 26 void Deallocate(void *p) { 27 free(p); 28 } 29 }; 30 31 typedef QuarantineCache<QuarantineCallback> Cache; 32 33 static void* kFakePtr = reinterpret_cast<void*>(0xFA83FA83); 34 static const size_t kBlockSize = 8; 35 36 static QuarantineCallback cb; 37 38 static void DeallocateCache(Cache *cache) { 39 while (QuarantineBatch *batch = cache->DequeueBatch()) 40 cb.Deallocate(batch); 41 } 42 43 TEST(SanitizerCommon, QuarantineBatchMerge) { 44 // Verify the trivial case. 45 QuarantineBatch into; 46 into.init(kFakePtr, 4UL); 47 QuarantineBatch from; 48 from.init(kFakePtr, 8UL); 49 50 into.merge(&from); 51 52 ASSERT_EQ(into.count, 2UL); 53 ASSERT_EQ(into.batch[0], kFakePtr); 54 ASSERT_EQ(into.batch[1], kFakePtr); 55 ASSERT_EQ(into.size, 12UL + sizeof(QuarantineBatch)); 56 ASSERT_EQ(into.quarantined_size(), 12UL); 57 58 ASSERT_EQ(from.count, 0UL); 59 ASSERT_EQ(from.size, sizeof(QuarantineBatch)); 60 ASSERT_EQ(from.quarantined_size(), 0UL); 61 62 // Merge the batch to the limit. 63 for (uptr i = 2; i < QuarantineBatch::kSize; ++i) 64 from.push_back(kFakePtr, 8UL); 65 ASSERT_TRUE(into.count + from.count == QuarantineBatch::kSize); 66 ASSERT_TRUE(into.can_merge(&from)); 67 68 into.merge(&from); 69 ASSERT_TRUE(into.count == QuarantineBatch::kSize); 70 71 // No more space, not even for one element. 72 from.init(kFakePtr, 8UL); 73 74 ASSERT_FALSE(into.can_merge(&from)); 75 } 76 77 TEST(SanitizerCommon, QuarantineCacheMergeBatchesEmpty) { 78 Cache cache; 79 Cache to_deallocate; 80 cache.MergeBatches(&to_deallocate); 81 82 ASSERT_EQ(to_deallocate.Size(), 0UL); 83 ASSERT_EQ(to_deallocate.DequeueBatch(), nullptr); 84 } 85 86 TEST(SanitizerCommon, QuarantineCacheMergeBatchesOneBatch) { 87 Cache cache; 88 cache.Enqueue(cb, kFakePtr, kBlockSize); 89 ASSERT_EQ(kBlockSize + sizeof(QuarantineBatch), cache.Size()); 90 91 Cache to_deallocate; 92 cache.MergeBatches(&to_deallocate); 93 94 // Nothing to merge, nothing to deallocate. 95 ASSERT_EQ(kBlockSize + sizeof(QuarantineBatch), cache.Size()); 96 97 ASSERT_EQ(to_deallocate.Size(), 0UL); 98 ASSERT_EQ(to_deallocate.DequeueBatch(), nullptr); 99 100 DeallocateCache(&cache); 101 } 102 103 TEST(SanitizerCommon, QuarantineCacheMergeBatchesSmallBatches) { 104 // Make a cache with two batches small enough to merge. 105 Cache from; 106 from.Enqueue(cb, kFakePtr, kBlockSize); 107 Cache cache; 108 cache.Enqueue(cb, kFakePtr, kBlockSize); 109 110 cache.Transfer(&from); 111 ASSERT_EQ(kBlockSize * 2 + sizeof(QuarantineBatch) * 2, cache.Size()); 112 113 Cache to_deallocate; 114 cache.MergeBatches(&to_deallocate); 115 116 // Batches merged, one batch to deallocate. 117 ASSERT_EQ(kBlockSize * 2 + sizeof(QuarantineBatch), cache.Size()); 118 ASSERT_EQ(to_deallocate.Size(), sizeof(QuarantineBatch)); 119 120 DeallocateCache(&cache); 121 DeallocateCache(&to_deallocate); 122 } 123 124 TEST(SanitizerCommon, QuarantineCacheMergeBatchesTooBigToMerge) { 125 const uptr kNumBlocks = QuarantineBatch::kSize - 1; 126 127 // Make a cache with two batches small enough to merge. 128 Cache from; 129 Cache cache; 130 for (uptr i = 0; i < kNumBlocks; ++i) { 131 from.Enqueue(cb, kFakePtr, kBlockSize); 132 cache.Enqueue(cb, kFakePtr, kBlockSize); 133 } 134 cache.Transfer(&from); 135 ASSERT_EQ(kBlockSize * kNumBlocks * 2 + 136 sizeof(QuarantineBatch) * 2, cache.Size()); 137 138 Cache to_deallocate; 139 cache.MergeBatches(&to_deallocate); 140 141 // Batches cannot be merged. 142 ASSERT_EQ(kBlockSize * kNumBlocks * 2 + 143 sizeof(QuarantineBatch) * 2, cache.Size()); 144 ASSERT_EQ(to_deallocate.Size(), 0UL); 145 146 DeallocateCache(&cache); 147 } 148 149 TEST(SanitizerCommon, QuarantineCacheMergeBatchesALotOfBatches) { 150 const uptr kNumBatchesAfterMerge = 3; 151 const uptr kNumBlocks = QuarantineBatch::kSize * kNumBatchesAfterMerge; 152 const uptr kNumBatchesBeforeMerge = kNumBlocks; 153 154 // Make a cache with many small batches. 155 Cache cache; 156 for (uptr i = 0; i < kNumBlocks; ++i) { 157 Cache from; 158 from.Enqueue(cb, kFakePtr, kBlockSize); 159 cache.Transfer(&from); 160 } 161 162 ASSERT_EQ(kBlockSize * kNumBlocks + 163 sizeof(QuarantineBatch) * kNumBatchesBeforeMerge, cache.Size()); 164 165 Cache to_deallocate; 166 cache.MergeBatches(&to_deallocate); 167 168 // All blocks should fit into 3 batches. 169 ASSERT_EQ(kBlockSize * kNumBlocks + 170 sizeof(QuarantineBatch) * kNumBatchesAfterMerge, cache.Size()); 171 172 ASSERT_EQ(to_deallocate.Size(), 173 sizeof(QuarantineBatch) * 174 (kNumBatchesBeforeMerge - kNumBatchesAfterMerge)); 175 176 DeallocateCache(&cache); 177 DeallocateCache(&to_deallocate); 178 } 179 180 } // namespace __sanitizer 181