1 //===--- SanitizerMetadata.cpp - Ignored entities for sanitizers ----------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Class which emits metadata consumed by sanitizer instrumentation passes. 10 // 11 //===----------------------------------------------------------------------===// 12 #include "SanitizerMetadata.h" 13 #include "CodeGenModule.h" 14 #include "clang/AST/Attr.h" 15 #include "clang/AST/Type.h" 16 17 using namespace clang; 18 using namespace CodeGen; 19 20 SanitizerMetadata::SanitizerMetadata(CodeGenModule &CGM) : CGM(CGM) {} 21 22 static bool isAsanHwasanMemTagOrTysan(const SanitizerSet &SS) { 23 return SS.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress | 24 SanitizerKind::HWAddress | SanitizerKind::MemTag | 25 SanitizerKind::Type); 26 } 27 28 static SanitizerMask expandKernelSanitizerMasks(SanitizerMask Mask) { 29 if (Mask & (SanitizerKind::Address | SanitizerKind::KernelAddress)) 30 Mask |= SanitizerKind::Address | SanitizerKind::KernelAddress; 31 // Note: KHWASan doesn't support globals. 32 return Mask; 33 } 34 35 static bool shouldTagGlobal(const llvm::GlobalVariable &G) { 36 // For now, don't instrument constant data, as it'll be in .rodata anyway. It 37 // may be worth instrumenting these in future to stop them from being used as 38 // gadgets. 39 if (G.getName().starts_with("llvm.") || G.isThreadLocal() || G.isConstant()) 40 return false; 41 42 // Globals can be placed implicitly or explicitly in sections. There's two 43 // different types of globals that meet this criteria that cause problems: 44 // 1. Function pointers that are going into various init arrays (either 45 // explicitly through `__attribute__((section(<foo>)))` or implicitly 46 // through `__attribute__((constructor)))`, such as ".(pre)init(_array)", 47 // ".fini(_array)", ".ctors", and ".dtors". These function pointers end up 48 // overaligned and overpadded, making iterating over them problematic, and 49 // each function pointer is individually tagged (so the iteration over 50 // them causes SIGSEGV/MTE[AS]ERR). 51 // 2. Global variables put into an explicit section, where the section's name 52 // is a valid C-style identifier. The linker emits a `__start_<name>` and 53 // `__stop_<name>` symbol for the section, so that you can iterate over 54 // globals within this section. Unfortunately, again, these globals would 55 // be tagged and so iteration causes SIGSEGV/MTE[AS]ERR. 56 // 57 // To mitigate both these cases, and because specifying a section is rare 58 // outside of these two cases, disable MTE protection for globals in any 59 // section. 60 if (G.hasSection()) 61 return false; 62 63 return true; 64 } 65 66 void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV, 67 SourceLocation Loc, StringRef Name, 68 QualType Ty, 69 SanitizerMask NoSanitizeAttrMask, 70 bool IsDynInit) { 71 SanitizerSet FsanitizeArgument = CGM.getLangOpts().Sanitize; 72 if (!isAsanHwasanMemTagOrTysan(FsanitizeArgument)) 73 return; 74 75 FsanitizeArgument.Mask = expandKernelSanitizerMasks(FsanitizeArgument.Mask); 76 NoSanitizeAttrMask = expandKernelSanitizerMasks(NoSanitizeAttrMask); 77 SanitizerSet NoSanitizeAttrSet = {NoSanitizeAttrMask & 78 FsanitizeArgument.Mask}; 79 80 llvm::GlobalVariable::SanitizerMetadata Meta; 81 if (GV->hasSanitizerMetadata()) 82 Meta = GV->getSanitizerMetadata(); 83 84 Meta.NoAddress |= NoSanitizeAttrSet.hasOneOf(SanitizerKind::Address); 85 Meta.NoAddress |= CGM.isInNoSanitizeList( 86 FsanitizeArgument.Mask & SanitizerKind::Address, GV, Loc, Ty); 87 88 Meta.NoHWAddress |= NoSanitizeAttrSet.hasOneOf(SanitizerKind::HWAddress); 89 Meta.NoHWAddress |= CGM.isInNoSanitizeList( 90 FsanitizeArgument.Mask & SanitizerKind::HWAddress, GV, Loc, Ty); 91 92 if (shouldTagGlobal(*GV)) { 93 Meta.Memtag |= static_cast<bool>(FsanitizeArgument.Mask & 94 SanitizerKind::MemtagGlobals); 95 Meta.Memtag &= !NoSanitizeAttrSet.hasOneOf(SanitizerKind::MemTag); 96 Meta.Memtag &= !CGM.isInNoSanitizeList( 97 FsanitizeArgument.Mask & SanitizerKind::MemTag, GV, Loc, Ty); 98 } else { 99 Meta.Memtag = false; 100 } 101 102 Meta.IsDynInit = IsDynInit && !Meta.NoAddress && 103 FsanitizeArgument.has(SanitizerKind::Address) && 104 !CGM.isInNoSanitizeList(SanitizerKind::Address | 105 SanitizerKind::KernelAddress, 106 GV, Loc, Ty, "init"); 107 108 GV->setSanitizerMetadata(Meta); 109 110 if (Ty.isNull() || !CGM.getLangOpts().Sanitize.has(SanitizerKind::Type) || 111 NoSanitizeAttrMask & SanitizerKind::Type) 112 return; 113 114 llvm::MDNode *TBAAInfo = CGM.getTBAATypeInfo(Ty); 115 if (!TBAAInfo || TBAAInfo == CGM.getTBAATypeInfo(CGM.getContext().CharTy)) 116 return; 117 118 llvm::Metadata *GlobalMetadata[] = {llvm::ConstantAsMetadata::get(GV), 119 TBAAInfo}; 120 121 // Metadata for the global already registered. 122 if (llvm::MDNode::getIfExists(CGM.getLLVMContext(), GlobalMetadata)) 123 return; 124 125 llvm::MDNode *ThisGlobal = 126 llvm::MDNode::get(CGM.getLLVMContext(), GlobalMetadata); 127 llvm::NamedMDNode *TysanGlobals = 128 CGM.getModule().getOrInsertNamedMetadata("llvm.tysan.globals"); 129 TysanGlobals->addOperand(ThisGlobal); 130 } 131 132 void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV, const VarDecl &D, 133 bool IsDynInit) { 134 if (!isAsanHwasanMemTagOrTysan(CGM.getLangOpts().Sanitize)) 135 return; 136 std::string QualName; 137 llvm::raw_string_ostream OS(QualName); 138 D.printQualifiedName(OS); 139 140 auto getNoSanitizeMask = [](const VarDecl &D) { 141 if (D.hasAttr<DisableSanitizerInstrumentationAttr>()) 142 return SanitizerKind::All; 143 144 SanitizerMask NoSanitizeMask; 145 for (auto *Attr : D.specific_attrs<NoSanitizeAttr>()) 146 NoSanitizeMask |= Attr->getMask(); 147 148 // External definitions and incomplete types get handled at the place they 149 // are defined. 150 if (D.hasExternalStorage() || D.getType()->isIncompleteType()) 151 NoSanitizeMask |= SanitizerKind::Type; 152 153 return NoSanitizeMask; 154 }; 155 156 reportGlobal(GV, D.getLocation(), QualName, D.getType(), getNoSanitizeMask(D), 157 IsDynInit); 158 } 159 160 void SanitizerMetadata::disableSanitizerForGlobal(llvm::GlobalVariable *GV) { 161 reportGlobal(GV, SourceLocation(), "", QualType(), SanitizerKind::All); 162 } 163