brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.3 KiB · 210b126 Raw
505 lines · cpp
1//===- SanitizerBinaryMetadata.cpp - binary analysis sanitizers metadata --===//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// This file is a part of SanitizerBinaryMetadata.10//11//===----------------------------------------------------------------------===//12 13#include "llvm/Transforms/Instrumentation/SanitizerBinaryMetadata.h"14#include "llvm/ADT/SetVector.h"15#include "llvm/ADT/SmallVector.h"16#include "llvm/ADT/Statistic.h"17#include "llvm/ADT/StringExtras.h"18#include "llvm/ADT/StringRef.h"19#include "llvm/ADT/Twine.h"20#include "llvm/Analysis/CaptureTracking.h"21#include "llvm/Analysis/ValueTracking.h"22#include "llvm/IR/Constant.h"23#include "llvm/IR/DerivedTypes.h"24#include "llvm/IR/Function.h"25#include "llvm/IR/GlobalValue.h"26#include "llvm/IR/GlobalVariable.h"27#include "llvm/IR/IRBuilder.h"28#include "llvm/IR/Instruction.h"29#include "llvm/IR/Instructions.h"30#include "llvm/IR/LLVMContext.h"31#include "llvm/IR/MDBuilder.h"32#include "llvm/IR/Metadata.h"33#include "llvm/IR/Module.h"34#include "llvm/IR/Type.h"35#include "llvm/IR/Value.h"36#include "llvm/ProfileData/InstrProf.h"37#include "llvm/Support/Allocator.h"38#include "llvm/Support/CommandLine.h"39#include "llvm/Support/SpecialCaseList.h"40#include "llvm/Support/StringSaver.h"41#include "llvm/Support/VirtualFileSystem.h"42#include "llvm/TargetParser/Triple.h"43#include "llvm/Transforms/Utils/ModuleUtils.h"44 45#include <array>46#include <cstdint>47#include <memory>48 49using namespace llvm;50 51#define DEBUG_TYPE "sanmd"52 53namespace {54 55//===--- Constants --------------------------------------------------------===//56 57constexpr uint32_t kVersionBase = 2;                // occupies lower 16 bits58constexpr uint32_t kVersionPtrSizeRel = (1u << 16); // offsets are pointer-sized59constexpr int kCtorDtorPriority = 2;60 61// Pairs of names of initialization callback functions and which section62// contains the relevant metadata.63class MetadataInfo {64public:65  const StringRef FunctionPrefix;66  const StringRef SectionSuffix;67 68  static const MetadataInfo Covered;69  static const MetadataInfo Atomics;70 71private:72  // Forbid construction elsewhere.73  explicit constexpr MetadataInfo(StringRef FunctionPrefix,74                                  StringRef SectionSuffix)75      : FunctionPrefix(FunctionPrefix), SectionSuffix(SectionSuffix) {}76};77const MetadataInfo MetadataInfo::Covered{78    "__sanitizer_metadata_covered", kSanitizerBinaryMetadataCoveredSection};79const MetadataInfo MetadataInfo::Atomics{80    "__sanitizer_metadata_atomics", kSanitizerBinaryMetadataAtomicsSection};81 82// The only instances of MetadataInfo are the constants above, so a set of83// them may simply store pointers to them. To deterministically generate code,84// we need to use a set with stable iteration order, such as SetVector.85using MetadataInfoSet = SetVector<const MetadataInfo *>;86 87//===--- Command-line options ---------------------------------------------===//88 89cl::opt<bool> ClWeakCallbacks(90    "sanitizer-metadata-weak-callbacks",91    cl::desc("Declare callbacks extern weak, and only call if non-null."),92    cl::Hidden, cl::init(true));93cl::opt<bool>94    ClNoSanitize("sanitizer-metadata-nosanitize-attr",95                 cl::desc("Mark some metadata features uncovered in functions "96                          "with associated no_sanitize attributes."),97                 cl::Hidden, cl::init(true));98 99cl::opt<bool> ClEmitCovered("sanitizer-metadata-covered",100                            cl::desc("Emit PCs for covered functions."),101                            cl::Hidden, cl::init(false));102cl::opt<bool> ClEmitAtomics("sanitizer-metadata-atomics",103                            cl::desc("Emit PCs for atomic operations."),104                            cl::Hidden, cl::init(false));105cl::opt<bool> ClEmitUAR("sanitizer-metadata-uar",106                        cl::desc("Emit PCs for start of functions that are "107                                 "subject for use-after-return checking"),108                        cl::Hidden, cl::init(false));109 110//===--- Statistics -------------------------------------------------------===//111 112STATISTIC(NumMetadataCovered, "Metadata attached to covered functions");113STATISTIC(NumMetadataAtomics, "Metadata attached to atomics");114STATISTIC(NumMetadataUAR, "Metadata attached to UAR functions");115 116//===----------------------------------------------------------------------===//117 118// Apply opt overrides.119SanitizerBinaryMetadataOptions &&120transformOptionsFromCl(SanitizerBinaryMetadataOptions &&Opts) {121  Opts.Covered |= ClEmitCovered;122  Opts.Atomics |= ClEmitAtomics;123  Opts.UAR |= ClEmitUAR;124  return std::move(Opts);125}126 127class SanitizerBinaryMetadata {128public:129  SanitizerBinaryMetadata(Module &M, SanitizerBinaryMetadataOptions Opts,130                          std::unique_ptr<SpecialCaseList> Ignorelist)131      : Mod(M), Options(transformOptionsFromCl(std::move(Opts))),132        Ignorelist(std::move(Ignorelist)), TargetTriple(M.getTargetTriple()),133        VersionStr(utostr(getVersion())), IRB(M.getContext()) {134    // FIXME: Make it work with other formats.135    assert(TargetTriple.isOSBinFormatELF() && "ELF only");136    assert(!TargetTriple.isGPU() && "Device targets are not supported");137  }138 139  bool run();140 141private:142  uint32_t getVersion() const {143    uint32_t Version = kVersionBase;144    const auto CM = Mod.getCodeModel();145    if (CM.has_value() && (*CM == CodeModel::Medium || *CM == CodeModel::Large))146      Version |= kVersionPtrSizeRel;147    return Version;148  }149 150  void runOn(Function &F, MetadataInfoSet &MIS);151 152  // Determines which set of metadata to collect for this instruction.153  //154  // Returns true if covered metadata is required to unambiguously interpret155  // other metadata. For example, if we are interested in atomics metadata, any156  // function with memory operations (atomic or not) requires covered metadata157  // to determine if a memory operation is atomic or not in modules compiled158  // with SanitizerBinaryMetadata.159  bool runOn(Instruction &I, MetadataInfoSet &MIS, MDBuilder &MDB,160             uint64_t &FeatureMask);161 162  // Get start/end section marker pointer.163  GlobalVariable *getSectionMarker(const Twine &MarkerName, Type *Ty);164 165  // Returns the target-dependent section name.166  StringRef getSectionName(StringRef SectionSuffix);167 168  // Returns the section start marker name.169  StringRef getSectionStart(StringRef SectionSuffix);170 171  // Returns the section end marker name.172  StringRef getSectionEnd(StringRef SectionSuffix);173 174  // Returns true if the access to the address should be considered "atomic".175  bool pretendAtomicAccess(const Value *Addr);176 177  Module &Mod;178  const SanitizerBinaryMetadataOptions Options;179  std::unique_ptr<SpecialCaseList> Ignorelist;180  const Triple TargetTriple;181  const std::string VersionStr;182  IRBuilder<> IRB;183  BumpPtrAllocator Alloc;184  UniqueStringSaver StringPool{Alloc};185};186 187bool SanitizerBinaryMetadata::run() {188  MetadataInfoSet MIS;189 190  for (Function &F : Mod)191    runOn(F, MIS);192 193  if (MIS.empty())194    return false;195 196  //197  // Setup constructors and call all initialization functions for requested198  // metadata features.199  //200 201  auto *PtrTy = IRB.getPtrTy();202  auto *Int32Ty = IRB.getInt32Ty();203  const std::array<Type *, 3> InitTypes = {Int32Ty, PtrTy, PtrTy};204  auto *Version = ConstantInt::get(Int32Ty, getVersion());205 206  for (const MetadataInfo *MI : MIS) {207    const std::array<Value *, InitTypes.size()> InitArgs = {208        Version,209        getSectionMarker(getSectionStart(MI->SectionSuffix), PtrTy),210        getSectionMarker(getSectionEnd(MI->SectionSuffix), PtrTy),211    };212 213    // Calls to the initialization functions with different versions cannot be214    // merged. Give the structors unique names based on the version, which will215    // also be used as the COMDAT key.216    const std::string StructorPrefix = (MI->FunctionPrefix + VersionStr).str();217 218    // We declare the _add and _del functions as weak, and only call them if219    // there is a valid symbol linked. This allows building binaries with220    // semantic metadata, but without having callbacks. When a tool that wants221    // the metadata is linked which provides the callbacks, they will be called.222    Function *Ctor =223        createSanitizerCtorAndInitFunctions(224            Mod, StructorPrefix + ".module_ctor",225            (MI->FunctionPrefix + "_add").str(), InitTypes, InitArgs,226            /*VersionCheckName=*/StringRef(), /*Weak=*/ClWeakCallbacks)227            .first;228    Function *Dtor =229        createSanitizerCtorAndInitFunctions(230            Mod, StructorPrefix + ".module_dtor",231            (MI->FunctionPrefix + "_del").str(), InitTypes, InitArgs,232            /*VersionCheckName=*/StringRef(), /*Weak=*/ClWeakCallbacks)233            .first;234    Constant *CtorComdatKey = nullptr;235    Constant *DtorComdatKey = nullptr;236    if (TargetTriple.supportsCOMDAT()) {237      // Use COMDAT to deduplicate constructor/destructor function. The COMDAT238      // key needs to be a non-local linkage.239      Ctor->setComdat(Mod.getOrInsertComdat(Ctor->getName()));240      Dtor->setComdat(Mod.getOrInsertComdat(Dtor->getName()));241      Ctor->setLinkage(GlobalValue::ExternalLinkage);242      Dtor->setLinkage(GlobalValue::ExternalLinkage);243      // DSOs should _not_ call another constructor/destructor!244      Ctor->setVisibility(GlobalValue::HiddenVisibility);245      Dtor->setVisibility(GlobalValue::HiddenVisibility);246      CtorComdatKey = Ctor;247      DtorComdatKey = Dtor;248    }249    appendToGlobalCtors(Mod, Ctor, kCtorDtorPriority, CtorComdatKey);250    appendToGlobalDtors(Mod, Dtor, kCtorDtorPriority, DtorComdatKey);251  }252 253  return true;254}255 256void SanitizerBinaryMetadata::runOn(Function &F, MetadataInfoSet &MIS) {257  if (F.empty())258    return;259  // Do not apply any instrumentation for naked functions.260  if (F.hasFnAttribute(Attribute::Naked))261    return;262  if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation))263    return;264  if (Ignorelist && Ignorelist->inSection("metadata", "fun", F.getName()))265    return;266  // Don't touch available_externally functions, their actual body is elsewhere.267  if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage)268    return;269 270  MDBuilder MDB(F.getContext());271 272  // The metadata features enabled for this function, stored along covered273  // metadata (if enabled).274  uint64_t FeatureMask = 0;275  // Don't emit unnecessary covered metadata for all functions to save space.276  bool RequiresCovered = false;277 278  if (Options.Atomics || Options.UAR) {279    for (BasicBlock &BB : F)280      for (Instruction &I : BB)281        RequiresCovered |= runOn(I, MIS, MDB, FeatureMask);282  }283 284  if (ClNoSanitize && F.hasFnAttribute("no_sanitize_thread"))285    FeatureMask &= ~kSanitizerBinaryMetadataAtomics;286  if (F.isVarArg())287    FeatureMask &= ~kSanitizerBinaryMetadataUAR;288  if (FeatureMask & kSanitizerBinaryMetadataUAR) {289    RequiresCovered = true;290    NumMetadataUAR++;291  }292 293  // Covered metadata is always emitted if explicitly requested, otherwise only294  // if some other metadata requires it to unambiguously interpret it for295  // modules compiled with SanitizerBinaryMetadata.296  if (Options.Covered || (FeatureMask && RequiresCovered)) {297    NumMetadataCovered++;298    const auto *MI = &MetadataInfo::Covered;299    MIS.insert(MI);300    const StringRef Section = getSectionName(MI->SectionSuffix);301    // The feature mask will be placed after the function size.302    Constant *CFM = IRB.getInt64(FeatureMask);303    F.setMetadata(LLVMContext::MD_pcsections,304                  MDB.createPCSections({{Section, {CFM}}}));305  }306}307 308bool isUARSafeCall(CallInst *CI) {309  auto *F = CI->getCalledFunction();310  // There are no intrinsic functions that leak arguments.311  // If the called function does not return, the current function312  // does not return as well, so no possibility of use-after-return.313  // Sanitizer function also don't leak or don't return.314  // It's safe to both pass pointers to local variables to them315  // and to tail-call them.316  return F && (F->isIntrinsic() || F->doesNotReturn() ||317               F->getName().starts_with("__asan_") ||318               F->getName().starts_with("__hwsan_") ||319               F->getName().starts_with("__ubsan_") ||320               F->getName().starts_with("__msan_") ||321               F->getName().starts_with("__tsan_"));322}323 324bool hasUseAfterReturnUnsafeUses(Value &V) {325  for (User *U : V.users()) {326    if (auto *I = dyn_cast<Instruction>(U)) {327      if (I->isLifetimeStartOrEnd() || I->isDroppable())328        continue;329      if (auto *CI = dyn_cast<CallInst>(U)) {330        if (isUARSafeCall(CI))331          continue;332      }333      if (isa<LoadInst>(U))334        continue;335      if (auto *SI = dyn_cast<StoreInst>(U)) {336        // If storing TO the alloca, then the address isn't taken.337        if (SI->getOperand(1) == &V)338          continue;339      }340      if (auto *GEPI = dyn_cast<GetElementPtrInst>(U)) {341        if (!hasUseAfterReturnUnsafeUses(*GEPI))342          continue;343      } else if (auto *BCI = dyn_cast<BitCastInst>(U)) {344        if (!hasUseAfterReturnUnsafeUses(*BCI))345          continue;346      }347    }348    return true;349  }350  return false;351}352 353bool useAfterReturnUnsafe(Instruction &I) {354  if (isa<AllocaInst>(I))355    return hasUseAfterReturnUnsafeUses(I);356  // Tail-called functions are not necessary intercepted357  // at runtime because there is no call instruction.358  // So conservatively mark the caller as requiring checking.359  else if (auto *CI = dyn_cast<CallInst>(&I))360    return CI->isTailCall() && !isUARSafeCall(CI);361  return false;362}363 364bool SanitizerBinaryMetadata::pretendAtomicAccess(const Value *Addr) {365  if (!Addr)366    return false;367 368  Addr = Addr->stripInBoundsOffsets();369  auto *GV = dyn_cast<GlobalVariable>(Addr);370  if (!GV)371    return false;372 373  // Some compiler-generated accesses are known racy, to avoid false positives374  // in data-race analysis pretend they're atomic.375  if (GV->hasSection()) {376    const auto OF = Mod.getTargetTriple().getObjectFormat();377    const auto ProfSec =378        getInstrProfSectionName(IPSK_cnts, OF, /*AddSegmentInfo=*/false);379    if (GV->getSection().ends_with(ProfSec))380      return true;381  }382  if (GV->getName().starts_with("__llvm_gcov") ||383      GV->getName().starts_with("__llvm_gcda"))384    return true;385 386  return false;387}388 389// Returns true if the memory at `Addr` may be shared with other threads.390bool maybeSharedMutable(const Value *Addr) {391  // By default assume memory may be shared.392  if (!Addr)393    return true;394 395  const AllocaInst *AI = findAllocaForValue(Addr);396  if (AI && !PointerMayBeCaptured(AI, /*ReturnCaptures=*/true))397    return false; // Object is on stack but does not escape.398 399  Addr = Addr->stripInBoundsOffsets();400  if (auto *GV = dyn_cast<GlobalVariable>(Addr)) {401    if (GV->isConstant())402      return false; // Shared, but not mutable.403  }404 405  return true;406}407 408bool SanitizerBinaryMetadata::runOn(Instruction &I, MetadataInfoSet &MIS,409                                    MDBuilder &MDB, uint64_t &FeatureMask) {410  SmallVector<const MetadataInfo *, 1> InstMetadata;411  bool RequiresCovered = false;412 413  // Only call if at least 1 type of metadata is requested.414  assert(Options.UAR || Options.Atomics);415 416  if (Options.UAR && !(FeatureMask & kSanitizerBinaryMetadataUAR)) {417    if (useAfterReturnUnsafe(I))418      FeatureMask |= kSanitizerBinaryMetadataUAR;419  }420 421  if (Options.Atomics) {422    const Value *Addr = nullptr;423    if (auto *SI = dyn_cast<StoreInst>(&I))424      Addr = SI->getPointerOperand();425    else if (auto *LI = dyn_cast<LoadInst>(&I))426      Addr = LI->getPointerOperand();427 428    if (I.mayReadOrWriteMemory() && maybeSharedMutable(Addr)) {429      auto SSID = getAtomicSyncScopeID(&I);430      if ((SSID.has_value() && *SSID != SyncScope::SingleThread) ||431          pretendAtomicAccess(Addr)) {432        NumMetadataAtomics++;433        InstMetadata.push_back(&MetadataInfo::Atomics);434      }435      FeatureMask |= kSanitizerBinaryMetadataAtomics;436      RequiresCovered = true;437    }438  }439 440  // Attach MD_pcsections to instruction.441  if (!InstMetadata.empty()) {442    MIS.insert_range(InstMetadata);443    SmallVector<MDBuilder::PCSection, 1> Sections;444    for (const auto &MI : InstMetadata)445      Sections.push_back({getSectionName(MI->SectionSuffix), {}});446    I.setMetadata(LLVMContext::MD_pcsections, MDB.createPCSections(Sections));447  }448 449  return RequiresCovered;450}451 452GlobalVariable *453SanitizerBinaryMetadata::getSectionMarker(const Twine &MarkerName, Type *Ty) {454  // Use ExternalWeak so that if all sections are discarded due to section455  // garbage collection, the linker will not report undefined symbol errors.456  auto *Marker = new GlobalVariable(Mod, Ty, /*isConstant=*/false,457                                    GlobalVariable::ExternalWeakLinkage,458                                    /*Initializer=*/nullptr, MarkerName);459  Marker->setVisibility(GlobalValue::HiddenVisibility);460  return Marker;461}462 463StringRef SanitizerBinaryMetadata::getSectionName(StringRef SectionSuffix) {464  // FIXME: Other TargetTriples.465  // Request ULEB128 encoding for all integer constants.466  return StringPool.save(SectionSuffix + VersionStr + "!C");467}468 469StringRef SanitizerBinaryMetadata::getSectionStart(StringRef SectionSuffix) {470  // Twine only concatenates 2 strings; with >2 strings, concatenating them471  // creates Twine temporaries, and returning the final Twine no longer works472  // because we'd end up with a stack-use-after-return. So here we also use the473  // StringPool to store the new string.474  return StringPool.save("__start_" + SectionSuffix + VersionStr);475}476 477StringRef SanitizerBinaryMetadata::getSectionEnd(StringRef SectionSuffix) {478  return StringPool.save("__stop_" + SectionSuffix + VersionStr);479}480 481} // namespace482 483SanitizerBinaryMetadataPass::SanitizerBinaryMetadataPass(484    SanitizerBinaryMetadataOptions Opts,485    IntrusiveRefCntPtr<vfs::FileSystem> VFS,486    ArrayRef<std::string> IgnorelistFiles)487    : Options(std::move(Opts)),488      VFS(VFS ? std::move(VFS) : vfs::getRealFileSystem()),489      IgnorelistFiles(std::move(IgnorelistFiles)) {}490 491PreservedAnalyses492SanitizerBinaryMetadataPass::run(Module &M, AnalysisManager<Module> &AM) {493  std::unique_ptr<SpecialCaseList> Ignorelist;494  if (!IgnorelistFiles.empty()) {495    Ignorelist = SpecialCaseList::createOrDie(IgnorelistFiles, *VFS);496    if (Ignorelist->inSection("metadata", "src", M.getSourceFileName()))497      return PreservedAnalyses::all();498  }499 500  SanitizerBinaryMetadata Pass(M, Options, std::move(Ignorelist));501  if (Pass.run())502    return PreservedAnalyses::none();503  return PreservedAnalyses::all();504}505