[llvm] r313809 - Introduce the llvm-cfi-verify tool (resubmission of D37937).
Vlad Tsyrklevich via llvm-commits
llvm-commits at lists.llvm.org
Wed Sep 20 13:38:15 PDT 2017
Author: vlad.tsyrklevich
Date: Wed Sep 20 13:38:14 2017
New Revision: 313809
URL: http://llvm.org/viewvc/llvm-project?rev=313809&view=rev
Log:
Introduce the llvm-cfi-verify tool (resubmission of D37937).
Summary: Resubmission of D37937. Fixed i386 target building (conversion from std::size_t& to uint64_t& failed). Fixed documentation warning failure about docs/CFIVerify.rst not being in the tree.
Reviewers: vlad.tsyrklevich
Reviewed By: vlad.tsyrklevich
Patch by Mitch Phillips
Subscribers: sbc100, mgorny, pcc, llvm-commits, kcc
Differential Revision: https://reviews.llvm.org/D38089
Added:
llvm/trunk/docs/CFIVerify.rst
llvm/trunk/tools/llvm-cfi-verify/CMakeLists.txt
llvm/trunk/tools/llvm-cfi-verify/LLVMBuild.txt
- copied, changed from r313807, llvm/trunk/tools/LLVMBuild.txt
llvm/trunk/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
Modified:
llvm/trunk/docs/index.rst
llvm/trunk/tools/LLVMBuild.txt
Added: llvm/trunk/docs/CFIVerify.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/CFIVerify.rst?rev=313809&view=auto
==============================================================================
--- llvm/trunk/docs/CFIVerify.rst (added)
+++ llvm/trunk/docs/CFIVerify.rst Wed Sep 20 13:38:14 2017
@@ -0,0 +1,91 @@
+==============================================
+Control Flow Verification Tool Design Document
+==============================================
+
+.. contents::
+ :local:
+
+Objective
+=========
+
+This document provides an overview of an external tool to verify the protection
+mechanisms implemented by Clang's *Control Flow Integrity* (CFI) schemes
+(``-fsanitize=cfi``). This tool, provided a binary or DSO, should infer whether
+indirect control flow operations are protected by CFI, and should output these
+results in a human-readable form.
+
+This tool should also be added as part of Clang's continuous integration testing
+framework, where modifications to the compiler ensure that CFI protection
+schemes are still present in the final binary.
+
+Location
+========
+
+This tool will be present as a part of the LLVM toolchain, and will reside in
+the "/llvm/tools/llvm-cfi-verify" directory, relative to the LLVM trunk. It will
+be tested in two methods:
+
+- Unit tests to validate code sections, present in "/llvm/unittests/llvm-cfi-
+ verify".
+- Integration tests, present in "/llvm/tools/clang/test/LLVMCFIVerify". These
+ integration tests are part of clang as part of a continuous integration
+ framework, ensuring updates to the compiler that reduce CFI coverage on
+ indirect control flow instructions are identified.
+
+Background
+==========
+
+This tool will continuously validate that CFI directives are properly
+implemented around all indirect control flows by analysing the output machine
+code. The analysis of machine code is important as it ensures that any bugs
+present in linker or compiler do not subvert CFI protections in the final
+shipped binary.
+
+Unprotected indirect control flow instructions will be flagged for manual
+review. These unexpected control flows may simply have not been accounted for in
+the compiler implementation of CFI (e.g. indirect jumps to facilitate switch
+statements may not be fully protected).
+
+It may be possible in the future to extend this tool to flag unnecessary CFI
+directives (e.g. CFI directives around a static call to a non-polymorphic base
+type). This type of directive has no security implications, but may present
+performance impacts.
+
+Design Ideas
+============
+
+This tool will disassemble binaries and DSO's from their machine code format and
+analyse the disassembled machine code. The tool will inspect virtual calls and
+indirect function calls. This tool will also inspect indirect jumps, as inlined
+functions and jump tables should also be subject to CFI protections. Non-virtual
+calls (``-fsanitize=cfi-nvcall``) and cast checks (``-fsanitize=cfi-*cast*``)
+are not implemented due to a lack of information provided by the bytecode.
+
+The tool would operate by searching for indirect control flow instructions in
+the disassembly. A control flow graph would be generated from a small buffer of
+the instructions surrounding the 'target' control flow instruction. If the
+target instruction is branched-to, the fallthrough of the branch should be the
+CFI trap (on x86, this is a ``ud2`` instruction). If the target instruction is
+the fallthrough (i.e. immediately succeeds) of a conditional jump, the
+conditional jump target should be the CFI trap. If an indirect control flow
+instruction does not conform to one of these formats, the target will be noted
+as being CFI-unprotected.
+
+Note that in the second case outlined above (where the target instruction is the
+fallthrough of a conditional jump), if the target represents a vcall that takes
+arguments, these arguments may be pushed to the stack after the branch but
+before the target instruction. In these cases, a secondary 'spill graph' in
+constructed, to ensure the register argument used by the indirect jump/call is
+not spilled from the stack at any point in the interim period. If there are no
+spills that affect the target register, the target is marked as CFI-protected.
+
+Other Design Notes
+~~~~~~~~~~~~~~~~~~
+
+Only machine code sections that are marked as executable will be subject to this
+analysis. Non-executable sections do not require analysis as any execution
+present in these sections has already violated the control flow integrity.
+
+Suitable extensions may be made at a later date to include anaylsis for indirect
+control flow operations across DSO boundaries. Currently, these CFI features are
+only experimental with an unstable ABI, making them unsuitable for analysis.
Modified: llvm/trunk/docs/index.rst
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/docs/index.rst?rev=313809&r1=313808&r2=313809&view=diff
==============================================================================
--- llvm/trunk/docs/index.rst (original)
+++ llvm/trunk/docs/index.rst Wed Sep 20 13:38:14 2017
@@ -159,7 +159,7 @@ representation.
misunderstood instruction.
:doc:`Frontend/PerformanceTips`
- A collection of tips for frontend authors on how to generate IR
+ A collection of tips for frontend authors on how to generate IR
which LLVM is able to effectively optimize.
:doc:`Docker`
@@ -281,6 +281,7 @@ For API clients and LLVM developers.
XRayExample
XRayFDRFormat
PDB/index
+ CFIVerify
:doc:`WritingAnLLVMPass`
Information on how to write LLVM transformations and analyses.
@@ -411,6 +412,9 @@ For API clients and LLVM developers.
:doc:`The Microsoft PDB File Format <PDB/index>`
A detailed description of the Microsoft PDB (Program Database) file format.
+:doc:`CFIVerify`
+ A description of the verification tool for Control Flow Integrity.
+
Development Process Documentation
=================================
Modified: llvm/trunk/tools/LLVMBuild.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/LLVMBuild.txt?rev=313809&r1=313808&r2=313809&view=diff
==============================================================================
--- llvm/trunk/tools/LLVMBuild.txt (original)
+++ llvm/trunk/tools/LLVMBuild.txt Wed Sep 20 13:38:14 2017
@@ -25,6 +25,7 @@ subdirectories =
llvm-as
llvm-bcanalyzer
llvm-cat
+ llvm-cfi-verify
llvm-cov
llvm-cvtres
llvm-diff
Added: llvm/trunk/tools/llvm-cfi-verify/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cfi-verify/CMakeLists.txt?rev=313809&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-cfi-verify/CMakeLists.txt (added)
+++ llvm/trunk/tools/llvm-cfi-verify/CMakeLists.txt Wed Sep 20 13:38:14 2017
@@ -0,0 +1,15 @@
+set(LLVM_LINK_COMPONENTS
+ AllTargetsAsmPrinters
+ AllTargetsAsmParsers
+ AllTargetsDescs
+ AllTargetsDisassemblers
+ AllTargetsInfos
+ MC
+ MCParser
+ Object
+ Support
+ )
+
+add_llvm_tool(llvm-cfi-verify
+ llvm-cfi-verify.cpp
+ )
Copied: llvm/trunk/tools/llvm-cfi-verify/LLVMBuild.txt (from r313807, llvm/trunk/tools/LLVMBuild.txt)
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cfi-verify/LLVMBuild.txt?p2=llvm/trunk/tools/llvm-cfi-verify/LLVMBuild.txt&p1=llvm/trunk/tools/LLVMBuild.txt&r1=313807&r2=313809&rev=313809&view=diff
==============================================================================
--- llvm/trunk/tools/LLVMBuild.txt (original)
+++ llvm/trunk/tools/llvm-cfi-verify/LLVMBuild.txt Wed Sep 20 13:38:14 2017
@@ -1,4 +1,4 @@
-;===- ./tools/LLVMBuild.txt ------------------------------------*- Conf -*--===;
+;===- ./tools/llvm-cfi-verify/LLVMBuild.txt --------------------*- Conf -*--===;
;
; The LLVM Compiler Infrastructure
;
@@ -15,43 +15,8 @@
;
;===------------------------------------------------------------------------===;
-[common]
-subdirectories =
- bugpoint
- dsymutil
- llc
- lli
- llvm-ar
- llvm-as
- llvm-bcanalyzer
- llvm-cat
- llvm-cov
- llvm-cvtres
- llvm-diff
- llvm-dis
- llvm-dwarfdump
- llvm-dwp
- llvm-extract
- llvm-jitlistener
- llvm-link
- llvm-lto
- llvm-mc
- llvm-mcmarkup
- llvm-modextract
- llvm-mt
- llvm-nm
- llvm-objcopy
- llvm-objdump
- llvm-pdbutil
- llvm-profdata
- llvm-rc
- llvm-rtdyld
- llvm-size
- llvm-split
- opt
- verify-uselistorder
-
[component_0]
-type = Group
-name = Tools
-parent = $ROOT
+type = Tool
+name = llvm-cfi-verify
+parent = Tools
+required_libraries = MC MCDisassembler MCParser Support all-targets
Added: llvm/trunk/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-cfi-verify/llvm-cfi-verify.cpp?rev=313809&view=auto
==============================================================================
--- llvm/trunk/tools/llvm-cfi-verify/llvm-cfi-verify.cpp (added)
+++ llvm/trunk/tools/llvm-cfi-verify/llvm-cfi-verify.cpp Wed Sep 20 13:38:14 2017
@@ -0,0 +1,241 @@
+//===-- llvm-cfi-verify.cpp - CFI Verification tool for LLVM --------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This tool verifies Control Flow Integrity (CFI) instrumentation by static
+// binary anaylsis. See the design document in /docs/CFIVerify.rst for more
+// information.
+//
+// This tool is currently incomplete. It currently only does disassembly for
+// object files, and searches through the code for indirect control flow
+// instructions, printing them once found.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCDisassembler/MCDisassembler.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrDesc.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Object/Binary.h"
+#include "llvm/Object/COFF.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <cassert>
+#include <cstdlib>
+
+using namespace llvm;
+using namespace llvm::object;
+
+cl::opt<bool> ArgDumpSymbols("sym", cl::desc("Dump the symbol table."));
+cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"),
+ cl::Required);
+
+static void printSymbols(const ObjectFile *Object) {
+ for (const SymbolRef &Symbol : Object->symbols()) {
+ outs() << "Symbol [" << format_hex_no_prefix(Symbol.getValue(), 2)
+ << "] = ";
+
+ auto SymbolName = Symbol.getName();
+ if (SymbolName)
+ outs() << *SymbolName;
+ else
+ outs() << "UNKNOWN";
+
+ if (Symbol.getFlags() & SymbolRef::SF_Hidden)
+ outs() << " .hidden";
+
+ outs() << " (Section = ";
+
+ auto SymbolSection = Symbol.getSection();
+ if (SymbolSection) {
+ StringRef SymbolSectionName;
+ if ((*SymbolSection)->getName(SymbolSectionName))
+ outs() << "UNKNOWN)";
+ else
+ outs() << SymbolSectionName << ")";
+ } else {
+ outs() << "N/A)";
+ }
+
+ outs() << "\n";
+ }
+}
+
+int main(int argc, char **argv) {
+ cl::ParseCommandLineOptions(argc, argv);
+
+ InitializeAllTargetInfos();
+ InitializeAllTargetMCs();
+ InitializeAllAsmParsers();
+ InitializeAllDisassemblers();
+
+ Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(InputFilename);
+ if (!BinaryOrErr) {
+ errs() << "Failed to open file.\n";
+ return EXIT_FAILURE;
+ }
+
+ Binary &Binary = *BinaryOrErr.get().getBinary();
+ ObjectFile *Object = dyn_cast<ObjectFile>(&Binary);
+ if (!Object) {
+ errs() << "Disassembling of non-objects not currently supported.\n";
+ return EXIT_FAILURE;
+ }
+
+ Triple TheTriple = Object->makeTriple();
+ std::string TripleName = TheTriple.getTriple();
+ std::string ArchName = "";
+ std::string ErrorString;
+
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(ArchName, TheTriple, ErrorString);
+
+ if (!TheTarget) {
+ errs() << "Couldn't find target \"" << TheTriple.getTriple()
+ << "\", failed with error: " << ErrorString << ".\n";
+ return EXIT_FAILURE;
+ }
+
+ SubtargetFeatures Features = Object->getFeatures();
+
+ std::unique_ptr<const MCRegisterInfo> RegisterInfo(
+ TheTarget->createMCRegInfo(TripleName));
+ if (!RegisterInfo) {
+ errs() << "Failed to initialise RegisterInfo.\n";
+ return EXIT_FAILURE;
+ }
+
+ std::unique_ptr<const MCAsmInfo> AsmInfo(
+ TheTarget->createMCAsmInfo(*RegisterInfo, TripleName));
+ if (!AsmInfo) {
+ errs() << "Failed to initialise AsmInfo.\n";
+ return EXIT_FAILURE;
+ }
+
+ std::string MCPU = "";
+ std::unique_ptr<MCSubtargetInfo> SubtargetInfo(
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, Features.getString()));
+ if (!SubtargetInfo) {
+ errs() << "Failed to initialise SubtargetInfo.\n";
+ return EXIT_FAILURE;
+ }
+
+ std::unique_ptr<const MCInstrInfo> MII(TheTarget->createMCInstrInfo());
+ if (!MII) {
+ errs() << "Failed to initialise MII.\n";
+ return EXIT_FAILURE;
+ }
+
+ MCObjectFileInfo MOFI;
+ MCContext Context(AsmInfo.get(), RegisterInfo.get(), &MOFI);
+
+ std::unique_ptr<const MCDisassembler> Disassembler(
+ TheTarget->createMCDisassembler(*SubtargetInfo, Context));
+
+ if (!Disassembler) {
+ errs() << "No disassembler available for target.";
+ return EXIT_FAILURE;
+ }
+
+ std::unique_ptr<const MCInstrAnalysis> MIA(
+ TheTarget->createMCInstrAnalysis(MII.get()));
+
+ std::unique_ptr<MCInstPrinter> Printer(
+ TheTarget->createMCInstPrinter(TheTriple, AsmInfo->getAssemblerDialect(),
+ *AsmInfo, *MII, *RegisterInfo));
+
+ if (ArgDumpSymbols)
+ printSymbols(Object);
+
+ for (const SectionRef &Section : Object->sections()) {
+ outs() << "Section [" << format_hex_no_prefix(Section.getAddress(), 2)
+ << "] = ";
+ StringRef SectionName;
+
+ if (Section.getName(SectionName))
+ outs() << "UNKNOWN.\n";
+ else
+ outs() << SectionName << "\n";
+
+ StringRef SectionContents;
+ if (Section.getContents(SectionContents)) {
+ errs() << "Failed to retrieve section contents.\n";
+ return EXIT_FAILURE;
+ }
+
+ MCInst Instruction;
+ uint64_t InstructionSize;
+
+ ArrayRef<uint8_t> SectionBytes((const uint8_t *)SectionContents.data(),
+ Section.getSize());
+
+ for (uint64_t Byte = 0; Byte < Section.getSize();) {
+ bool BadInstruction = false;
+
+ // Disassemble the instruction.
+ if (Disassembler->getInstruction(
+ Instruction, InstructionSize, SectionBytes.drop_front(Byte), 0,
+ nulls(), outs()) != MCDisassembler::Success) {
+ BadInstruction = true;
+ }
+
+ Byte += InstructionSize;
+
+ if (BadInstruction)
+ continue;
+
+ // Skip instructions that do not affect the control flow.
+ const auto &InstrDesc = MII->get(Instruction.getOpcode());
+ if (!InstrDesc.mayAffectControlFlow(Instruction, *RegisterInfo))
+ continue;
+
+ // Skip instructions that do not operate on register operands.
+ bool UsesRegisterOperand = false;
+ for (const auto &Operand : Instruction) {
+ if (Operand.isReg())
+ UsesRegisterOperand = true;
+ }
+
+ if (!UsesRegisterOperand)
+ continue;
+
+ // Print the instruction address.
+ outs() << " "
+ << format_hex(Section.getAddress() + Byte - InstructionSize, 2)
+ << ": ";
+
+ // Print the instruction bytes.
+ for (uint64_t i = 0; i < InstructionSize; ++i) {
+ outs() << format_hex_no_prefix(SectionBytes[Byte - InstructionSize + i],
+ 2)
+ << " ";
+ }
+
+ // Print the instruction.
+ outs() << " | " << MII->getName(Instruction.getOpcode()) << " ";
+ Instruction.dump_pretty(outs(), Printer.get());
+
+ outs() << "\n";
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
More information about the llvm-commits
mailing list