• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ** Copyright 2023, The Android Open Source Project
3  **
4  ** Licensed under the Apache License, Version 2.0 (the "License");
5  ** you may not use this file except in compliance with the License.
6  ** You may obtain a copy of the License at
7  **
8  **     http://www.apache.org/licenses/LICENSE-2.0
9  **
10  ** Unless required by applicable law or agreed to in writing, software
11  ** distributed under the License is distributed on an "AS IS" BASIS,
12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  ** See the License for the specific language governing permissions and
14  ** limitations under the License.
15  */
16 
17 #include "MultifileBlobCache.h"
18 
19 #include <android-base/test_utils.h>
20 #include <fcntl.h>
21 #include <fuzzer/FuzzedDataProvider.h>
22 #include <stddef.h>
23 #include <stdint.h>
24 #include <stdio.h>
25 
26 namespace android {
27 
28 constexpr size_t kMaxKeySize = 2 * 1024;
29 constexpr size_t kMaxValueSize = 6 * 1024;
30 constexpr size_t kMaxTotalSize = 32 * 1024;
31 
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)32 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
33     // To fuzz this, we're going to create a key/value pair from data
34     // and use them with MultifileBlobCache in a random order
35     // - Use the first entry in data to determine keySize
36     // - Use the second entry in data to determine valueSize
37     // - Mod each of them against half the remaining size, ensuring both fit
38     // - Create key and value using sizes from data
39     // - Use remaining data to switch between GET and SET while
40     //   tweaking the keys slightly
41     // - Ensure two cache cleaning scenarios are hit at the end
42 
43     // Ensure we have enough data to create interesting key/value pairs
44     size_t kMinInputLength = 128;
45     if (size < kMinInputLength) {
46         return 0;
47     }
48 
49     // Need non-zero sizes for interesting results
50     if (data[0] == 0 || data[1] == 0) {
51         return 0;
52     }
53 
54     // We need to divide the data up into buffers and sizes
55     FuzzedDataProvider fdp(data, size);
56 
57     // Pull two values from data for key and value size
58     EGLsizeiANDROID keySize = static_cast<EGLsizeiANDROID>(fdp.ConsumeIntegral<uint8_t>());
59     EGLsizeiANDROID valueSize = static_cast<EGLsizeiANDROID>(fdp.ConsumeIntegral<uint8_t>());
60     size -= 2 * sizeof(uint8_t);
61 
62     // Ensure key and value fit in the remaining space (cap them at half data size)
63     keySize = keySize % (size >> 1);
64     valueSize = valueSize % (size >> 1);
65 
66     // If either size ended up zero, just move on to save time
67     if (keySize == 0 || valueSize == 0) {
68         return 0;
69     }
70 
71     // Create key and value from remaining data
72     std::vector<uint8_t> key;
73     std::vector<uint8_t> value;
74     key = fdp.ConsumeBytes<uint8_t>(keySize);
75     value = fdp.ConsumeBytes<uint8_t>(valueSize);
76 
77     // Create a tempfile and a cache
78     std::unique_ptr<TemporaryFile> tempFile;
79     std::unique_ptr<MultifileBlobCache> mbc;
80 
81     tempFile.reset(new TemporaryFile());
82     mbc.reset(
83             new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
84     // With remaining data, select different paths below
85     int loopCount = 1;
86     uint8_t bumpCount = 0;
87     while (fdp.remaining_bytes() > 0) {
88         // Bounce back and forth between gets and sets
89         if (fdp.ConsumeBool()) {
90             mbc->set(key.data(), keySize, value.data(), valueSize);
91         } else {
92             uint8_t* buffer = new uint8_t[valueSize];
93             mbc->get(key.data(), keySize, buffer, valueSize);
94             delete[] buffer;
95         }
96 
97         // Bump the key and values periodically, causing different hits/misses
98         if (fdp.ConsumeBool()) {
99             key[0]++;
100             value[0]++;
101             bumpCount++;
102         }
103 
104         // Reset the key and value periodically to hit old entries
105         if (fdp.ConsumeBool()) {
106             key[0] -= bumpCount;
107             value[0] -= bumpCount;
108             bumpCount = 0;
109         }
110 
111         loopCount++;
112     }
113     mbc->finish();
114 
115     // Fill 2 keys and 2 values to max size with unique values
116     std::vector<uint8_t> maxKey1, maxKey2, maxValue1, maxValue2;
117     maxKey1.resize(kMaxKeySize, 0);
118     maxKey2.resize(kMaxKeySize, 0);
119     maxValue1.resize(kMaxValueSize, 0);
120     maxValue2.resize(kMaxValueSize, 0);
121     for (int i = 0; i < keySize && i < kMaxKeySize; ++i) {
122         maxKey1[i] = key[i];
123         maxKey2[i] = key[i] - 1;
124     }
125     for (int i = 0; i < valueSize && i < kMaxValueSize; ++i) {
126         maxValue1[i] = value[i];
127         maxValue2[i] = value[i] - 1;
128     }
129 
130     // Trigger hot cache trimming
131     // Place the maxKey/maxValue twice
132     // The first will fit, the second will trigger hot cache trimming
133     tempFile.reset(new TemporaryFile());
134     mbc.reset(
135             new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
136     uint8_t* buffer = new uint8_t[kMaxValueSize];
137     mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
138     mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
139     mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize);
140     mbc->finish();
141 
142     // Trigger cold cache trimming
143     // Create a total size small enough only one entry fits
144     // Since the cache will add a header, 2 * key + value will only hold one value, the second will
145     // overflow
146     tempFile.reset(new TemporaryFile());
147     mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, 2 * (kMaxKeySize + kMaxValueSize),
148                                      &tempFile->path[0]));
149     mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
150     mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
151     mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize);
152     mbc->finish();
153 
154     delete[] buffer;
155     return 0;
156 }
157 
158 } // namespace android
159