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