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