| //===-- allocator_fuzzer.cpp ----------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #define SCUDO_FUZZ | |
| #include "allocator_config.h" | |
| #include "combined.h" | |
| #include <fuzzer/FuzzedDataProvider.h> | |
| #include <vector> | |
| extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { | |
| using AllocatorT = scudo::Allocator<scudo::Config>; | |
| static AllocatorT *Instance = []() { | |
| auto *A = new AllocatorT(); | |
| A->init(); | |
| // The way we are using the allocator doesn't work properly with MTE | |
| // enabled. | |
| if (scudo::systemSupportsMemoryTagging()) | |
| A->disableMemoryTagging(); | |
| return A; | |
| }(); | |
| FuzzedDataProvider FDP(Data, Size); | |
| std::vector<void *> Allocations; | |
| Instance->setOption(scudo::Option::ReleaseInterval, 1000); | |
| constexpr size_t kMaxAllocatedBytes = 50 * 1024 * 1024; | |
| size_t TotalAllocatedBytes = 0; | |
| while (FDP.remaining_bytes() > 0) { | |
| uint8_t Op = FDP.ConsumeIntegralInRange<uint8_t>(0, 4); | |
| if ((Op == 0 || Op == 1) && TotalAllocatedBytes < kMaxAllocatedBytes) { | |
| size_t ReqSize = | |
| FDP.ConsumeIntegralInRange<size_t>(1, 1 << 20); // Up to 1MB | |
| void *Ptr; | |
| if (Op == 0) { | |
| // Allocate no alignment | |
| Ptr = Instance->allocate(ReqSize, scudo::Chunk::Origin::Malloc); | |
| } else { | |
| // Allocate with alignment | |
| size_t Alignment = | |
| 1 << FDP.ConsumeIntegralInRange<size_t>(4, 12); // 16 to 4096 | |
| Ptr = Instance->allocate(ReqSize, scudo::Chunk::Origin::Memalign, | |
| Alignment); | |
| CHECK_EQ(0, reinterpret_cast<uintptr_t>(Ptr) & (Alignment - 1)); | |
| } | |
| CHECK(Ptr != nullptr); | |
| size_t Size = Instance->getUsableSize(Ptr); | |
| TotalAllocatedBytes += Size; | |
| Allocations.push_back(Ptr); | |
| memset(Ptr, 0xff, Size); | |
| } else if (Op == 2 && !Allocations.empty()) { | |
| // Deallocate | |
| size_t Index = | |
| FDP.ConsumeIntegralInRange<size_t>(0, Allocations.size() - 1); | |
| TotalAllocatedBytes -= Instance->getUsableSize(Allocations[Index]); | |
| Instance->deallocate(Allocations[Index], scudo::Chunk::Origin::Malloc); | |
| Allocations.erase(Allocations.begin() + Index); | |
| } else if (Op == 3 && !Allocations.empty()) { | |
| // Reallocate (Assumes reallocate of a memalign does not crash). | |
| size_t Index = | |
| FDP.ConsumeIntegralInRange<size_t>(0, Allocations.size() - 1); | |
| size_t OldSize = Instance->getUsableSize(Allocations[Index]); | |
| TotalAllocatedBytes -= OldSize; | |
| size_t NewSize = FDP.ConsumeIntegralInRange<size_t>(1, 1 << 20); | |
| void *NewPtr = Instance->reallocate(Allocations[Index], NewSize); | |
| if (NewSize == 0) { | |
| CHECK(NewPtr == nullptr); | |
| Allocations.erase(Allocations.begin() + Index); | |
| } else { | |
| CHECK(NewPtr != nullptr); | |
| size_t Size = Instance->getUsableSize(NewPtr); | |
| memset(NewPtr, 0xff, Size); | |
| Allocations[Index] = NewPtr; | |
| TotalAllocatedBytes -= Size; | |
| } | |
| } else if (Op == 4) { | |
| // ReleaseToOS | |
| scudo::ReleaseToOS ReleaseType = | |
| static_cast<scudo::ReleaseToOS>(FDP.ConsumeIntegralInRange<size_t>( | |
| 0, static_cast<size_t>(scudo::ReleaseToOS::Last))); | |
| Instance->releaseToOS(ReleaseType); | |
| } | |
| } | |
| // Cleanup remaining | |
| for (void *Ptr : Allocations) { | |
| Instance->deallocate(Ptr, scudo::Chunk::Origin::Malloc); | |
| } | |
| return 0; | |
| } |