[FuzzMutate] Move LLVM module (de)serialization from FuzzerCLI -> IRMutator. NFC

These are not directly related to the CLI, and are mostly (always?) used when
mutating the modules as part of fuzzing.

Motivation: split FuzzerCLI into its own library that does not depend on IR.
Subprojects that don't use IR should be be fuzzed without the dependency.

Differential Revision: https://reviews.llvm.org/D125080
This commit is contained in:
Sam McCall 2022-05-06 10:15:41 +02:00
parent eeb44579f1
commit 0a83ff83af
4 changed files with 73 additions and 73 deletions

View File

@ -20,8 +20,6 @@
namespace llvm {
class LLVMContext;
class Module;
class StringRef;
/// Parse cl::opts from a fuzz target commandline.
@ -54,29 +52,6 @@ using FuzzerInitFun = int (*)(int *argc, char ***argv);
int runFuzzerOnInputs(int ArgC, char *ArgV[], FuzzerTestFun TestOne,
FuzzerInitFun Init = [](int *, char ***) { return 0; });
/// Fuzzer friendly interface for the llvm bitcode parser.
///
/// \param Data Bitcode we are going to parse
/// \param Size Size of the 'Data' in bytes
/// \return New module or nullptr in case of error
std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
LLVMContext &Context);
/// Fuzzer friendly interface for the llvm bitcode printer.
///
/// \param M Module to print
/// \param Dest Location to store serialized module
/// \param MaxSize Size of the destination buffer
/// \return Number of bytes that were written. When module size exceeds MaxSize
/// returns 0 and leaves Dest unchanged.
size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize);
/// Try to parse module and verify it. May output verification errors to the
/// errs().
/// \return New module or nullptr in case of error.
std::unique_ptr<Module> parseAndVerify(const uint8_t *Data, size_t Size,
LLVMContext &Context);
} // end llvm namespace
} // namespace llvm
#endif // LLVM_FUZZMUTATE_FUZZERCLI_H

View File

@ -10,6 +10,9 @@
// configurable set of strategies. Some common strategies are also included
// here.
//
// Fuzzer-friendly (de)serialization functions are also provided, as these
// are usually needed when mutating IR.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_FUZZMUTATE_IRMUTATOR_H
@ -113,6 +116,29 @@ public:
void mutate(Instruction &Inst, RandomIRBuilder &IB) override;
};
/// Fuzzer friendly interface for the llvm bitcode parser.
///
/// \param Data Bitcode we are going to parse
/// \param Size Size of the 'Data' in bytes
/// \return New module or nullptr in case of error
std::unique_ptr<Module> parseModule(const uint8_t *Data, size_t Size,
LLVMContext &Context);
/// Fuzzer friendly interface for the llvm bitcode printer.
///
/// \param M Module to print
/// \param Dest Location to store serialized module
/// \param MaxSize Size of the destination buffer
/// \return Number of bytes that were written. When module size exceeds MaxSize
/// returns 0 and leaves Dest unchanged.
size_t writeModule(const Module &M, uint8_t *Dest, size_t MaxSize);
/// Try to parse module and verify it. May output verification errors to the
/// errs().
/// \return New module or nullptr in case of error.
std::unique_ptr<Module> parseAndVerify(const uint8_t *Data, size_t Size,
LLVMContext &Context);
} // end llvm namespace
#endif // LLVM_FUZZMUTATE_IRMUTATOR_H

View File

@ -9,14 +9,8 @@
#include "llvm/FuzzMutate/FuzzerCLI.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@ -165,44 +159,3 @@ int llvm::runFuzzerOnInputs(int ArgC, char *ArgV[], FuzzerTestFun TestOne,
}
return 0;
}
std::unique_ptr<Module> llvm::parseModule(
const uint8_t *Data, size_t Size, LLVMContext &Context) {
if (Size <= 1)
// We get bogus data given an empty corpus - just create a new module.
return std::make_unique<Module>("M", Context);
auto Buffer = MemoryBuffer::getMemBuffer(
StringRef(reinterpret_cast<const char *>(Data), Size), "Fuzzer input",
/*RequiresNullTerminator=*/false);
SMDiagnostic Err;
auto M = parseBitcodeFile(Buffer->getMemBufferRef(), Context);
if (Error E = M.takeError()) {
errs() << toString(std::move(E)) << "\n";
return nullptr;
}
return std::move(M.get());
}
size_t llvm::writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) {
std::string Buf;
{
raw_string_ostream OS(Buf);
WriteBitcodeToFile(M, OS);
}
if (Buf.size() > MaxSize)
return 0;
memcpy(Dest, Buf.data(), Buf.size());
return Buf.size();
}
std::unique_ptr<Module> llvm::parseAndVerify(const uint8_t *Data, size_t Size,
LLVMContext &Context) {
auto M = parseModule(Data, Size, Context);
if (!M || verifyModule(*M, &errs()))
return nullptr;
return M;
}

View File

@ -9,6 +9,8 @@
#include "llvm/FuzzMutate/IRMutator.h"
#include "llvm/ADT/Optional.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/FuzzMutate/Operations.h"
#include "llvm/FuzzMutate/Random.h"
#include "llvm/FuzzMutate/RandomIRBuilder.h"
@ -17,6 +19,9 @@
#include "llvm/IR/InstIterator.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Transforms/Scalar/DCE.h"
using namespace llvm;
@ -243,3 +248,44 @@ void InstModificationIRStrategy::mutate(Instruction &Inst,
if (RS)
RS.getSelection()();
}
std::unique_ptr<Module> llvm::parseModule(const uint8_t *Data, size_t Size,
LLVMContext &Context) {
if (Size <= 1)
// We get bogus data given an empty corpus - just create a new module.
return std::make_unique<Module>("M", Context);
auto Buffer = MemoryBuffer::getMemBuffer(
StringRef(reinterpret_cast<const char *>(Data), Size), "Fuzzer input",
/*RequiresNullTerminator=*/false);
SMDiagnostic Err;
auto M = parseBitcodeFile(Buffer->getMemBufferRef(), Context);
if (Error E = M.takeError()) {
errs() << toString(std::move(E)) << "\n";
return nullptr;
}
return std::move(M.get());
}
size_t llvm::writeModule(const Module &M, uint8_t *Dest, size_t MaxSize) {
std::string Buf;
{
raw_string_ostream OS(Buf);
WriteBitcodeToFile(M, OS);
}
if (Buf.size() > MaxSize)
return 0;
memcpy(Dest, Buf.data(), Buf.size());
return Buf.size();
}
std::unique_ptr<Module> llvm::parseAndVerify(const uint8_t *Data, size_t Size,
LLVMContext &Context) {
auto M = parseModule(Data, Size, Context);
if (!M || verifyModule(*M, &errs()))
return nullptr;
return M;
}