1 //===- AArch64GlobalsTagging.cpp - Global tagging in IR -------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //===----------------------------------------------------------------------===//
10 
11 #include "AArch64.h"
12 #include "llvm/BinaryFormat/ELF.h"
13 #include "llvm/IR/Attributes.h"
14 #include "llvm/IR/Constants.h"
15 #include "llvm/IR/GlobalValue.h"
16 #include "llvm/IR/GlobalVariable.h"
17 #include "llvm/IR/IRBuilder.h"
18 #include "llvm/IR/Module.h"
19 #include "llvm/Pass.h"
20 #include "llvm/Support/raw_ostream.h"
21 
22 #include <algorithm>
23 #include <set>
24 
25 using namespace llvm;
26 
27 static const Align kTagGranuleSize = Align(16);
28 
29 static bool shouldTagGlobal(GlobalVariable &G) {
30   if (!G.isTagged())
31     return false;
32 
33   assert(G.hasSanitizerMetadata() &&
34          "Missing sanitizer metadata, but symbol is apparently tagged.");
35   GlobalValue::SanitizerMetadata Meta = G.getSanitizerMetadata();
36 
37   // For now, don't instrument constant data, as it'll be in .rodata anyway. It
38   // may be worth instrumenting these in future to stop them from being used as
39   // gadgets.
40   if (G.getName().startswith("llvm.") || G.isThreadLocal() || G.isConstant()) {
41     Meta.Memtag = false;
42     G.setSanitizerMetadata(Meta);
43     return false;
44   }
45 
46   return true;
47 }
48 
49 // Technically, due to ELF symbol interposition semantics, we can't change the
50 // alignment or size of symbols. If we increase the alignment or size of a
51 // symbol, the compiler may make optimisations based on this new alignment or
52 // size. If the symbol is interposed, this optimisation could lead to
53 // alignment-related or OOB read/write crashes.
54 //
55 // This is handled in the linker. When the linker sees multiple declarations of
56 // a global variable, and some are tagged, and some are untagged, it resolves it
57 // to be an untagged definition - but preserves the tag-granule-rounded size and
58 // tag-granule-alignment. This should prevent these kind of crashes intra-DSO.
59 // For cross-DSO, it's been a reasonable contract that if you're interposing a
60 // sanitizer-instrumented global, then the interposer also needs to be
61 // sanitizer-instrumented.
62 //
63 // FIXME: In theory, this can be fixed by splitting the size/alignment of
64 // globals into two uses: an "output alignment" that's emitted to the ELF file,
65 // and an "optimisation alignment" that's used for optimisation. Thus, we could
66 // adjust the output alignment only, and still optimise based on the pessimistic
67 // pre-tagging size/alignment.
68 static void tagGlobalDefinition(Module &M, GlobalVariable *G) {
69   Constant *Initializer = G->getInitializer();
70   uint64_t SizeInBytes =
71       M.getDataLayout().getTypeAllocSize(Initializer->getType());
72 
73   uint64_t NewSize = alignTo(SizeInBytes, kTagGranuleSize);
74   if (SizeInBytes != NewSize) {
75     // Pad the initializer out to the next multiple of 16 bytes.
76     llvm::SmallVector<uint8_t> Init(NewSize - SizeInBytes, 0);
77     Constant *Padding = ConstantDataArray::get(M.getContext(), Init);
78     Initializer = ConstantStruct::getAnon({Initializer, Padding});
79     auto *NewGV = new GlobalVariable(
80         M, Initializer->getType(), G->isConstant(), G->getLinkage(),
81         Initializer, "", G, G->getThreadLocalMode(), G->getAddressSpace());
82     NewGV->copyAttributesFrom(G);
83     NewGV->setComdat(G->getComdat());
84     NewGV->copyMetadata(G, 0);
85 
86     NewGV->takeName(G);
87     G->replaceAllUsesWith(NewGV);
88     G->eraseFromParent();
89     G = NewGV;
90   }
91 
92   G->setAlignment(std::max(G->getAlign().valueOrOne(), kTagGranuleSize));
93 
94   // Ensure that tagged globals don't get merged by ICF - as they should have
95   // different tags at runtime.
96   G->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
97 }
98 
99 namespace {
100 class AArch64GlobalsTagging : public ModulePass {
101 public:
102   static char ID;
103 
104   explicit AArch64GlobalsTagging() : ModulePass(ID) {
105     initializeAArch64GlobalsTaggingPass(*PassRegistry::getPassRegistry());
106   }
107 
108   bool runOnModule(Module &M) override;
109 
110   StringRef getPassName() const override { return "AArch64 Globals Tagging"; }
111 
112 private:
113   std::set<GlobalVariable *> GlobalsToTag;
114 };
115 } // anonymous namespace
116 
117 char AArch64GlobalsTagging::ID = 0;
118 
119 bool AArch64GlobalsTagging::runOnModule(Module &M) {
120   // No mutating the globals in-place, or iterator invalidation occurs.
121   std::vector<GlobalVariable *> GlobalsToTag;
122   for (GlobalVariable &G : M.globals()) {
123     if (G.isDeclaration() || !shouldTagGlobal(G))
124       continue;
125     GlobalsToTag.push_back(&G);
126   }
127 
128   for (GlobalVariable *G : GlobalsToTag) {
129     tagGlobalDefinition(M, G);
130   }
131 
132   return true;
133 }
134 
135 INITIALIZE_PASS_BEGIN(AArch64GlobalsTagging, "aarch64-globals-tagging",
136                       "AArch64 Globals Tagging Pass", false, false)
137 INITIALIZE_PASS_END(AArch64GlobalsTagging, "aarch64-globals-tagging",
138                     "AArch64 Globals Tagging Pass", false, false)
139 
140 ModulePass *llvm::createAArch64GlobalsTaggingPass() {
141   return new AArch64GlobalsTagging();
142 }
143