1*76559068SAndroid Build Coastguard Worker //===-- quarantine_test.cpp -------------------------------------*- C++ -*-===//
2*76559068SAndroid Build Coastguard Worker //
3*76559068SAndroid Build Coastguard Worker // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*76559068SAndroid Build Coastguard Worker // See https://llvm.org/LICENSE.txt for license information.
5*76559068SAndroid Build Coastguard Worker // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*76559068SAndroid Build Coastguard Worker //
7*76559068SAndroid Build Coastguard Worker //===----------------------------------------------------------------------===//
8*76559068SAndroid Build Coastguard Worker
9*76559068SAndroid Build Coastguard Worker #include "tests/scudo_unit_test.h"
10*76559068SAndroid Build Coastguard Worker
11*76559068SAndroid Build Coastguard Worker #include "quarantine.h"
12*76559068SAndroid Build Coastguard Worker
13*76559068SAndroid Build Coastguard Worker #include <pthread.h>
14*76559068SAndroid Build Coastguard Worker #include <stdlib.h>
15*76559068SAndroid Build Coastguard Worker
16*76559068SAndroid Build Coastguard Worker static void *FakePtr = reinterpret_cast<void *>(0xFA83FA83);
17*76559068SAndroid Build Coastguard Worker static const scudo::uptr BlockSize = 8UL;
18*76559068SAndroid Build Coastguard Worker static const scudo::uptr LargeBlockSize = 16384UL;
19*76559068SAndroid Build Coastguard Worker
20*76559068SAndroid Build Coastguard Worker struct QuarantineCallback {
recycleQuarantineCallback21*76559068SAndroid Build Coastguard Worker void recycle(void *P) { EXPECT_EQ(P, FakePtr); }
allocateQuarantineCallback22*76559068SAndroid Build Coastguard Worker void *allocate(scudo::uptr Size) { return malloc(Size); }
deallocateQuarantineCallback23*76559068SAndroid Build Coastguard Worker void deallocate(void *P) { free(P); }
24*76559068SAndroid Build Coastguard Worker };
25*76559068SAndroid Build Coastguard Worker
26*76559068SAndroid Build Coastguard Worker typedef scudo::GlobalQuarantine<QuarantineCallback, void> QuarantineT;
27*76559068SAndroid Build Coastguard Worker typedef typename QuarantineT::CacheT CacheT;
28*76559068SAndroid Build Coastguard Worker
29*76559068SAndroid Build Coastguard Worker static QuarantineCallback Cb;
30*76559068SAndroid Build Coastguard Worker
deallocateCache(CacheT * Cache)31*76559068SAndroid Build Coastguard Worker static void deallocateCache(CacheT *Cache) {
32*76559068SAndroid Build Coastguard Worker while (scudo::QuarantineBatch *Batch = Cache->dequeueBatch())
33*76559068SAndroid Build Coastguard Worker Cb.deallocate(Batch);
34*76559068SAndroid Build Coastguard Worker }
35*76559068SAndroid Build Coastguard Worker
TEST(ScudoQuarantineTest,QuarantineBatchMerge)36*76559068SAndroid Build Coastguard Worker TEST(ScudoQuarantineTest, QuarantineBatchMerge) {
37*76559068SAndroid Build Coastguard Worker // Verify the trivial case.
38*76559068SAndroid Build Coastguard Worker scudo::QuarantineBatch Into;
39*76559068SAndroid Build Coastguard Worker Into.init(FakePtr, 4UL);
40*76559068SAndroid Build Coastguard Worker scudo::QuarantineBatch From;
41*76559068SAndroid Build Coastguard Worker From.init(FakePtr, 8UL);
42*76559068SAndroid Build Coastguard Worker
43*76559068SAndroid Build Coastguard Worker Into.merge(&From);
44*76559068SAndroid Build Coastguard Worker
45*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Into.Count, 2UL);
46*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Into.Batch[0], FakePtr);
47*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Into.Batch[1], FakePtr);
48*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Into.Size, 12UL + sizeof(scudo::QuarantineBatch));
49*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Into.getQuarantinedSize(), 12UL);
50*76559068SAndroid Build Coastguard Worker
51*76559068SAndroid Build Coastguard Worker EXPECT_EQ(From.Count, 0UL);
52*76559068SAndroid Build Coastguard Worker EXPECT_EQ(From.Size, sizeof(scudo::QuarantineBatch));
53*76559068SAndroid Build Coastguard Worker EXPECT_EQ(From.getQuarantinedSize(), 0UL);
54*76559068SAndroid Build Coastguard Worker
55*76559068SAndroid Build Coastguard Worker // Merge the batch to the limit.
56*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 2; I < scudo::QuarantineBatch::MaxCount; ++I)
57*76559068SAndroid Build Coastguard Worker From.push_back(FakePtr, 8UL);
58*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Into.Count + From.Count == scudo::QuarantineBatch::MaxCount);
59*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Into.canMerge(&From));
60*76559068SAndroid Build Coastguard Worker
61*76559068SAndroid Build Coastguard Worker Into.merge(&From);
62*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(Into.Count == scudo::QuarantineBatch::MaxCount);
63*76559068SAndroid Build Coastguard Worker
64*76559068SAndroid Build Coastguard Worker // No more space, not even for one element.
65*76559068SAndroid Build Coastguard Worker From.init(FakePtr, 8UL);
66*76559068SAndroid Build Coastguard Worker
67*76559068SAndroid Build Coastguard Worker EXPECT_FALSE(Into.canMerge(&From));
68*76559068SAndroid Build Coastguard Worker }
69*76559068SAndroid Build Coastguard Worker
TEST(ScudoQuarantineTest,QuarantineCacheMergeBatchesEmpty)70*76559068SAndroid Build Coastguard Worker TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesEmpty) {
71*76559068SAndroid Build Coastguard Worker CacheT Cache;
72*76559068SAndroid Build Coastguard Worker CacheT ToDeallocate;
73*76559068SAndroid Build Coastguard Worker Cache.init();
74*76559068SAndroid Build Coastguard Worker ToDeallocate.init();
75*76559068SAndroid Build Coastguard Worker Cache.mergeBatches(&ToDeallocate);
76*76559068SAndroid Build Coastguard Worker
77*76559068SAndroid Build Coastguard Worker EXPECT_EQ(ToDeallocate.getSize(), 0UL);
78*76559068SAndroid Build Coastguard Worker EXPECT_EQ(ToDeallocate.dequeueBatch(), nullptr);
79*76559068SAndroid Build Coastguard Worker }
80*76559068SAndroid Build Coastguard Worker
TEST(SanitizerCommon,QuarantineCacheMergeBatchesOneBatch)81*76559068SAndroid Build Coastguard Worker TEST(SanitizerCommon, QuarantineCacheMergeBatchesOneBatch) {
82*76559068SAndroid Build Coastguard Worker CacheT Cache;
83*76559068SAndroid Build Coastguard Worker Cache.init();
84*76559068SAndroid Build Coastguard Worker Cache.enqueue(Cb, FakePtr, BlockSize);
85*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize + sizeof(scudo::QuarantineBatch), Cache.getSize());
86*76559068SAndroid Build Coastguard Worker
87*76559068SAndroid Build Coastguard Worker CacheT ToDeallocate;
88*76559068SAndroid Build Coastguard Worker ToDeallocate.init();
89*76559068SAndroid Build Coastguard Worker Cache.mergeBatches(&ToDeallocate);
90*76559068SAndroid Build Coastguard Worker
91*76559068SAndroid Build Coastguard Worker // Nothing to merge, nothing to deallocate.
92*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize + sizeof(scudo::QuarantineBatch), Cache.getSize());
93*76559068SAndroid Build Coastguard Worker
94*76559068SAndroid Build Coastguard Worker EXPECT_EQ(ToDeallocate.getSize(), 0UL);
95*76559068SAndroid Build Coastguard Worker EXPECT_EQ(ToDeallocate.dequeueBatch(), nullptr);
96*76559068SAndroid Build Coastguard Worker
97*76559068SAndroid Build Coastguard Worker deallocateCache(&Cache);
98*76559068SAndroid Build Coastguard Worker }
99*76559068SAndroid Build Coastguard Worker
TEST(ScudoQuarantineTest,QuarantineCacheMergeBatchesSmallBatches)100*76559068SAndroid Build Coastguard Worker TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesSmallBatches) {
101*76559068SAndroid Build Coastguard Worker // Make a Cache with two batches small enough to merge.
102*76559068SAndroid Build Coastguard Worker CacheT From;
103*76559068SAndroid Build Coastguard Worker From.init();
104*76559068SAndroid Build Coastguard Worker From.enqueue(Cb, FakePtr, BlockSize);
105*76559068SAndroid Build Coastguard Worker CacheT Cache;
106*76559068SAndroid Build Coastguard Worker Cache.init();
107*76559068SAndroid Build Coastguard Worker Cache.enqueue(Cb, FakePtr, BlockSize);
108*76559068SAndroid Build Coastguard Worker
109*76559068SAndroid Build Coastguard Worker Cache.transfer(&From);
110*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize * 2 + sizeof(scudo::QuarantineBatch) * 2,
111*76559068SAndroid Build Coastguard Worker Cache.getSize());
112*76559068SAndroid Build Coastguard Worker
113*76559068SAndroid Build Coastguard Worker CacheT ToDeallocate;
114*76559068SAndroid Build Coastguard Worker ToDeallocate.init();
115*76559068SAndroid Build Coastguard Worker Cache.mergeBatches(&ToDeallocate);
116*76559068SAndroid Build Coastguard Worker
117*76559068SAndroid Build Coastguard Worker // Batches merged, one batch to deallocate.
118*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize * 2 + sizeof(scudo::QuarantineBatch), Cache.getSize());
119*76559068SAndroid Build Coastguard Worker EXPECT_EQ(ToDeallocate.getSize(), sizeof(scudo::QuarantineBatch));
120*76559068SAndroid Build Coastguard Worker
121*76559068SAndroid Build Coastguard Worker deallocateCache(&Cache);
122*76559068SAndroid Build Coastguard Worker deallocateCache(&ToDeallocate);
123*76559068SAndroid Build Coastguard Worker }
124*76559068SAndroid Build Coastguard Worker
TEST(ScudoQuarantineTest,QuarantineCacheMergeBatchesTooBigToMerge)125*76559068SAndroid Build Coastguard Worker TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesTooBigToMerge) {
126*76559068SAndroid Build Coastguard Worker const scudo::uptr NumBlocks = scudo::QuarantineBatch::MaxCount - 1;
127*76559068SAndroid Build Coastguard Worker
128*76559068SAndroid Build Coastguard Worker // Make a Cache with two batches small enough to merge.
129*76559068SAndroid Build Coastguard Worker CacheT From;
130*76559068SAndroid Build Coastguard Worker CacheT Cache;
131*76559068SAndroid Build Coastguard Worker From.init();
132*76559068SAndroid Build Coastguard Worker Cache.init();
133*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < NumBlocks; ++I) {
134*76559068SAndroid Build Coastguard Worker From.enqueue(Cb, FakePtr, BlockSize);
135*76559068SAndroid Build Coastguard Worker Cache.enqueue(Cb, FakePtr, BlockSize);
136*76559068SAndroid Build Coastguard Worker }
137*76559068SAndroid Build Coastguard Worker Cache.transfer(&From);
138*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize * NumBlocks * 2 + sizeof(scudo::QuarantineBatch) * 2,
139*76559068SAndroid Build Coastguard Worker Cache.getSize());
140*76559068SAndroid Build Coastguard Worker
141*76559068SAndroid Build Coastguard Worker CacheT ToDeallocate;
142*76559068SAndroid Build Coastguard Worker ToDeallocate.init();
143*76559068SAndroid Build Coastguard Worker Cache.mergeBatches(&ToDeallocate);
144*76559068SAndroid Build Coastguard Worker
145*76559068SAndroid Build Coastguard Worker // Batches cannot be merged.
146*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize * NumBlocks * 2 + sizeof(scudo::QuarantineBatch) * 2,
147*76559068SAndroid Build Coastguard Worker Cache.getSize());
148*76559068SAndroid Build Coastguard Worker EXPECT_EQ(ToDeallocate.getSize(), 0UL);
149*76559068SAndroid Build Coastguard Worker
150*76559068SAndroid Build Coastguard Worker deallocateCache(&Cache);
151*76559068SAndroid Build Coastguard Worker }
152*76559068SAndroid Build Coastguard Worker
TEST(ScudoQuarantineTest,QuarantineCacheMergeBatchesALotOfBatches)153*76559068SAndroid Build Coastguard Worker TEST(ScudoQuarantineTest, QuarantineCacheMergeBatchesALotOfBatches) {
154*76559068SAndroid Build Coastguard Worker const scudo::uptr NumBatchesAfterMerge = 3;
155*76559068SAndroid Build Coastguard Worker const scudo::uptr NumBlocks =
156*76559068SAndroid Build Coastguard Worker scudo::QuarantineBatch::MaxCount * NumBatchesAfterMerge;
157*76559068SAndroid Build Coastguard Worker const scudo::uptr NumBatchesBeforeMerge = NumBlocks;
158*76559068SAndroid Build Coastguard Worker
159*76559068SAndroid Build Coastguard Worker // Make a Cache with many small batches.
160*76559068SAndroid Build Coastguard Worker CacheT Cache;
161*76559068SAndroid Build Coastguard Worker Cache.init();
162*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < NumBlocks; ++I) {
163*76559068SAndroid Build Coastguard Worker CacheT From;
164*76559068SAndroid Build Coastguard Worker From.init();
165*76559068SAndroid Build Coastguard Worker From.enqueue(Cb, FakePtr, BlockSize);
166*76559068SAndroid Build Coastguard Worker Cache.transfer(&From);
167*76559068SAndroid Build Coastguard Worker }
168*76559068SAndroid Build Coastguard Worker
169*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize * NumBlocks +
170*76559068SAndroid Build Coastguard Worker sizeof(scudo::QuarantineBatch) * NumBatchesBeforeMerge,
171*76559068SAndroid Build Coastguard Worker Cache.getSize());
172*76559068SAndroid Build Coastguard Worker
173*76559068SAndroid Build Coastguard Worker CacheT ToDeallocate;
174*76559068SAndroid Build Coastguard Worker ToDeallocate.init();
175*76559068SAndroid Build Coastguard Worker Cache.mergeBatches(&ToDeallocate);
176*76559068SAndroid Build Coastguard Worker
177*76559068SAndroid Build Coastguard Worker // All blocks should fit Into 3 batches.
178*76559068SAndroid Build Coastguard Worker EXPECT_EQ(BlockSize * NumBlocks +
179*76559068SAndroid Build Coastguard Worker sizeof(scudo::QuarantineBatch) * NumBatchesAfterMerge,
180*76559068SAndroid Build Coastguard Worker Cache.getSize());
181*76559068SAndroid Build Coastguard Worker
182*76559068SAndroid Build Coastguard Worker EXPECT_EQ(ToDeallocate.getSize(),
183*76559068SAndroid Build Coastguard Worker sizeof(scudo::QuarantineBatch) *
184*76559068SAndroid Build Coastguard Worker (NumBatchesBeforeMerge - NumBatchesAfterMerge));
185*76559068SAndroid Build Coastguard Worker
186*76559068SAndroid Build Coastguard Worker deallocateCache(&Cache);
187*76559068SAndroid Build Coastguard Worker deallocateCache(&ToDeallocate);
188*76559068SAndroid Build Coastguard Worker }
189*76559068SAndroid Build Coastguard Worker
190*76559068SAndroid Build Coastguard Worker static const scudo::uptr MaxQuarantineSize = 1024UL << 10; // 1MB
191*76559068SAndroid Build Coastguard Worker static const scudo::uptr MaxCacheSize = 256UL << 10; // 256KB
192*76559068SAndroid Build Coastguard Worker
TEST(ScudoQuarantineTest,GlobalQuarantine)193*76559068SAndroid Build Coastguard Worker TEST(ScudoQuarantineTest, GlobalQuarantine) {
194*76559068SAndroid Build Coastguard Worker QuarantineT Quarantine;
195*76559068SAndroid Build Coastguard Worker CacheT Cache;
196*76559068SAndroid Build Coastguard Worker Cache.init();
197*76559068SAndroid Build Coastguard Worker Quarantine.init(MaxQuarantineSize, MaxCacheSize);
198*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Quarantine.getMaxSize(), MaxQuarantineSize);
199*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Quarantine.getCacheSize(), MaxCacheSize);
200*76559068SAndroid Build Coastguard Worker
201*76559068SAndroid Build Coastguard Worker bool DrainOccurred = false;
202*76559068SAndroid Build Coastguard Worker scudo::uptr CacheSize = Cache.getSize();
203*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Cache.getSize(), 0UL);
204*76559068SAndroid Build Coastguard Worker // We quarantine enough blocks that a drain has to occur. Verify this by
205*76559068SAndroid Build Coastguard Worker // looking for a decrease of the size of the cache.
206*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 128UL; I++) {
207*76559068SAndroid Build Coastguard Worker Quarantine.put(&Cache, Cb, FakePtr, LargeBlockSize);
208*76559068SAndroid Build Coastguard Worker if (!DrainOccurred && Cache.getSize() < CacheSize)
209*76559068SAndroid Build Coastguard Worker DrainOccurred = true;
210*76559068SAndroid Build Coastguard Worker CacheSize = Cache.getSize();
211*76559068SAndroid Build Coastguard Worker }
212*76559068SAndroid Build Coastguard Worker EXPECT_TRUE(DrainOccurred);
213*76559068SAndroid Build Coastguard Worker
214*76559068SAndroid Build Coastguard Worker Quarantine.drainAndRecycle(&Cache, Cb);
215*76559068SAndroid Build Coastguard Worker EXPECT_EQ(Cache.getSize(), 0UL);
216*76559068SAndroid Build Coastguard Worker
217*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
218*76559068SAndroid Build Coastguard Worker Quarantine.getStats(&Str);
219*76559068SAndroid Build Coastguard Worker Str.output();
220*76559068SAndroid Build Coastguard Worker }
221*76559068SAndroid Build Coastguard Worker
222*76559068SAndroid Build Coastguard Worker struct PopulateQuarantineThread {
223*76559068SAndroid Build Coastguard Worker pthread_t Thread;
224*76559068SAndroid Build Coastguard Worker QuarantineT *Quarantine;
225*76559068SAndroid Build Coastguard Worker CacheT Cache;
226*76559068SAndroid Build Coastguard Worker };
227*76559068SAndroid Build Coastguard Worker
populateQuarantine(void * Param)228*76559068SAndroid Build Coastguard Worker void *populateQuarantine(void *Param) {
229*76559068SAndroid Build Coastguard Worker PopulateQuarantineThread *P = static_cast<PopulateQuarantineThread *>(Param);
230*76559068SAndroid Build Coastguard Worker P->Cache.init();
231*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < 128UL; I++)
232*76559068SAndroid Build Coastguard Worker P->Quarantine->put(&P->Cache, Cb, FakePtr, LargeBlockSize);
233*76559068SAndroid Build Coastguard Worker return 0;
234*76559068SAndroid Build Coastguard Worker }
235*76559068SAndroid Build Coastguard Worker
TEST(ScudoQuarantineTest,ThreadedGlobalQuarantine)236*76559068SAndroid Build Coastguard Worker TEST(ScudoQuarantineTest, ThreadedGlobalQuarantine) {
237*76559068SAndroid Build Coastguard Worker QuarantineT Quarantine;
238*76559068SAndroid Build Coastguard Worker Quarantine.init(MaxQuarantineSize, MaxCacheSize);
239*76559068SAndroid Build Coastguard Worker
240*76559068SAndroid Build Coastguard Worker const scudo::uptr NumberOfThreads = 32U;
241*76559068SAndroid Build Coastguard Worker PopulateQuarantineThread T[NumberOfThreads];
242*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < NumberOfThreads; I++) {
243*76559068SAndroid Build Coastguard Worker T[I].Quarantine = &Quarantine;
244*76559068SAndroid Build Coastguard Worker pthread_create(&T[I].Thread, 0, populateQuarantine, &T[I]);
245*76559068SAndroid Build Coastguard Worker }
246*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < NumberOfThreads; I++)
247*76559068SAndroid Build Coastguard Worker pthread_join(T[I].Thread, 0);
248*76559068SAndroid Build Coastguard Worker
249*76559068SAndroid Build Coastguard Worker scudo::ScopedString Str;
250*76559068SAndroid Build Coastguard Worker Quarantine.getStats(&Str);
251*76559068SAndroid Build Coastguard Worker Str.output();
252*76559068SAndroid Build Coastguard Worker
253*76559068SAndroid Build Coastguard Worker for (scudo::uptr I = 0; I < NumberOfThreads; I++)
254*76559068SAndroid Build Coastguard Worker Quarantine.drainAndRecycle(&T[I].Cache, Cb);
255*76559068SAndroid Build Coastguard Worker }
256