unplugged-system/frameworks/native/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp

159 lines
5.7 KiB
C++

/*
** Copyright 2023, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
#include "MultifileBlobCache.h"
#include <android-base/test_utils.h>
#include <fcntl.h>
#include <fuzzer/FuzzedDataProvider.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
namespace android {
constexpr size_t kMaxKeySize = 2 * 1024;
constexpr size_t kMaxValueSize = 6 * 1024;
constexpr size_t kMaxTotalSize = 32 * 1024;
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
// To fuzz this, we're going to create a key/value pair from data
// and use them with MultifileBlobCache in a random order
// - Use the first entry in data to determine keySize
// - Use the second entry in data to determine valueSize
// - Mod each of them against half the remaining size, ensuring both fit
// - Create key and value using sizes from data
// - Use remaining data to switch between GET and SET while
// tweaking the keys slightly
// - Ensure two cache cleaning scenarios are hit at the end
// Ensure we have enough data to create interesting key/value pairs
size_t kMinInputLength = 128;
if (size < kMinInputLength) {
return 0;
}
// Need non-zero sizes for interesting results
if (data[0] == 0 || data[1] == 0) {
return 0;
}
// We need to divide the data up into buffers and sizes
FuzzedDataProvider fdp(data, size);
// Pull two values from data for key and value size
EGLsizeiANDROID keySize = static_cast<EGLsizeiANDROID>(fdp.ConsumeIntegral<uint8_t>());
EGLsizeiANDROID valueSize = static_cast<EGLsizeiANDROID>(fdp.ConsumeIntegral<uint8_t>());
size -= 2 * sizeof(uint8_t);
// Ensure key and value fit in the remaining space (cap them at half data size)
keySize = keySize % (size >> 1);
valueSize = valueSize % (size >> 1);
// If either size ended up zero, just move on to save time
if (keySize == 0 || valueSize == 0) {
return 0;
}
// Create key and value from remaining data
std::vector<uint8_t> key;
std::vector<uint8_t> value;
key = fdp.ConsumeBytes<uint8_t>(keySize);
value = fdp.ConsumeBytes<uint8_t>(valueSize);
// Create a tempfile and a cache
std::unique_ptr<TemporaryFile> tempFile;
std::unique_ptr<MultifileBlobCache> mbc;
tempFile.reset(new TemporaryFile());
mbc.reset(
new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
// With remaining data, select different paths below
int loopCount = 1;
uint8_t bumpCount = 0;
while (fdp.remaining_bytes() > 0) {
// Bounce back and forth between gets and sets
if (fdp.ConsumeBool()) {
mbc->set(key.data(), keySize, value.data(), valueSize);
} else {
uint8_t* buffer = new uint8_t[valueSize];
mbc->get(key.data(), keySize, buffer, valueSize);
delete[] buffer;
}
// Bump the key and values periodically, causing different hits/misses
if (fdp.ConsumeBool()) {
key[0]++;
value[0]++;
bumpCount++;
}
// Reset the key and value periodically to hit old entries
if (fdp.ConsumeBool()) {
key[0] -= bumpCount;
value[0] -= bumpCount;
bumpCount = 0;
}
loopCount++;
}
mbc->finish();
// Fill 2 keys and 2 values to max size with unique values
std::vector<uint8_t> maxKey1, maxKey2, maxValue1, maxValue2;
maxKey1.resize(kMaxKeySize, 0);
maxKey2.resize(kMaxKeySize, 0);
maxValue1.resize(kMaxValueSize, 0);
maxValue2.resize(kMaxValueSize, 0);
for (int i = 0; i < keySize && i < kMaxKeySize; ++i) {
maxKey1[i] = key[i];
maxKey2[i] = key[i] - 1;
}
for (int i = 0; i < valueSize && i < kMaxValueSize; ++i) {
maxValue1[i] = value[i];
maxValue2[i] = value[i] - 1;
}
// Trigger hot cache trimming
// Place the maxKey/maxValue twice
// The first will fit, the second will trigger hot cache trimming
tempFile.reset(new TemporaryFile());
mbc.reset(
new MultifileBlobCache(kMaxKeySize, kMaxValueSize, kMaxTotalSize, &tempFile->path[0]));
uint8_t* buffer = new uint8_t[kMaxValueSize];
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize);
mbc->finish();
// Trigger cold cache trimming
// Create a total size small enough only one entry fits
// Since the cache will add a header, 2 * key + value will only hold one value, the second will
// overflow
tempFile.reset(new TemporaryFile());
mbc.reset(new MultifileBlobCache(kMaxKeySize, kMaxValueSize, 2 * (kMaxKeySize + kMaxValueSize),
&tempFile->path[0]));
mbc->set(maxKey1.data(), kMaxKeySize, maxValue1.data(), kMaxValueSize);
mbc->set(maxKey2.data(), kMaxKeySize, maxValue2.data(), kMaxValueSize);
mbc->get(maxKey1.data(), kMaxKeySize, buffer, kMaxValueSize);
mbc->finish();
delete[] buffer;
return 0;
}
} // namespace android