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 {
Recycle__sanitizer::QuarantineCallback21 void Recycle(void *m) {}
Allocate__sanitizer::QuarantineCallback22 void *Allocate(uptr size) {
23 return malloc(size);
24 }
Deallocate__sanitizer::QuarantineCallback25 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
DeallocateCache(Cache * cache)37 static void DeallocateCache(Cache *cache) {
38 while (QuarantineBatch *batch = cache->DequeueBatch())
39 cb.Deallocate(batch);
40 }
41
TEST(SanitizerCommon,QuarantineBatchMerge)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
TEST(SanitizerCommon,QuarantineCacheMergeBatchesEmpty)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
TEST(SanitizerCommon,QuarantineCacheMergeBatchesOneBatch)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
TEST(SanitizerCommon,QuarantineCacheMergeBatchesSmallBatches)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
TEST(SanitizerCommon,QuarantineCacheMergeBatchesTooBigToMerge)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
TEST(SanitizerCommon,QuarantineCacheMergeBatchesALotOfBatches)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