1 //===--- SanitizerArgs.h - Arguments for sanitizer tools -------*- C++ -*-===// 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 #ifndef CLANG_LIB_DRIVER_SANITIZERARGS_H_ 10 #define CLANG_LIB_DRIVER_SANITIZERARGS_H_ 11 12 #include "clang/Driver/Driver.h" 13 #include "clang/Driver/DriverDiagnostic.h" 14 #include "clang/Driver/Options.h" 15 #include "llvm/ADT/StringSwitch.h" 16 #include "llvm/Option/Arg.h" 17 #include "llvm/Option/ArgList.h" 18 #include "llvm/Support/Path.h" 19 20 namespace clang { 21 namespace driver { 22 23 class SanitizerArgs { 24 /// Assign ordinals to sanitizer flags. We'll use the ordinal values as 25 /// bit positions within \c Kind. 26 enum SanitizeOrdinal { 27 #define SANITIZER(NAME, ID) SO_##ID, 28 #include "clang/Basic/Sanitizers.def" 29 SO_Count 30 }; 31 32 /// Bugs to catch at runtime. 33 enum SanitizeKind { 34 #define SANITIZER(NAME, ID) ID = 1 << SO_##ID, 35 #define SANITIZER_GROUP(NAME, ID, ALIAS) ID = ALIAS, 36 #include "clang/Basic/Sanitizers.def" 37 NeedsAsanRt = Address, 38 NeedsTsanRt = Thread, 39 NeedsMsanRt = Memory, 40 NeedsLeakDetection = Leak, 41 NeedsUbsanRt = Undefined | Integer, 42 NotAllowedWithTrap = Vptr, 43 HasZeroBaseShadow = Thread | Memory 44 }; 45 unsigned Kind; 46 std::string BlacklistFile; 47 bool MsanTrackOrigins; 48 bool AsanZeroBaseShadow; 49 bool UbsanTrapOnError; 50 51 public: 52 SanitizerArgs() : Kind(0), BlacklistFile(""), MsanTrackOrigins(false), 53 AsanZeroBaseShadow(false), UbsanTrapOnError(false) {} 54 /// Parses the sanitizer arguments from an argument list. 55 SanitizerArgs(const ToolChain &TC, const llvm::opt::ArgList &Args); 56 57 bool needsAsanRt() const { return Kind & NeedsAsanRt; } 58 bool needsTsanRt() const { return Kind & NeedsTsanRt; } 59 bool needsMsanRt() const { return Kind & NeedsMsanRt; } 60 bool needsLeakDetection() const { return Kind & NeedsLeakDetection; } 61 bool needsLsanRt() const { 62 return needsLeakDetection() && !needsAsanRt(); 63 } 64 bool needsUbsanRt() const { 65 if (UbsanTrapOnError) 66 return false; 67 return Kind & NeedsUbsanRt; 68 } 69 70 bool sanitizesVptr() const { return Kind & Vptr; } 71 bool notAllowedWithTrap() const { return Kind & NotAllowedWithTrap; } 72 bool hasZeroBaseShadow() const { 73 return (Kind & HasZeroBaseShadow) || AsanZeroBaseShadow; 74 } 75 76 void addArgs(const llvm::opt::ArgList &Args, 77 llvm::opt::ArgStringList &CmdArgs) const { 78 if (!Kind) 79 return; 80 SmallString<256> SanitizeOpt("-fsanitize="); 81 #define SANITIZER(NAME, ID) \ 82 if (Kind & ID) \ 83 SanitizeOpt += NAME ","; 84 #include "clang/Basic/Sanitizers.def" 85 SanitizeOpt.pop_back(); 86 CmdArgs.push_back(Args.MakeArgString(SanitizeOpt)); 87 if (!BlacklistFile.empty()) { 88 SmallString<64> BlacklistOpt("-fsanitize-blacklist="); 89 BlacklistOpt += BlacklistFile; 90 CmdArgs.push_back(Args.MakeArgString(BlacklistOpt)); 91 } 92 93 if (MsanTrackOrigins) 94 CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins")); 95 96 if (AsanZeroBaseShadow) 97 CmdArgs.push_back( 98 Args.MakeArgString("-fsanitize-address-zero-base-shadow")); 99 100 // Workaround for PR16386. 101 if (needsMsanRt()) 102 CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new")); 103 } 104 105 private: 106 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list. 107 /// Returns OR of members of the \c SanitizeKind enumeration, or \c 0 108 /// if \p Value is not known. 109 static unsigned parse(const char *Value) { 110 unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value) 111 #define SANITIZER(NAME, ID) .Case(NAME, ID) 112 #define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID) 113 #include "clang/Basic/Sanitizers.def" 114 .Default(SanitizeKind()); 115 // Assume -fsanitize=address implies -fsanitize=init-order. 116 // FIXME: This should be either specified in Sanitizers.def, or go away when 117 // we get rid of "-fsanitize=init-order" flag at all. 118 if (ParsedKind & Address) 119 ParsedKind |= InitOrder; 120 return ParsedKind; 121 } 122 123 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any 124 /// invalid components. 125 static unsigned parse(const Driver &D, const llvm::opt::Arg *A, 126 bool DiagnoseErrors) { 127 unsigned Kind = 0; 128 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { 129 if (unsigned K = parse(A->getValue(I))) 130 Kind |= K; 131 else if (DiagnoseErrors) 132 D.Diag(diag::err_drv_unsupported_option_argument) 133 << A->getOption().getName() << A->getValue(I); 134 } 135 return Kind; 136 } 137 138 /// Parse a single flag of the form -f[no]sanitize=, or 139 /// -f*-sanitizer. Sets the masks defining required change of Kind value. 140 /// Returns true if the flag was parsed successfully. 141 static bool parse(const Driver &D, const llvm::opt::ArgList &Args, 142 const llvm::opt::Arg *A, unsigned &Add, unsigned &Remove, 143 bool DiagnoseErrors) { 144 Add = 0; 145 Remove = 0; 146 const char *DeprecatedReplacement = 0; 147 if (A->getOption().matches(options::OPT_faddress_sanitizer)) { 148 Add = Address; 149 DeprecatedReplacement = "-fsanitize=address"; 150 } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) { 151 Remove = Address; 152 DeprecatedReplacement = "-fno-sanitize=address"; 153 } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) { 154 Add = Thread; 155 DeprecatedReplacement = "-fsanitize=thread"; 156 } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) { 157 Remove = Thread; 158 DeprecatedReplacement = "-fno-sanitize=thread"; 159 } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) { 160 Add = UndefinedTrap; 161 DeprecatedReplacement = 162 "-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error"; 163 } else if (A->getOption().matches(options::OPT_fbounds_checking) || 164 A->getOption().matches(options::OPT_fbounds_checking_EQ)) { 165 Add = Bounds; 166 DeprecatedReplacement = "-fsanitize=bounds"; 167 } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) { 168 Add = parse(D, A, DiagnoseErrors); 169 } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) { 170 Remove = parse(D, A, DiagnoseErrors); 171 } else { 172 // Flag is not relevant to sanitizers. 173 return false; 174 } 175 // If this is a deprecated synonym, produce a warning directing users 176 // towards the new spelling. 177 if (DeprecatedReplacement && DiagnoseErrors) 178 D.Diag(diag::warn_drv_deprecated_arg) 179 << A->getAsString(Args) << DeprecatedReplacement; 180 return true; 181 } 182 183 /// Produce an argument string from ArgList \p Args, which shows how it 184 /// provides a sanitizer kind in \p Mask. For example, the argument list 185 /// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt 186 /// would produce "-fsanitize=vptr". 187 static std::string lastArgumentForKind(const Driver &D, 188 const llvm::opt::ArgList &Args, 189 unsigned Kind) { 190 for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(), 191 E = Args.rend(); 192 I != E; ++I) { 193 unsigned Add, Remove; 194 if (parse(D, Args, *I, Add, Remove, false) && 195 (Add & Kind)) 196 return describeSanitizeArg(Args, *I, Kind); 197 Kind &= ~Remove; 198 } 199 llvm_unreachable("arg list didn't provide expected value"); 200 } 201 202 /// Produce an argument string from argument \p A, which shows how it provides 203 /// a value in \p Mask. For instance, the argument 204 /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce 205 /// "-fsanitize=alignment". 206 static std::string describeSanitizeArg(const llvm::opt::ArgList &Args, 207 const llvm::opt::Arg *A, 208 unsigned Mask) { 209 if (!A->getOption().matches(options::OPT_fsanitize_EQ)) 210 return A->getAsString(Args); 211 212 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) 213 if (parse(A->getValue(I)) & Mask) 214 return std::string("-fsanitize=") + A->getValue(I); 215 216 llvm_unreachable("arg didn't provide expected value"); 217 } 218 219 static bool getDefaultBlacklistForKind(const Driver &D, unsigned Kind, 220 std::string &BLPath) { 221 const char *BlacklistFile = 0; 222 if (Kind & NeedsAsanRt) 223 BlacklistFile = "asan_blacklist.txt"; 224 else if (Kind & NeedsMsanRt) 225 BlacklistFile = "msan_blacklist.txt"; 226 else if (Kind & NeedsTsanRt) 227 BlacklistFile = "tsan_blacklist.txt"; 228 if (BlacklistFile) { 229 SmallString<64> Path(D.ResourceDir); 230 llvm::sys::path::append(Path, BlacklistFile); 231 BLPath = Path.str(); 232 return true; 233 } 234 return false; 235 } 236 }; 237 238 } // namespace driver 239 } // namespace clang 240 241 #endif // CLANG_LIB_DRIVER_SANITIZERARGS_H_ 242