diff options
-rw-r--r-- | opengl/libs/EGL/MultifileBlobCache.cpp | 4 | ||||
-rw-r--r-- | opengl/libs/EGL/fuzzer/Android.bp | 42 | ||||
-rw-r--r-- | opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp | 158 |
3 files changed, 202 insertions, 2 deletions
diff --git a/opengl/libs/EGL/MultifileBlobCache.cpp b/opengl/libs/EGL/MultifileBlobCache.cpp index b5ddd601ce..7ffdac7ea7 100644 --- a/opengl/libs/EGL/MultifileBlobCache.cpp +++ b/opengl/libs/EGL/MultifileBlobCache.cpp @@ -276,11 +276,10 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi uint8_t* buffer = new uint8_t[fileSize]; - // Write placeholders for magic and CRC until deferred thread complets the write + // Write placeholders for magic and CRC until deferred thread completes the write android::MultifileHeader header = {kMultifileMagic, kCrcPlaceholder, keySize, valueSize}; memcpy(static_cast<void*>(buffer), static_cast<const void*>(&header), sizeof(android::MultifileHeader)); - // Write the key and value after the header memcpy(static_cast<void*>(buffer + sizeof(MultifileHeader)), static_cast<const void*>(key), keySize); @@ -301,6 +300,7 @@ void MultifileBlobCache::set(const void* key, EGLsizeiANDROID keySize, const voi // Sending -1 as the fd indicates we don't have an fd for this if (!addToHotCache(entryHash, -1, buffer, fileSize)) { ALOGE("SET: Failed to add %u to hot cache", entryHash); + delete[] buffer; return; } diff --git a/opengl/libs/EGL/fuzzer/Android.bp b/opengl/libs/EGL/fuzzer/Android.bp new file mode 100644 index 0000000000..022a2a3f06 --- /dev/null +++ b/opengl/libs/EGL/fuzzer/Android.bp @@ -0,0 +1,42 @@ +// Copyright (C) 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. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_native_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_native_license"], +} + +cc_fuzz { + name: "MultifileBlobCache_fuzzer", + + fuzz_config: { + cc: ["cnorthrop@google.com"], + libfuzzer_options: ["len_control=0"], + }, + + static_libs: [ + "libbase", + "libEGL_blobCache", + "liblog", + "libutils", + ], + + srcs: [ + "MultifileBlobCache_fuzzer.cpp", + ], +} diff --git a/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp new file mode 100644 index 0000000000..633cc9c51b --- /dev/null +++ b/opengl/libs/EGL/fuzzer/MultifileBlobCache_fuzzer.cpp @@ -0,0 +1,158 @@ +/* + ** 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 |