blob: f8d699553939fab981bef2da1e6d21984a135a1c [file] [edit]
//===-- 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 <format>
#include <fuzzer/FuzzedDataProvider.h>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
FuzzedDataProvider FDP(Data, Size);
if (FDP.remaining_bytes() == 0)
return 0;
// TODO: Make this configurable.
static bool ZeroFill = true;
// After a number of operation, reset the the allocator with different
// parameters.
using AllocatorT = scudo::Allocator<scudo::Config>;
static std::unique_ptr<AllocatorT> Allocator;
if (Allocator == nullptr) {
Allocator.reset(new AllocatorT());
Allocator->init();
// The way we are using the allocator doesn't work properly with MTE
// enabled.
if (scudo::systemSupportsMemoryTagging())
Allocator->disableMemoryTagging();
Allocator->setOption(scudo::Option::ReleaseInterval, 1000);
// Set the fill type once for the allocator, changing the fill type during
// the run can lead to some data not being zeroed if zero on deallocate
// is enabled.
std::cout << std::format("Allocator Created with ZeroFill {}\n", ZeroFill);
Allocator->setFillContents(ZeroFill ? scudo::ZeroFill : scudo::NoFill);
}
constexpr size_t kMaxAllocatedBytes = 50 * 1024 * 1024;
size_t TotalAllocatedBytes = 0;
std::vector<void *> Allocations;
while (FDP.remaining_bytes() > 0) {
uint8_t Op = FDP.ConsumeIntegralInRange<uint8_t>(0, 4);
if (Op == 0 || Op == 1) {
if (TotalAllocatedBytes >= kMaxAllocatedBytes)
continue;
bool ZeroContents = FDP.ConsumeBool();
size_t ReqSize =
FDP.ConsumeIntegralInRange<size_t>(1, 1 << 20); // Up to 1MB
void *Ptr;
if (Op == 0) {
// Allocate default alignment
Ptr = Allocator->allocate(ReqSize, scudo::Chunk::Origin::Malloc, 2,
ZeroContents);
} else {
// Allocate with alignment
size_t Alignment =
1 << FDP.ConsumeIntegralInRange<size_t>(4, 12); // 16 to 4096
Ptr = Allocator->allocate(ReqSize, scudo::Chunk::Origin::Memalign,
Alignment, ZeroContents);
CHECK_EQ(0, reinterpret_cast<uintptr_t>(Ptr) & (Alignment - 1));
}
CHECK(Ptr != nullptr);
size_t Size = Allocator->getUsableSize(Ptr);
TotalAllocatedBytes += Size;
Allocations.push_back(Ptr);
if (ZeroContents || ZeroFill) {
bool Mismatch = false;
uint8_t *Data = reinterpret_cast<uint8_t *>(Ptr);
for (size_t i = 0; i < Size; i++) {
if (Data[i] != 0) {
if (!Mismatch) {
std::cerr << std::format(
"Failed Ptr {} Size {} ZeroContents {} ZeroFill {}\n", Ptr,
Size, ZeroContents, ZeroFill);
Mismatch = true;
}
std::cerr << std::format(" Index {} Ptr {} Value {}\n", i,
(void *)&Data[i], Data[i]);
}
}
if (Mismatch)
abort();
}
memset(Ptr, 0xff, Size);
} else if (Op == 2) {
if (Allocations.empty())
continue;
// Deallocate
size_t Index =
FDP.ConsumeIntegralInRange<size_t>(0, Allocations.size() - 1);
TotalAllocatedBytes -= Allocator->getUsableSize(Allocations[Index]);
Allocator->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 = Allocator->getUsableSize(Allocations[Index]);
TotalAllocatedBytes -= OldSize;
size_t NewSize = FDP.ConsumeIntegralInRange<size_t>(1, 1 << 20);
void *NewPtr = Allocator->reallocate(Allocations[Index], NewSize);
if (NewSize == 0) {
CHECK(NewPtr == nullptr);
Allocations.erase(Allocations.begin() + Index);
} else {
CHECK(NewPtr != nullptr);
NewSize = Allocator->getUsableSize(NewPtr);
memset(NewPtr, 0xff, NewSize);
Allocations[Index] = NewPtr;
TotalAllocatedBytes += NewSize;
}
} else if (Op == 4) {
// ReleaseToOS
scudo::ReleaseToOS ReleaseType =
static_cast<scudo::ReleaseToOS>(FDP.ConsumeIntegralInRange<size_t>(
0, static_cast<size_t>(scudo::ReleaseToOS::Last)));
Allocator->releaseToOS(ReleaseType);
}
}
for (void *Ptr : Allocations) {
Allocator->deallocate(Ptr, scudo::Chunk::Origin::Malloc);
}
return 0;
}