xref: /llvm-project/clang/lib/CodeGen/SanitizerMetadata.cpp (revision 473cdb93e55deeea6a7f654e192f5227d85cee08)
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