Parse DWARF information to reduce false positives.
Summary: Help differentiate code and data by parsing DWARF information. This will reduce false positive rates where data is placed in executable sections and is mistakenly parsed as code, resulting in an inflation in the number of indirect CF instructions (and hence an inflation of the number of unprotected).
Also prints the DWARF line data around the region of each indirect CF instruction.
Reviewers: pcc
Subscribers: probinson, llvm-commits, vlad.tsyrklevich, mgorny, aprantl, kcc
Differential Revision: https://reviews.llvm.org/D38654
llvm-svn: 317050
diff --git a/llvm/tools/llvm-cfi-verify/CMakeLists.txt b/llvm/tools/llvm-cfi-verify/CMakeLists.txt
index 8dc8cb9..07c6504 100644
--- a/llvm/tools/llvm-cfi-verify/CMakeLists.txt
+++ b/llvm/tools/llvm-cfi-verify/CMakeLists.txt
@@ -4,6 +4,7 @@
AllTargetsDescs
AllTargetsDisassemblers
AllTargetsInfos
+ DebugInfoDWARF
MC
MCParser
Object
diff --git a/llvm/tools/llvm-cfi-verify/LLVMBuild.txt b/llvm/tools/llvm-cfi-verify/LLVMBuild.txt
index 9bff3a8..5c4ce26 100644
--- a/llvm/tools/llvm-cfi-verify/LLVMBuild.txt
+++ b/llvm/tools/llvm-cfi-verify/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Tool
name = llvm-cfi-verify
parent = Tools
-required_libraries = all-targets MC MCDisassembler MCParser Support
+required_libraries = all-targets DebugInfoDWARF MC MCDisassembler MCParser Support
diff --git a/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt b/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt
index 8cbbc79c..c90e4ed 100644
--- a/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt
+++ b/llvm/tools/llvm-cfi-verify/lib/CMakeLists.txt
@@ -7,6 +7,7 @@
llvm_update_compile_flags(LLVMCFIVerify)
llvm_map_components_to_libnames(libs
+ DebugInfoDWARF
MC
MCParser
Object
diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
index 928571b..278e861 100644
--- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
+++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.cpp
@@ -11,6 +11,7 @@
#include "GraphBuilder.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
@@ -42,6 +43,19 @@
namespace llvm {
namespace cfi_verify {
+static cl::opt<bool> IgnoreDWARF(
+ "ignore-dwarf",
+ cl::desc(
+ "Ignore all DWARF data. This relaxes the requirements for all "
+ "statically linked libraries to have been compiled with '-g', but "
+ "will result in false positives for 'CFI unprotected' instructions."),
+ cl::init(false));
+
+cl::opt<unsigned long long> DWARFSearchRange(
+ "dwarf-search-range",
+ cl::desc("Address search range used to determine if instruction is valid."),
+ cl::init(0x10));
+
Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {
// Open the filename provided.
Expected<object::OwningBinary<object::Binary>> BinaryOrErr =
@@ -294,6 +308,28 @@
}
Error FileAnalysis::parseCodeSections() {
+ if (!IgnoreDWARF) {
+ DWARF.reset(DWARFContext::create(*Object).release());
+ if (!DWARF)
+ return make_error<StringError>("Could not create DWARF information.",
+ inconvertibleErrorCode());
+
+ bool LineInfoValid = false;
+
+ for (auto &Unit : DWARF->compile_units()) {
+ const auto &LineTable = DWARF->getLineTableForUnit(Unit.get());
+ if (LineTable && !LineTable->Rows.empty()) {
+ LineInfoValid = true;
+ break;
+ }
+ }
+
+ if (!LineInfoValid)
+ return make_error<StringError>(
+ "DWARF line information missing. Did you compile with '-g'?",
+ inconvertibleErrorCode());
+ }
+
for (const object::SectionRef &Section : Object->sections()) {
// Ensure only executable sections get analysed.
if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))
@@ -311,6 +347,19 @@
return Error::success();
}
+DILineInfoTable FileAnalysis::getLineInfoForAddressRange(uint64_t Address) {
+ if (!hasLineTableInfo())
+ return DILineInfoTable();
+
+ return DWARF->getLineInfoForAddressRange(Address, DWARFSearchRange);
+}
+
+bool FileAnalysis::hasValidLineInfoForAddressRange(uint64_t Address) {
+ return !getLineInfoForAddressRange(Address).empty();
+}
+
+bool FileAnalysis::hasLineTableInfo() const { return DWARF != nullptr; }
+
void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes,
uint64_t SectionAddress) {
MCInst Instruction;
@@ -330,6 +379,11 @@
InstrMeta.VMAddress = VMAddress;
InstrMeta.InstructionSize = InstructionSize;
InstrMeta.Valid = ValidInstruction;
+
+ // Check if this instruction exists in the range of the DWARF metadata.
+ if (hasLineTableInfo() && !hasValidLineInfoForAddressRange(VMAddress))
+ continue;
+
addInstruction(InstrMeta);
if (!ValidInstruction)
diff --git a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
index 1ed575b..9945a21 100644
--- a/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
+++ b/llvm/tools/llvm-cfi-verify/lib/FileAnalysis.h
@@ -12,6 +12,7 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
@@ -120,6 +121,18 @@
const MCInstrInfo *getMCInstrInfo() const;
const MCInstrAnalysis *getMCInstrAnalysis() const;
+ // Returns true if this class is using DWARF line tables for elimination.
+ bool hasLineTableInfo() const;
+
+ // Returns the line table information for the range {Address +-
+ // DWARFSearchRange}. Returns an empty table if the address has no valid line
+ // table information, or this analysis object has DWARF handling disabled.
+ DILineInfoTable getLineInfoForAddressRange(uint64_t Address);
+
+ // Returns whether the provided address has valid line information for
+ // instructions in the range of Address +- DWARFSearchRange.
+ bool hasValidLineInfoForAddressRange(uint64_t Address);
+
protected:
// Construct a blank object with the provided triple and features. Used in
// testing, where a sub class will dependency inject protected methods to
@@ -162,8 +175,12 @@
std::unique_ptr<const MCInstrAnalysis> MIA;
std::unique_ptr<MCInstPrinter> Printer;
+ // DWARF debug information.
+ std::unique_ptr<DWARFContext> DWARF;
+
// A mapping between the virtual memory address to the instruction metadata
- // struct.
+ // struct. TODO(hctim): Reimplement this as a sorted vector to avoid per-
+ // insertion allocation.
std::map<uint64_t, Instr> Instructions;
// Contains a mapping between a specific address, and a list of instructions
diff --git a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
index 1121045..808b992 100644
--- a/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
+++ b/llvm/tools/llvm-cfi-verify/lib/GraphBuilder.cpp
@@ -221,6 +221,13 @@
continue;
}
+ // Call instructions are not valid in the upwards traversal.
+ if (ParentDesc.isCall()) {
+ Result.IntermediateNodes[ParentMeta.VMAddress] = Address;
+ Result.OrphanedNodes.push_back(ParentMeta.VMAddress);
+ continue;
+ }
+
// Evaluate the branch target to ascertain whether this XRef is the result
// of a fallthrough or the target of a branch.
uint64_t BranchTarget;
diff --git a/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt b/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt
index 39537f5..99b678f 100644
--- a/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt
+++ b/llvm/tools/llvm-cfi-verify/lib/LLVMBuild.txt
@@ -19,4 +19,4 @@
type = Library
name = CFIVerify
parent = Libraries
-required_libraries = MC MCDisassembler MCParser Support
+required_libraries = DebugInfoDWARF MC MCDisassembler MCParser Support
diff --git a/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp b/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
index 00324ed..d4a46fc 100644
--- a/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
+++ b/llvm/tools/llvm-cfi-verify/llvm-cfi-verify.cpp
@@ -22,6 +22,7 @@
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
+#include "llvm/Support/FormatVariadic.h"
#include <cstdlib>
@@ -34,30 +35,62 @@
ExitOnError ExitOnErr;
-void printIndirectCFInstructions(const FileAnalysis &Verifier) {
- for (uint64_t Address : Verifier.getIndirectInstructions()) {
- const auto &InstrMeta = Verifier.getInstructionOrDie(Address);
- outs() << format_hex(Address, 2) << " |"
- << Verifier.getMCInstrInfo()->getName(
+void printIndirectCFInstructions(FileAnalysis &Analysis) {
+ uint64_t ProtectedCount = 0;
+ uint64_t UnprotectedCount = 0;
+
+ for (uint64_t Address : Analysis.getIndirectInstructions()) {
+ const auto &InstrMeta = Analysis.getInstructionOrDie(Address);
+
+ if (Analysis.isIndirectInstructionCFIProtected(Address)) {
+ outs() << "P ";
+ ProtectedCount++;
+ } else {
+ outs() << "U ";
+ UnprotectedCount++;
+ }
+
+ outs() << format_hex(Address, 2) << " | "
+ << Analysis.getMCInstrInfo()->getName(
InstrMeta.Instruction.getOpcode())
<< " ";
- InstrMeta.Instruction.print(outs());
outs() << "\n";
- outs() << " Protected? "
- << Verifier.isIndirectInstructionCFIProtected(Address) << "\n";
+
+ if (Analysis.hasLineTableInfo()) {
+ for (const auto &LineKV : Analysis.getLineInfoForAddressRange(Address)) {
+ outs() << " " << format_hex(LineKV.first, 2) << " = "
+ << LineKV.second.FileName << ":" << LineKV.second.Line << ":"
+ << LineKV.second.Column << " (" << LineKV.second.FunctionName
+ << ")\n";
+ }
+ }
}
+
+ if (ProtectedCount || UnprotectedCount)
+ outs() << formatv(
+ "Unprotected: {0} ({1:P}), Protected: {2} ({3:P})\n", UnprotectedCount,
+ (((double)UnprotectedCount) / (UnprotectedCount + ProtectedCount)),
+ ProtectedCount,
+ (((double)ProtectedCount) / (UnprotectedCount + ProtectedCount)));
+ else
+ outs() << "No indirect CF instructions found.\n";
}
int main(int argc, char **argv) {
- cl::ParseCommandLineOptions(argc, argv);
+ cl::ParseCommandLineOptions(
+ argc, argv,
+ "Identifies whether Control Flow Integrity protects all indirect control "
+ "flow instructions in the provided object file, DSO or binary.\nNote: "
+ "Anything statically linked into the provided file *must* be compiled "
+ "with '-g'. This can be relaxed through the '--ignore-dwarf' flag.");
InitializeAllTargetInfos();
InitializeAllTargetMCs();
InitializeAllAsmParsers();
InitializeAllDisassemblers();
- FileAnalysis Verifier = ExitOnErr(FileAnalysis::Create(InputFilename));
- printIndirectCFInstructions(Verifier);
+ FileAnalysis Analysis = ExitOnErr(FileAnalysis::Create(InputFilename));
+ printIndirectCFInstructions(Analysis);
return EXIT_SUCCESS;
}