blob: 2db555a545bf7ae10621b534823df29a7fb0448b [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/elfldltl/container.h>
#include <lib/elfldltl/diagnostics.h>
#include <lib/elfldltl/load.h>
#include <lib/elfldltl/phdr.h>
#include <lib/elfldltl/testing/fuzzer.h>
#include <vector>
namespace {
constexpr size_t kPageSize = 0x1000;
constexpr auto segment_less = [](const auto& first, const auto& second) -> bool {
// Each argument is of a std::variant<...> type and they may or may not
// both be the same variant.
return std::visit(
[](const auto& first, const auto& second) -> bool {
return first.vaddr() + first.memsz() <= second.vaddr();
},
first, second);
};
template <class Elf>
struct LoadInfoFuzzer {
using size_type = typename Elf::size_type;
using Phdr = typename Elf::Phdr;
using LoadInfo = elfldltl::LoadInfo<Elf, elfldltl::StdContainer<std::vector>::Container>;
using FuzzerInputs = elfldltl::testing::FuzzerInput<alignof(Phdr), Phdr, uint8_t>;
int operator()(FuzzedDataProvider& provider) const {
FuzzerInputs inputs(provider);
auto [phdrs, blob] = inputs.inputs();
elfldltl::Diagnostics diag{[](auto&&...) -> bool { return true; }};
LoadInfo info;
elfldltl::DecodePhdrs(diag, phdrs, info.GetPhdrObserver(kPageSize));
// Verify the segments are in ascending vaddr order.
ZX_ASSERT(std::is_sorted(info.segments().begin(), info.segments().end(), segment_less));
// Verify no empty segments.
for (const auto& segment : info.segments()) {
std::visit([](const auto& segment) { ZX_ASSERT(segment.memsz() > 0); }, segment);
}
// Fuzz FindSegment.
FuzzedDataProvider blob_provider(blob.data(), blob.size());
while (blob_provider.remaining_bytes() > 0) {
size_type vaddr = blob_provider.ConsumeIntegral<size_type>();
auto found = info.FindSegment(vaddr);
if (found == info.segments().end()) {
// Not found, verify no segment matches.
for (const auto& segment : info.segments()) {
std::visit(
[vaddr](const auto& segment) {
ZX_ASSERT(vaddr < segment.vaddr() || vaddr >= segment.vaddr() + segment.memsz());
},
segment);
}
} else {
// Verify the found segment does match.
std::visit(
[vaddr](const auto& segment) {
ZX_ASSERT(segment.vaddr() <= vaddr);
ZX_ASSERT(vaddr - segment.vaddr() < segment.memsz());
},
*found);
}
}
return 0;
}
};
using Fuzzer = elfldltl::testing::ElfFuzzer<LoadInfoFuzzer>;
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider provider(data, size);
return Fuzzer{}(provider);
}