[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