LLVM 18.0.0git
AArch64GlobalsTagging.cpp
Go to the documentation of this file.
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"
13#include "llvm/IR/Attributes.h"
14#include "llvm/IR/Constants.h"
15#include "llvm/IR/GlobalValue.h"
17#include "llvm/IR/IRBuilder.h"
18#include "llvm/IR/Module.h"
19#include "llvm/Pass.h"
21
22#include <algorithm>
23#include <set>
24
25using namespace llvm;
26
27static const Align kTagGranuleSize = Align(16);
28
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 // Don't instrument function pointers that are going into various init arrays
47 // via `__attribute__((section(<foo>)))`:
48 // https://github.com/llvm/llvm-project/issues/69939
49 if (G.hasSection() &&
50 (G.getSection() == ".init" || G.getSection() == ".fini" ||
51 G.getSection() == ".init_array" || G.getSection() == ".fini_array" ||
52 G.getSection() == ".ctors" || G.getSection() == ".dtors")) {
53 Meta.Memtag = false;
54 G.setSanitizerMetadata(Meta);
55 return false;
56 }
57
58 return true;
59}
60
61// Technically, due to ELF symbol interposition semantics, we can't change the
62// alignment or size of symbols. If we increase the alignment or size of a
63// symbol, the compiler may make optimisations based on this new alignment or
64// size. If the symbol is interposed, this optimisation could lead to
65// alignment-related or OOB read/write crashes.
66//
67// This is handled in the linker. When the linker sees multiple declarations of
68// a global variable, and some are tagged, and some are untagged, it resolves it
69// to be an untagged definition - but preserves the tag-granule-rounded size and
70// tag-granule-alignment. This should prevent these kind of crashes intra-DSO.
71// For cross-DSO, it's been a reasonable contract that if you're interposing a
72// sanitizer-instrumented global, then the interposer also needs to be
73// sanitizer-instrumented.
74//
75// FIXME: In theory, this can be fixed by splitting the size/alignment of
76// globals into two uses: an "output alignment" that's emitted to the ELF file,
77// and an "optimisation alignment" that's used for optimisation. Thus, we could
78// adjust the output alignment only, and still optimise based on the pessimistic
79// pre-tagging size/alignment.
81 Constant *Initializer = G->getInitializer();
82 uint64_t SizeInBytes =
83 M.getDataLayout().getTypeAllocSize(Initializer->getType());
84
85 uint64_t NewSize = alignTo(SizeInBytes, kTagGranuleSize);
86 if (SizeInBytes != NewSize) {
87 // Pad the initializer out to the next multiple of 16 bytes.
88 llvm::SmallVector<uint8_t> Init(NewSize - SizeInBytes, 0);
89 Constant *Padding = ConstantDataArray::get(M.getContext(), Init);
90 Initializer = ConstantStruct::getAnon({Initializer, Padding});
91 auto *NewGV = new GlobalVariable(
92 M, Initializer->getType(), G->isConstant(), G->getLinkage(),
93 Initializer, "", G, G->getThreadLocalMode(), G->getAddressSpace());
94 NewGV->copyAttributesFrom(G);
95 NewGV->setComdat(G->getComdat());
96 NewGV->copyMetadata(G, 0);
97
98 NewGV->takeName(G);
99 G->replaceAllUsesWith(NewGV);
100 G->eraseFromParent();
101 G = NewGV;
102 }
103
104 G->setAlignment(std::max(G->getAlign().valueOrOne(), kTagGranuleSize));
105
106 // Ensure that tagged globals don't get merged by ICF - as they should have
107 // different tags at runtime.
108 G->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
109}
110
111namespace {
112class AArch64GlobalsTagging : public ModulePass {
113public:
114 static char ID;
115
116 explicit AArch64GlobalsTagging() : ModulePass(ID) {
118 }
119
120 bool runOnModule(Module &M) override;
121
122 StringRef getPassName() const override { return "AArch64 Globals Tagging"; }
123
124private:
125 std::set<GlobalVariable *> GlobalsToTag;
126};
127} // anonymous namespace
128
129char AArch64GlobalsTagging::ID = 0;
130
131bool AArch64GlobalsTagging::runOnModule(Module &M) {
132 // No mutating the globals in-place, or iterator invalidation occurs.
133 std::vector<GlobalVariable *> GlobalsToTag;
134 for (GlobalVariable &G : M.globals()) {
135 if (G.isDeclaration() || !shouldTagGlobal(G))
136 continue;
137 GlobalsToTag.push_back(&G);
138 }
139
140 for (GlobalVariable *G : GlobalsToTag) {
142 }
143
144 return true;
145}
146
147INITIALIZE_PASS_BEGIN(AArch64GlobalsTagging, "aarch64-globals-tagging",
148 "AArch64 Globals Tagging Pass", false, false)
149INITIALIZE_PASS_END(AArch64GlobalsTagging, "aarch64-globals-tagging",
150 "AArch64 Globals Tagging Pass", false, false)
151
153 return new AArch64GlobalsTagging();
154}
static bool shouldTagGlobal(GlobalVariable &G)
static void tagGlobalDefinition(Module &M, GlobalVariable *G)
static const Align kTagGranuleSize
aarch64 globals tagging
AArch64 Stack Tagging
This file contains the simple types necessary to represent the attributes associated with functions a...
This file contains the declarations for the subclasses of Constant, which represent the different fla...
dxil globals
#define G(x, y, z)
Definition: MD5.cpp:56
Module.h This file contains the declarations for the Module class.
#define INITIALIZE_PASS_END(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:59
#define INITIALIZE_PASS_BEGIN(passName, arg, name, cfg, analysis)
Definition: PassSupport.h:52
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
static Constant * get(LLVMContext &Context, ArrayRef< ElementTy > Elts)
get() constructor - Return a constant with array type with an element count and element type matching...
Definition: Constants.h:690
static Constant * getAnon(ArrayRef< Constant * > V, bool Packed=false)
Return an anonymous struct that has the specified elements.
Definition: Constants.h:461
This is an important base class in LLVM.
Definition: Constant.h:41
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition: Pass.h:251
virtual bool runOnModule(Module &M)=0
runOnModule - Virtual method overriden by subclasses to process the module being operated on.
A Module instance is used to store all the information related to an LLVM module.
Definition: Module.h:65
static PassRegistry * getPassRegistry()
getPassRegistry - Access the global registry object, which is automatically initialized at applicatio...
Pass interface - Implemented by all 'passes'.
Definition: Pass.h:94
virtual StringRef getPassName() const
getPassName - Return a nice clean name for a pass.
Definition: Pass.cpp:81
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Definition: SmallVector.h:1200
StringRef - Represent a constant reference to a string, i.e.
Definition: StringRef.h:50
Type * getType() const
All values are typed, get the type of this value.
Definition: Value.h:255
unsigned ID
LLVM IR allows to use arbitrary numbers as calling convention identifiers.
Definition: CallingConv.h:24
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
void initializeAArch64GlobalsTaggingPass(PassRegistry &)
ModulePass * createAArch64GlobalsTaggingPass()
uint64_t alignTo(uint64_t Size, Align A)
Returns a multiple of A needed to store Size bytes.
Definition: Alignment.h:155
This struct is a compact representation of a valid (non-zero power of two) alignment.
Definition: Alignment.h:39