diff --git a/llvm/tools/llvm-cfi-verify-fuzzer/CMakeLists.txt b/llvm/tools/llvm-cfi-verify-fuzzer/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-cfi-verify-fuzzer/CMakeLists.txt @@ -0,0 +1,16 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsAsmPrinters + AllTargetsAsmParsers + AllTargetsDescs + AllTargetsDisassemblers + AllTargetsInfos + Support + FuzzMutate +) + +add_llvm_fuzzer(llvm-cfi-verify-fuzzer + cfi-verify-fuzzer.cpp + DUMMY_MAIN DummyCFIVerifyFuzzer.cpp +) + +target_link_libraries(llvm-cfi-verify-fuzzer PRIVATE LLVMCFIVerify) diff --git a/llvm/tools/llvm-cfi-verify-fuzzer/DummyCFIVerifyFuzzer.cpp b/llvm/tools/llvm-cfi-verify-fuzzer/DummyCFIVerifyFuzzer.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-cfi-verify-fuzzer/DummyCFIVerifyFuzzer.cpp @@ -0,0 +1,18 @@ +//===--- DummyCFIVerifyFuzzer.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 +// +//===----------------------------------------------------------------------===// +// +// Implementation of main so we can build and test without linking libFuzzer. +// +//===----------------------------------------------------------------------===// + +#include "llvm/FuzzMutate/FuzzerCLI.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int main(int argc, char *argv[]) { + return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput); +} diff --git a/llvm/tools/llvm-cfi-verify-fuzzer/cfi-verify-fuzzer.cpp b/llvm/tools/llvm-cfi-verify-fuzzer/cfi-verify-fuzzer.cpp new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-cfi-verify-fuzzer/cfi-verify-fuzzer.cpp @@ -0,0 +1,89 @@ +//===--- cfi-verify-fuzzer.cpp - Fuzzer for llvm's cfi-verify tool --------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "../tools/llvm-cfi-verify/lib/FileAnalysis.h" +#include "../tools/llvm-cfi-verify/lib/GraphBuilder.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#include + +namespace llvm { +namespace cfi_verify_fuzzer { + +class FileAnalysisStub : public llvm::cfi_verify::FileAnalysis { +public: + FileAnalysisStub(StringRef Trip) + : FileAnalysis(Triple(Trip), SubtargetFeatures()) {} + + // Expose this method publicly for fuzzing. + void parseSectionContents(ArrayRef SectionBytes, + uint64_t SectionAddress) { + FileAnalysis::parseSectionContents(SectionBytes, SectionAddress); + } + + Error initialiseDisassemblyMembers() { + return FileAnalysis::initialiseDisassemblyMembers(); + } +}; + +} // namespace cfi_verify_fuzzer +} // namespace llvm + +bool DoInitialisation() { + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllDisassemblers(); + + llvm::cfi_verify::IgnoreDWARFFlag = true; + llvm::cfi_verify::QuietFlag = true; + return true; +} + +using llvm::cfi_verify::GraphBuilder; +using llvm::cfi_verify::GraphResult; +using llvm::cfi_verify::UnsupportedDisassembly; +using llvm::cfi_verify_fuzzer::FileAnalysisStub; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + static bool Init = DoInitialisation(); + (void)(Init); // Suppress unused-variable warning. + + if (Size < sizeof(uint64_t)) + return 0; + + FileAnalysisStub Stub("x86_64--"); + + if (auto Err = Stub.initialiseDisassemblyMembers()) { + handleAllErrors(std::move(Err), [&](const UnsupportedDisassembly &E) { + printf("Note: CFI-verify-fuzzer is disabled due to lack of architecture " + "support on this build.\n"); + exit(EXIT_SUCCESS); + }); + } + + // Parse the "object file", generated by the fuzzer. The offset of this + // section is provided by the first four bytes of the fuzzer. + Stub.parseSectionContents( + llvm::ArrayRef(Data + sizeof(uint64_t), Size - sizeof(uint64_t)), + *reinterpret_cast(Data)); + + // Now extract all the indirect CFs and run normal cross-analysis, like the + // cfi-verify tool does. + for (uint64_t Address : Stub.getIndirectInstructions()) { + Stub.getInstructionOrDie(Address); + GraphResult Graph = GraphBuilder::buildFlowGraph(Stub, Address); + + Stub.validateCFIProtection(Graph); + } + + return 0; +} diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h --- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h +++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h @@ -47,6 +47,7 @@ struct GraphResult; extern bool IgnoreDWARFFlag; +extern bool QuietFlag; enum class CFIProtectionStatus { // This instruction is protected by CFI. diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp --- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -42,6 +42,7 @@ namespace cfi_verify { bool IgnoreDWARFFlag; +bool QuietFlag; static cl::opt IgnoreDWARFArg( "ignore-dwarf", @@ -51,6 +52,16 @@ "will result in false positives for 'CFI unprotected' instructions."), cl::location(IgnoreDWARFFlag), cl::init(false)); +static cl::opt + QuietArg("q", + cl::desc("Same as '--quiet'. Silence all errors when parsing the " + "object file. Does not silence " + "result output."), + cl::location(QuietFlag), cl::init(false)); + +static cl::alias QuietLong("quiet", cl::desc("Alias for -q."), + cl::aliasopt(QuietArg)); + StringRef stringCFIProtectionStatus(CFIProtectionStatus Status) { switch (Status) { case CFIProtectionStatus::PROTECTED: @@ -241,10 +252,12 @@ for (uint64_t SourceInstrAddress : TargetRefsKV->second) { const auto &SourceInstrKV = Instructions.find(SourceInstrAddress); if (SourceInstrKV == Instructions.end()) { - errs() << "Failed to find source instruction at address " - << format_hex(SourceInstrAddress, 2) - << " for the cross-reference to instruction at address " - << format_hex(InstrMeta.VMAddress, 2) << ".\n"; + if (!QuietFlag) { + errs() << "Failed to find source instruction at address " + << format_hex(SourceInstrAddress, 2) + << " for the cross-reference to instruction at address " + << format_hex(InstrMeta.VMAddress, 2) << ".\n"; + } continue; } @@ -513,7 +526,8 @@ Symbolizer->symbolizeCode(Object->getFileName(), VMAddress); if (!LineInfo) { handleAllErrors(LineInfo.takeError(), [](const ErrorInfoBase &E) { - errs() << "Symbolizer failed to get line: " << E.message() << "\n"; + if (!QuietFlag) + errs() << "Symbolizer failed to get line: " << E.message() << "\n"; }); continue; } @@ -530,9 +544,11 @@ const auto &KV = Instructions.insert(std::make_pair(Instruction.VMAddress, Instruction)); if (!KV.second) { - errs() << "Failed to add instruction at address " - << format_hex(Instruction.VMAddress, 2) - << ": Instruction at this address already exists.\n"; + if (!QuietFlag) { + errs() << "Failed to add instruction at address " + << format_hex(Instruction.VMAddress, 2) + << ": Instruction at this address already exists.\n"; + } exit(EXIT_FAILURE); } } diff --git a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp --- a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp +++ b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp @@ -124,8 +124,10 @@ // We know the target of the branch, find the fallthrough. NextMetaPtr = Analysis.getNextInstructionSequential(BranchInstrMeta); if (!NextMetaPtr) { - errs() << "Failed to get next instruction from " - << format_hex(BranchNode.Address, 2) << ".\n"; + if (!QuietFlag) { + errs() << "Failed to get next instruction from " + << format_hex(BranchNode.Address, 2) << ".\n"; + } return; } @@ -138,16 +140,21 @@ if (!Analysis.getMCInstrAnalysis()->evaluateBranch( BranchInstrMeta.Instruction, BranchInstrMeta.VMAddress, BranchInstrMeta.InstructionSize, Target)) { - errs() << "Failed to get branch target for conditional branch at address " - << format_hex(BranchInstrMeta.VMAddress, 2) << ".\n"; + if (!QuietFlag) { + errs() + << "Failed to get branch target for conditional branch at address " + << format_hex(BranchInstrMeta.VMAddress, 2) << ".\n"; + } return; } // Resolve the meta pointer for the target of this branch. NextMetaPtr = Analysis.getInstruction(Target); if (!NextMetaPtr) { - errs() << "Failed to find instruction at address " - << format_hex(Target, 2) << ".\n"; + if (!QuietFlag) { + errs() << "Failed to find instruction at address " + << format_hex(Target, 2) << ".\n"; + } return; } @@ -155,8 +162,10 @@ BranchNode.Target = NextMetaPtr->VMAddress; // Add the new node to the branch head. } else { - errs() << "ControlBranchNode supplied to buildFlowsToUndefined should " - "provide Target xor Fallthrough.\n"; + if (!QuietFlag) { + errs() << "ControlBranchNode supplied to buildFlowsToUndefined should " + "provide Target xor Fallthrough.\n"; + } return; } @@ -213,8 +222,10 @@ // Get the metadata for the node instruction. const auto &InstrMetaPtr = Analysis.getInstruction(Address); if (!InstrMetaPtr) { - errs() << "Failed to build flow graph for instruction at address " - << format_hex(Address, 2) << ".\n"; + if (!QuietFlag) { + errs() << "Failed to build flow graph for instruction at address " + << format_hex(Address, 2) << ".\n"; + } Result.OrphanedNodes.push_back(Address); return; } @@ -255,8 +266,10 @@ if (!Analysis.getMCInstrAnalysis()->evaluateBranch( ParentMeta.Instruction, ParentMeta.VMAddress, ParentMeta.InstructionSize, BranchTarget)) { - errs() << "Failed to evaluate branch target for instruction at address " - << format_hex(ParentMeta.VMAddress, 2) << ".\n"; + if (!QuietFlag) { + errs() << "Failed to evaluate branch target for instruction at address " + << format_hex(ParentMeta.VMAddress, 2) << ".\n"; + } Result.IntermediateNodes[ParentMeta.VMAddress] = Address; Result.OrphanedNodes.push_back(ParentMeta.VMAddress); continue; @@ -266,10 +279,12 @@ if (ParentDesc.isUnconditionalBranch()) { // Ensures that the unconditional branch is actually an XRef to the child. if (BranchTarget != Address) { - errs() << "Control flow to " << format_hex(Address, 2) - << ", but target resolution of " - << format_hex(ParentMeta.VMAddress, 2) - << " is not this address?\n"; + if (!QuietFlag) { + errs() << "Control flow to " << format_hex(Address, 2) + << ", but target resolution of " + << format_hex(ParentMeta.VMAddress, 2) + << " is not this address?\n"; + } Result.IntermediateNodes[ParentMeta.VMAddress] = Address; Result.OrphanedNodes.push_back(ParentMeta.VMAddress); continue; @@ -284,8 +299,10 @@ // Ensure that any unknown CFs are caught. if (!ParentDesc.isConditionalBranch()) { - errs() << "Unknown control flow encountered when building graph at " - << format_hex(Address, 2) << "\n."; + if (!QuietFlag) { + errs() << "Unknown control flow encountered when building graph at " + << format_hex(Address, 2) << "\n."; + } Result.IntermediateNodes[ParentMeta.VMAddress] = Address; Result.OrphanedNodes.push_back(ParentMeta.VMAddress); continue;