brintos

brintos / llvm-project-archived public Read only

0
0
Text · 19.8 KiB · 4209e23 Raw
605 lines · cpp
1//===- FileAnalysis.cpp -----------------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "FileAnalysis.h"10#include "GraphBuilder.h"11 12#include "llvm/BinaryFormat/ELF.h"13#include "llvm/DebugInfo/DWARF/DWARFContext.h"14#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"15#include "llvm/MC/MCAsmInfo.h"16#include "llvm/MC/MCContext.h"17#include "llvm/MC/MCDisassembler/MCDisassembler.h"18#include "llvm/MC/MCInst.h"19#include "llvm/MC/MCInstPrinter.h"20#include "llvm/MC/MCInstrAnalysis.h"21#include "llvm/MC/MCInstrDesc.h"22#include "llvm/MC/MCInstrInfo.h"23#include "llvm/MC/MCObjectFileInfo.h"24#include "llvm/MC/MCRegisterInfo.h"25#include "llvm/MC/MCSubtargetInfo.h"26#include "llvm/MC/MCTargetOptions.h"27#include "llvm/MC/TargetRegistry.h"28#include "llvm/Object/Binary.h"29#include "llvm/Object/COFF.h"30#include "llvm/Object/ELFObjectFile.h"31#include "llvm/Object/ObjectFile.h"32#include "llvm/Support/Casting.h"33#include "llvm/Support/CommandLine.h"34#include "llvm/Support/Error.h"35#include "llvm/Support/MemoryBuffer.h"36#include "llvm/Support/TargetSelect.h"37#include "llvm/Support/raw_ostream.h"38 39using Instr = llvm::cfi_verify::FileAnalysis::Instr;40using LLVMSymbolizer = llvm::symbolize::LLVMSymbolizer;41 42namespace llvm {43namespace cfi_verify {44 45bool IgnoreDWARFFlag;46 47static cl::opt<bool, true> IgnoreDWARFArg(48    "ignore-dwarf",49    cl::desc(50        "Ignore all DWARF data. This relaxes the requirements for all "51        "statically linked libraries to have been compiled with '-g', but "52        "will result in false positives for 'CFI unprotected' instructions."),53    cl::location(IgnoreDWARFFlag), cl::init(false));54 55StringRef stringCFIProtectionStatus(CFIProtectionStatus Status) {56  switch (Status) {57  case CFIProtectionStatus::PROTECTED:58    return "PROTECTED";59  case CFIProtectionStatus::FAIL_NOT_INDIRECT_CF:60    return "FAIL_NOT_INDIRECT_CF";61  case CFIProtectionStatus::FAIL_ORPHANS:62    return "FAIL_ORPHANS";63  case CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH:64    return "FAIL_BAD_CONDITIONAL_BRANCH";65  case CFIProtectionStatus::FAIL_REGISTER_CLOBBERED:66    return "FAIL_REGISTER_CLOBBERED";67  case CFIProtectionStatus::FAIL_INVALID_INSTRUCTION:68    return "FAIL_INVALID_INSTRUCTION";69  }70  llvm_unreachable("Attempted to stringify an unknown enum value.");71}72 73Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) {74  // Open the filename provided.75  Expected<object::OwningBinary<object::Binary>> BinaryOrErr =76      object::createBinary(Filename);77  if (!BinaryOrErr)78    return BinaryOrErr.takeError();79 80  // Construct the object and allow it to take ownership of the binary.81  object::OwningBinary<object::Binary> Binary = std::move(BinaryOrErr.get());82  FileAnalysis Analysis(std::move(Binary));83 84  Analysis.Object = dyn_cast<object::ObjectFile>(Analysis.Binary.getBinary());85  if (!Analysis.Object)86    return make_error<UnsupportedDisassembly>("Failed to cast object");87 88  switch (Analysis.Object->getArch()) {89    case Triple::x86:90    case Triple::x86_64:91    case Triple::aarch64:92    case Triple::aarch64_be:93      break;94    default:95      return make_error<UnsupportedDisassembly>("Unsupported architecture.");96  }97 98  Analysis.ObjectTriple = Analysis.Object->makeTriple();99  Expected<SubtargetFeatures> Features = Analysis.Object->getFeatures();100  if (!Features)101    return Features.takeError();102 103  Analysis.Features = *Features;104 105  // Init the rest of the object.106  if (auto InitResponse = Analysis.initialiseDisassemblyMembers())107    return std::move(InitResponse);108 109  if (auto SectionParseResponse = Analysis.parseCodeSections())110    return std::move(SectionParseResponse);111 112  if (auto SymbolTableParseResponse = Analysis.parseSymbolTable())113    return std::move(SymbolTableParseResponse);114 115  return std::move(Analysis);116}117 118FileAnalysis::FileAnalysis(object::OwningBinary<object::Binary> Binary)119    : Binary(std::move(Binary)) {}120 121FileAnalysis::FileAnalysis(const Triple &ObjectTriple,122                           const SubtargetFeatures &Features)123    : ObjectTriple(ObjectTriple), Features(Features) {}124 125const Instr *126FileAnalysis::getPrevInstructionSequential(const Instr &InstrMeta) const {127  std::map<uint64_t, Instr>::const_iterator KV =128      Instructions.find(InstrMeta.VMAddress);129  if (KV == Instructions.end() || KV == Instructions.begin())130    return nullptr;131 132  if (!(--KV)->second.Valid)133    return nullptr;134 135  return &KV->second;136}137 138const Instr *139FileAnalysis::getNextInstructionSequential(const Instr &InstrMeta) const {140  std::map<uint64_t, Instr>::const_iterator KV =141      Instructions.find(InstrMeta.VMAddress);142  if (KV == Instructions.end() || ++KV == Instructions.end())143    return nullptr;144 145  if (!KV->second.Valid)146    return nullptr;147 148  return &KV->second;149}150 151bool FileAnalysis::usesRegisterOperand(const Instr &InstrMeta) const {152  for (const auto &Operand : InstrMeta.Instruction) {153    if (Operand.isReg())154      return true;155  }156  return false;157}158 159const Instr *FileAnalysis::getInstruction(uint64_t Address) const {160  const auto &InstrKV = Instructions.find(Address);161  if (InstrKV == Instructions.end())162    return nullptr;163 164  return &InstrKV->second;165}166 167const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const {168  const auto &InstrKV = Instructions.find(Address);169  assert(InstrKV != Instructions.end() && "Address doesn't exist.");170  return InstrKV->second;171}172 173bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const {174  const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());175  return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta);176}177 178bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const {179  const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());180  if (!InstrDesc.isCall())181    return false;182  uint64_t Target;183  if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,184                           InstrMeta.InstructionSize, Target))185    return false;186  return TrapOnFailFunctionAddresses.contains(Target);187}188 189bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const {190  if (!InstrMeta.Valid)191    return false;192 193  if (isCFITrap(InstrMeta))194    return false;195 196  const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());197  if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo))198    return InstrDesc.isConditionalBranch();199 200  return true;201}202 203const Instr *204FileAnalysis::getDefiniteNextInstruction(const Instr &InstrMeta) const {205  if (!InstrMeta.Valid)206    return nullptr;207 208  if (isCFITrap(InstrMeta))209    return nullptr;210 211  const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode());212  const Instr *NextMetaPtr;213  if (InstrDesc.mayAffectControlFlow(InstrMeta.Instruction, *RegisterInfo)) {214    if (InstrDesc.isConditionalBranch())215      return nullptr;216 217    uint64_t Target;218    if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress,219                             InstrMeta.InstructionSize, Target))220      return nullptr;221 222    NextMetaPtr = getInstruction(Target);223  } else {224    NextMetaPtr =225        getInstruction(InstrMeta.VMAddress + InstrMeta.InstructionSize);226  }227 228  if (!NextMetaPtr || !NextMetaPtr->Valid)229    return nullptr;230 231  return NextMetaPtr;232}233 234std::set<const Instr *>235FileAnalysis::getDirectControlFlowXRefs(const Instr &InstrMeta) const {236  std::set<const Instr *> CFCrossReferences;237  const Instr *PrevInstruction = getPrevInstructionSequential(InstrMeta);238 239  if (PrevInstruction && canFallThrough(*PrevInstruction))240    CFCrossReferences.insert(PrevInstruction);241 242  const auto &TargetRefsKV = StaticBranchTargetings.find(InstrMeta.VMAddress);243  if (TargetRefsKV == StaticBranchTargetings.end())244    return CFCrossReferences;245 246  for (uint64_t SourceInstrAddress : TargetRefsKV->second) {247    const auto &SourceInstrKV = Instructions.find(SourceInstrAddress);248    if (SourceInstrKV == Instructions.end()) {249      errs() << "Failed to find source instruction at address "250             << format_hex(SourceInstrAddress, 2)251             << " for the cross-reference to instruction at address "252             << format_hex(InstrMeta.VMAddress, 2) << ".\n";253      continue;254    }255 256    CFCrossReferences.insert(&SourceInstrKV->second);257  }258 259  return CFCrossReferences;260}261 262const std::set<object::SectionedAddress> &263FileAnalysis::getIndirectInstructions() const {264  return IndirectInstructions;265}266 267const MCRegisterInfo *FileAnalysis::getRegisterInfo() const {268  return RegisterInfo.get();269}270 271const MCInstrInfo *FileAnalysis::getMCInstrInfo() const { return MII.get(); }272 273const MCInstrAnalysis *FileAnalysis::getMCInstrAnalysis() const {274  return MIA.get();275}276 277Expected<DIInliningInfo>278FileAnalysis::symbolizeInlinedCode(object::SectionedAddress Address) {279  assert(Symbolizer != nullptr && "Symbolizer is invalid.");280 281  return Symbolizer->symbolizeInlinedCode(Object->getFileName(), Address);282}283 284CFIProtectionStatus285FileAnalysis::validateCFIProtection(const GraphResult &Graph) const {286  const Instr *InstrMetaPtr = getInstruction(Graph.BaseAddress);287  if (!InstrMetaPtr)288    return CFIProtectionStatus::FAIL_INVALID_INSTRUCTION;289 290  const auto &InstrDesc = MII->get(InstrMetaPtr->Instruction.getOpcode());291  if (!InstrDesc.mayAffectControlFlow(InstrMetaPtr->Instruction, *RegisterInfo))292    return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF;293 294  if (!usesRegisterOperand(*InstrMetaPtr))295    return CFIProtectionStatus::FAIL_NOT_INDIRECT_CF;296 297  if (!Graph.OrphanedNodes.empty())298    return CFIProtectionStatus::FAIL_ORPHANS;299 300  for (const auto &BranchNode : Graph.ConditionalBranchNodes) {301    if (!BranchNode.CFIProtection)302      return CFIProtectionStatus::FAIL_BAD_CONDITIONAL_BRANCH;303  }304 305  if (indirectCFOperandClobber(Graph) != Graph.BaseAddress)306    return CFIProtectionStatus::FAIL_REGISTER_CLOBBERED;307 308  return CFIProtectionStatus::PROTECTED;309}310 311uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const {312  assert(Graph.OrphanedNodes.empty() && "Orphaned nodes should be empty.");313 314  // Get the set of registers we must check to ensure they're not clobbered.315  const Instr &IndirectCF = getInstructionOrDie(Graph.BaseAddress);316  DenseSet<unsigned> RegisterNumbers;317  for (const auto &Operand : IndirectCF.Instruction) {318    if (Operand.isReg())319      RegisterNumbers.insert(Operand.getReg());320  }321  assert(RegisterNumbers.size() && "Zero register operands on indirect CF.");322 323  // Now check all branches to indirect CFs and ensure no clobbering happens.324  for (const auto &Branch : Graph.ConditionalBranchNodes) {325    uint64_t Node;326    if (Branch.IndirectCFIsOnTargetPath)327      Node = Branch.Target;328    else329      Node = Branch.Fallthrough;330 331    // Some architectures (e.g., AArch64) cannot load in an indirect branch, so332    // we allow them one load.333    bool canLoad = !MII->get(IndirectCF.Instruction.getOpcode()).mayLoad();334 335    // We walk backwards from the indirect CF.  It is the last node returned by336    // Graph.flattenAddress, so we skip it since we already handled it.337    DenseSet<unsigned> CurRegisterNumbers = RegisterNumbers;338    std::vector<uint64_t> Nodes = Graph.flattenAddress(Node);339    for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) {340      Node = *I;341      const Instr &NodeInstr = getInstructionOrDie(Node);342      const auto &InstrDesc = MII->get(NodeInstr.Instruction.getOpcode());343 344      for (auto RI = CurRegisterNumbers.begin(), RE = CurRegisterNumbers.end();345           RI != RE; ++RI) {346        unsigned RegNum = *RI;347        if (InstrDesc.hasDefOfPhysReg(NodeInstr.Instruction, RegNum,348                                      *RegisterInfo)) {349          if (!canLoad || !InstrDesc.mayLoad())350            return Node;351          canLoad = false;352          CurRegisterNumbers.erase(RI);353          // Add the registers this load reads to those we check for clobbers.354          for (unsigned i = InstrDesc.getNumDefs(),355                        e = InstrDesc.getNumOperands(); i != e; i++) {356            const auto &Operand = NodeInstr.Instruction.getOperand(i);357            if (Operand.isReg())358              CurRegisterNumbers.insert(Operand.getReg());359          }360          break;361        }362      }363    }364  }365 366  return Graph.BaseAddress;367}368 369void FileAnalysis::printInstruction(const Instr &InstrMeta,370                                    raw_ostream &OS) const {371  Printer->printInst(&InstrMeta.Instruction, 0, "", *SubtargetInfo, OS);372}373 374Error FileAnalysis::initialiseDisassemblyMembers() {375  std::string TripleName = ObjectTriple.getTriple();376  ArchName = "";377  MCPU = "";378  std::string ErrorString;379 380  Triple TheTriple(TripleName);381 382  LLVMSymbolizer::Options Opt;383  Opt.UseSymbolTable = false;384  Symbolizer.reset(new LLVMSymbolizer(Opt));385 386  ObjectTarget =387      TargetRegistry::lookupTarget(ArchName, ObjectTriple, ErrorString);388  if (!ObjectTarget)389    return make_error<UnsupportedDisassembly>(390        (Twine("Couldn't find target \"") + ObjectTriple.getTriple() +391         "\", failed with error: " + ErrorString)392            .str());393 394  RegisterInfo.reset(ObjectTarget->createMCRegInfo(TheTriple));395  if (!RegisterInfo)396    return make_error<UnsupportedDisassembly>(397        "Failed to initialise RegisterInfo.");398 399  MCTargetOptions MCOptions;400  AsmInfo.reset(401      ObjectTarget->createMCAsmInfo(*RegisterInfo, TheTriple, MCOptions));402  if (!AsmInfo)403    return make_error<UnsupportedDisassembly>("Failed to initialise AsmInfo.");404 405  SubtargetInfo.reset(ObjectTarget->createMCSubtargetInfo(406      TheTriple, MCPU, Features.getString()));407  if (!SubtargetInfo)408    return make_error<UnsupportedDisassembly>(409        "Failed to initialise SubtargetInfo.");410 411  MII.reset(ObjectTarget->createMCInstrInfo());412  if (!MII)413    return make_error<UnsupportedDisassembly>("Failed to initialise MII.");414 415  Context.reset(new MCContext(Triple(TripleName), AsmInfo.get(),416                              RegisterInfo.get(), SubtargetInfo.get()));417 418  Disassembler.reset(419      ObjectTarget->createMCDisassembler(*SubtargetInfo, *Context));420 421  if (!Disassembler)422    return make_error<UnsupportedDisassembly>(423        "No disassembler available for target");424 425  MIA.reset(ObjectTarget->createMCInstrAnalysis(MII.get()));426 427  Printer.reset(ObjectTarget->createMCInstPrinter(428      ObjectTriple, AsmInfo->getAssemblerDialect(), *AsmInfo, *MII,429      *RegisterInfo));430 431  return Error::success();432}433 434Error FileAnalysis::parseCodeSections() {435  if (!IgnoreDWARFFlag) {436    std::unique_ptr<DWARFContext> DWARF = DWARFContext::create(*Object);437    if (!DWARF)438      return make_error<StringError>("Could not create DWARF information.",439                                     inconvertibleErrorCode());440 441    bool LineInfoValid = false;442 443    for (auto &Unit : DWARF->compile_units()) {444      const auto &LineTable = DWARF->getLineTableForUnit(Unit.get());445      if (LineTable && !LineTable->Rows.empty()) {446        LineInfoValid = true;447        break;448      }449    }450 451    if (!LineInfoValid)452      return make_error<StringError>(453          "DWARF line information missing. Did you compile with '-g'?",454          inconvertibleErrorCode());455  }456 457  for (const object::SectionRef &Section : Object->sections()) {458    // Ensure only executable sections get analysed.459    if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR))460      continue;461 462    // Avoid checking the PLT since it produces spurious failures on AArch64463    // when ignoring DWARF data.464    Expected<StringRef> NameOrErr = Section.getName();465    if (NameOrErr && *NameOrErr == ".plt")466      continue;467    consumeError(NameOrErr.takeError());468 469    Expected<StringRef> Contents = Section.getContents();470    if (!Contents)471      return Contents.takeError();472    ArrayRef<uint8_t> SectionBytes = arrayRefFromStringRef(*Contents);473 474    parseSectionContents(SectionBytes,475                         {Section.getAddress(), Section.getIndex()});476  }477  return Error::success();478}479 480void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes,481                                        object::SectionedAddress Address) {482  assert(Symbolizer && "Symbolizer is uninitialised.");483  MCInst Instruction;484  Instr InstrMeta;485  uint64_t InstructionSize;486 487  for (uint64_t Byte = 0; Byte < SectionBytes.size();) {488    bool ValidInstruction =489        Disassembler->getInstruction(Instruction, InstructionSize,490                                     SectionBytes.drop_front(Byte), 0,491                                     outs()) == MCDisassembler::Success;492 493    Byte += InstructionSize;494 495    uint64_t VMAddress = Address.Address + Byte - InstructionSize;496    InstrMeta.Instruction = Instruction;497    InstrMeta.VMAddress = VMAddress;498    InstrMeta.InstructionSize = InstructionSize;499    InstrMeta.Valid = ValidInstruction;500 501    addInstruction(InstrMeta);502 503    if (!ValidInstruction)504      continue;505 506    // Skip additional parsing for instructions that do not affect the control507    // flow.508    const auto &InstrDesc = MII->get(Instruction.getOpcode());509    if (!InstrDesc.mayAffectControlFlow(Instruction, *RegisterInfo))510      continue;511 512    uint64_t Target;513    if (MIA->evaluateBranch(Instruction, VMAddress, InstructionSize, Target)) {514      // If the target can be evaluated, it's not indirect.515      StaticBranchTargetings[Target].push_back(VMAddress);516      continue;517    }518 519    if (!usesRegisterOperand(InstrMeta))520      continue;521 522    if (InstrDesc.isReturn())523      continue;524 525    // Check if this instruction exists in the range of the DWARF metadata.526    if (!IgnoreDWARFFlag) {527      auto LineInfo = Symbolizer->symbolizeCode(528          Object->getFileName(), {VMAddress, Address.SectionIndex});529      if (!LineInfo) {530        handleAllErrors(LineInfo.takeError(), [](const ErrorInfoBase &E) {531          errs() << "Symbolizer failed to get line: " << E.message() << "\n";532        });533        continue;534      }535 536      if (LineInfo->FileName == DILineInfo::BadString)537        continue;538    }539 540    IndirectInstructions.insert({VMAddress, Address.SectionIndex});541  }542}543 544void FileAnalysis::addInstruction(const Instr &Instruction) {545  const auto &KV =546      Instructions.insert(std::make_pair(Instruction.VMAddress, Instruction));547  if (!KV.second) {548    errs() << "Failed to add instruction at address "549           << format_hex(Instruction.VMAddress, 2)550           << ": Instruction at this address already exists.\n";551    exit(EXIT_FAILURE);552  }553}554 555Error FileAnalysis::parseSymbolTable() {556  // Functions that will trap on CFI violations.557  SmallSet<StringRef, 4> TrapOnFailFunctions;558  TrapOnFailFunctions.insert("__cfi_slowpath");559  TrapOnFailFunctions.insert("__cfi_slowpath_diag");560  TrapOnFailFunctions.insert("abort");561 562  // Look through the list of symbols for functions that will trap on CFI563  // violations.564  for (auto &Sym : Object->symbols()) {565    auto SymNameOrErr = Sym.getName();566    if (!SymNameOrErr)567      consumeError(SymNameOrErr.takeError());568    else if (TrapOnFailFunctions.contains(*SymNameOrErr)) {569      auto AddrOrErr = Sym.getAddress();570      if (!AddrOrErr)571        consumeError(AddrOrErr.takeError());572      else573        TrapOnFailFunctionAddresses.insert(*AddrOrErr);574    }575  }576  if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Object)) {577    for (const auto &Plt : ElfObject->getPltEntries(*SubtargetInfo)) {578      if (!Plt.Symbol)579        continue;580      object::SymbolRef Sym(*Plt.Symbol, Object);581      auto SymNameOrErr = Sym.getName();582      if (!SymNameOrErr)583        consumeError(SymNameOrErr.takeError());584      else if (TrapOnFailFunctions.contains(*SymNameOrErr))585        TrapOnFailFunctionAddresses.insert(Plt.Address);586    }587  }588  return Error::success();589}590 591UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text)592    : Text(std::string(Text)) {}593 594char UnsupportedDisassembly::ID;595void UnsupportedDisassembly::log(raw_ostream &OS) const {596  OS << "Could not initialise disassembler: " << Text;597}598 599std::error_code UnsupportedDisassembly::convertToErrorCode() const {600  return std::error_code();601}602 603} // namespace cfi_verify604} // namespace llvm605