1 //===--- SanitizerArgs.cpp - Arguments for sanitizer tools ---------------===// 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 #include "clang/Driver/SanitizerArgs.h" 10 #include "clang/Driver/Driver.h" 11 #include "clang/Driver/DriverDiagnostic.h" 12 #include "clang/Driver/Options.h" 13 #include "clang/Driver/ToolChain.h" 14 #include "llvm/ADT/StringExtras.h" 15 #include "llvm/ADT/StringSwitch.h" 16 #include "llvm/Support/FileSystem.h" 17 #include "llvm/Support/Path.h" 18 #include "llvm/Support/SpecialCaseList.h" 19 #include <memory> 20 21 using namespace clang::driver; 22 using namespace llvm::opt; 23 24 namespace { 25 /// Assign ordinals to possible values of -fsanitize= flag. 26 /// We use the ordinal values as bit positions within \c SanitizeKind. 27 enum SanitizeOrdinal { 28 #define SANITIZER(NAME, ID) SO_##ID, 29 #define SANITIZER_GROUP(NAME, ID, ALIAS) SO_##ID##Group, 30 #include "clang/Basic/Sanitizers.def" 31 SO_Count 32 }; 33 34 /// Represents a set of sanitizer kinds. It is also used to define: 35 /// 1) set of sanitizers each sanitizer group expands into. 36 /// 2) set of sanitizers sharing a specific property (e.g. 37 /// all sanitizers with zero-base shadow). 38 enum SanitizeKind { 39 #define SANITIZER(NAME, ID) ID = 1 << SO_##ID, 40 #define SANITIZER_GROUP(NAME, ID, ALIAS) \ 41 ID = ALIAS, ID##Group = 1 << SO_##ID##Group, 42 #include "clang/Basic/Sanitizers.def" 43 NeedsUbsanRt = Undefined | Integer, 44 NotAllowedWithTrap = Vptr, 45 RequiresPIE = Memory | DataFlow, 46 NeedsUnwindTables = Address | Thread | Memory | DataFlow 47 }; 48 } 49 50 /// Returns true if set of \p Sanitizers contain at least one sanitizer from 51 /// \p Kinds. 52 static bool hasOneOf(const clang::SanitizerSet &Sanitizers, unsigned Kinds) { 53 #define SANITIZER(NAME, ID) \ 54 if (Sanitizers.has(clang::SanitizerKind::ID) && (Kinds & ID)) \ 55 return true; 56 #include "clang/Basic/Sanitizers.def" 57 return false; 58 } 59 60 /// Adds all sanitizers from \p Kinds to \p Sanitizers. 61 static void addAllOf(clang::SanitizerSet &Sanitizers, unsigned Kinds) { 62 #define SANITIZER(NAME, ID) \ 63 if (Kinds & ID) \ 64 Sanitizers.set(clang::SanitizerKind::ID, true); 65 #include "clang/Basic/Sanitizers.def" 66 } 67 68 static unsigned toSanitizeKind(clang::SanitizerKind K) { 69 #define SANITIZER(NAME, ID) \ 70 if (K == clang::SanitizerKind::ID) \ 71 return ID; 72 #include "clang/Basic/Sanitizers.def" 73 llvm_unreachable("Invalid SanitizerKind!"); 74 } 75 76 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list. 77 /// Returns a member of the \c SanitizeKind enumeration, or \c 0 78 /// if \p Value is not known. 79 static unsigned parseValue(const char *Value); 80 81 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any 82 /// invalid components. Returns OR of members of \c SanitizeKind enumeration. 83 static unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A, 84 bool DiagnoseErrors); 85 86 /// Parse a single flag of the form -f[no]sanitize=. 87 /// Sets the masks defining required change of the set of sanitizers. 88 /// Returns true if the flag was parsed successfully. 89 static bool parseArgument(const Driver &D, const llvm::opt::Arg *A, 90 unsigned &Add, unsigned &Remove, bool DiagnoseErrors); 91 92 /// Produce an argument string from ArgList \p Args, which shows how it 93 /// provides some sanitizer kind from \p Mask. For example, the argument list 94 /// "-fsanitize=thread,vptr -fsanitize=address" with mask \c NeedsUbsanRt 95 /// would produce "-fsanitize=vptr". 96 static std::string lastArgumentForMask(const Driver &D, 97 const llvm::opt::ArgList &Args, 98 unsigned Mask); 99 100 static std::string lastArgumentForKind(const Driver &D, 101 const llvm::opt::ArgList &Args, 102 clang::SanitizerKind K) { 103 return lastArgumentForMask(D, Args, toSanitizeKind(K)); 104 } 105 106 /// Produce an argument string from argument \p A, which shows how it provides 107 /// a value in \p Mask. For instance, the argument 108 /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce 109 /// "-fsanitize=alignment". 110 static std::string describeSanitizeArg(const llvm::opt::Arg *A, unsigned Mask); 111 112 /// Produce a string containing comma-separated names of sanitizers in \p 113 /// Sanitizers set. 114 static std::string toString(const clang::SanitizerSet &Sanitizers); 115 116 /// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers 117 /// this group enables. 118 static unsigned expandGroups(unsigned Kinds); 119 120 static unsigned getToolchainUnsupportedKinds(const ToolChain &TC) { 121 bool IsFreeBSD = TC.getTriple().getOS() == llvm::Triple::FreeBSD; 122 bool IsLinux = TC.getTriple().getOS() == llvm::Triple::Linux; 123 bool IsX86 = TC.getTriple().getArch() == llvm::Triple::x86; 124 bool IsX86_64 = TC.getTriple().getArch() == llvm::Triple::x86_64; 125 126 unsigned Unsupported = 0; 127 if (!(IsLinux && IsX86_64)) { 128 Unsupported |= Memory | DataFlow; 129 } 130 if (!((IsLinux || IsFreeBSD) && IsX86_64)) { 131 Unsupported |= Thread; 132 } 133 if (!(IsLinux && (IsX86 || IsX86_64))) { 134 Unsupported |= Function; 135 } 136 return Unsupported; 137 } 138 139 bool SanitizerArgs::needsUbsanRt() const { 140 return !UbsanTrapOnError && hasOneOf(Sanitizers, NeedsUbsanRt); 141 } 142 143 bool SanitizerArgs::requiresPIE() const { 144 return AsanZeroBaseShadow || hasOneOf(Sanitizers, RequiresPIE); 145 } 146 147 bool SanitizerArgs::needsUnwindTables() const { 148 return hasOneOf(Sanitizers, NeedsUnwindTables); 149 } 150 151 void SanitizerArgs::clear() { 152 Sanitizers.clear(); 153 SanitizeRecover = false; 154 BlacklistFile = ""; 155 SanitizeCoverage = 0; 156 MsanTrackOrigins = 0; 157 AsanFieldPadding = 0; 158 AsanZeroBaseShadow = false; 159 UbsanTrapOnError = false; 160 AsanSharedRuntime = false; 161 LinkCXXRuntimes = false; 162 } 163 164 SanitizerArgs::SanitizerArgs(const ToolChain &TC, 165 const llvm::opt::ArgList &Args) { 166 clear(); 167 unsigned AllRemove = 0; // During the loop below, the accumulated set of 168 // sanitizers disabled by the current sanitizer 169 // argument or any argument after it. 170 unsigned DiagnosedKinds = 0; // All Kinds we have diagnosed up to now. 171 // Used to deduplicate diagnostics. 172 unsigned Kinds = 0; 173 unsigned NotSupported = getToolchainUnsupportedKinds(TC); 174 const Driver &D = TC.getDriver(); 175 for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend(); 176 I != E; ++I) { 177 unsigned Add, Remove; 178 if (!parseArgument(D, *I, Add, Remove, true)) 179 continue; 180 (*I)->claim(); 181 182 AllRemove |= expandGroups(Remove); 183 184 // Avoid diagnosing any sanitizer which is disabled later. 185 Add &= ~AllRemove; 186 187 // At this point we have not expanded groups, so any unsupported sanitizers 188 // in Add are those which have been explicitly enabled. Diagnose them. 189 if (unsigned KindsToDiagnose = Add & NotSupported & ~DiagnosedKinds) { 190 // Only diagnose the new kinds. 191 std::string Desc = describeSanitizeArg(*I, KindsToDiagnose); 192 D.Diag(diag::err_drv_unsupported_opt_for_target) << Desc 193 << TC.getTriple().str(); 194 DiagnosedKinds |= KindsToDiagnose; 195 } 196 Add &= ~NotSupported; 197 198 Add = expandGroups(Add); 199 // Group expansion may have enabled a sanitizer which is disabled later. 200 Add &= ~AllRemove; 201 // Silently discard any unsupported sanitizers implicitly enabled through 202 // group expansion. 203 Add &= ~NotSupported; 204 205 Kinds |= Add; 206 } 207 addAllOf(Sanitizers, Kinds); 208 209 SanitizeRecover = Args.hasFlag(options::OPT_fsanitize_recover, 210 options::OPT_fno_sanitize_recover, true); 211 212 UbsanTrapOnError = 213 Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, 214 options::OPT_fno_sanitize_undefined_trap_on_error, false); 215 216 // Warn about undefined sanitizer options that require runtime support. 217 if (UbsanTrapOnError && hasOneOf(Sanitizers, NotAllowedWithTrap)) { 218 D.Diag(clang::diag::err_drv_argument_not_allowed_with) 219 << lastArgumentForMask(D, Args, NotAllowedWithTrap) 220 << "-fsanitize-undefined-trap-on-error"; 221 } 222 223 // Check for incompatible sanitizers. 224 bool NeedsAsan = Sanitizers.has(SanitizerKind::Address); 225 bool NeedsTsan = Sanitizers.has(SanitizerKind::Thread); 226 bool NeedsMsan = Sanitizers.has(SanitizerKind::Memory); 227 bool NeedsLsan = Sanitizers.has(SanitizerKind::Leak); 228 if (NeedsAsan && NeedsTsan) 229 D.Diag(clang::diag::err_drv_argument_not_allowed_with) 230 << lastArgumentForKind(D, Args, SanitizerKind::Address) 231 << lastArgumentForKind(D, Args, SanitizerKind::Thread); 232 if (NeedsAsan && NeedsMsan) 233 D.Diag(clang::diag::err_drv_argument_not_allowed_with) 234 << lastArgumentForKind(D, Args, SanitizerKind::Address) 235 << lastArgumentForKind(D, Args, SanitizerKind::Memory); 236 if (NeedsTsan && NeedsMsan) 237 D.Diag(clang::diag::err_drv_argument_not_allowed_with) 238 << lastArgumentForKind(D, Args, SanitizerKind::Thread) 239 << lastArgumentForKind(D, Args, SanitizerKind::Memory); 240 if (NeedsLsan && NeedsTsan) 241 D.Diag(clang::diag::err_drv_argument_not_allowed_with) 242 << lastArgumentForKind(D, Args, SanitizerKind::Leak) 243 << lastArgumentForKind(D, Args, SanitizerKind::Thread); 244 if (NeedsLsan && NeedsMsan) 245 D.Diag(clang::diag::err_drv_argument_not_allowed_with) 246 << lastArgumentForKind(D, Args, SanitizerKind::Leak) 247 << lastArgumentForKind(D, Args, SanitizerKind::Memory); 248 // FIXME: Currently -fsanitize=leak is silently ignored in the presence of 249 // -fsanitize=address. Perhaps it should print an error, or perhaps 250 // -f(-no)sanitize=leak should change whether leak detection is enabled by 251 // default in ASan? 252 253 // Parse -f(no-)sanitize-blacklist options. 254 if (Arg *BLArg = Args.getLastArg(options::OPT_fsanitize_blacklist, 255 options::OPT_fno_sanitize_blacklist)) { 256 if (BLArg->getOption().matches(options::OPT_fsanitize_blacklist)) { 257 std::string BLPath = BLArg->getValue(); 258 if (llvm::sys::fs::exists(BLPath)) { 259 // Validate the blacklist format. 260 std::string BLError; 261 std::unique_ptr<llvm::SpecialCaseList> SCL( 262 llvm::SpecialCaseList::create(BLPath, BLError)); 263 if (!SCL.get()) 264 D.Diag(clang::diag::err_drv_malformed_sanitizer_blacklist) << BLError; 265 else 266 BlacklistFile = BLPath; 267 } else { 268 D.Diag(clang::diag::err_drv_no_such_file) << BLPath; 269 } 270 } 271 } else { 272 // If no -fsanitize-blacklist option is specified, try to look up for 273 // blacklist in the resource directory. 274 std::string BLPath; 275 if (getDefaultBlacklist(D, BLPath) && llvm::sys::fs::exists(BLPath)) 276 BlacklistFile = BLPath; 277 } 278 279 // Parse -f[no-]sanitize-memory-track-origins[=level] options. 280 if (NeedsMsan) { 281 if (Arg *A = 282 Args.getLastArg(options::OPT_fsanitize_memory_track_origins_EQ, 283 options::OPT_fsanitize_memory_track_origins, 284 options::OPT_fno_sanitize_memory_track_origins)) { 285 if (A->getOption().matches(options::OPT_fsanitize_memory_track_origins)) { 286 MsanTrackOrigins = 1; 287 } else if (A->getOption().matches( 288 options::OPT_fno_sanitize_memory_track_origins)) { 289 MsanTrackOrigins = 0; 290 } else { 291 StringRef S = A->getValue(); 292 if (S.getAsInteger(0, MsanTrackOrigins) || MsanTrackOrigins < 0 || 293 MsanTrackOrigins > 2) { 294 D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; 295 } 296 } 297 } 298 } 299 300 // Parse -fsanitize-coverage=N 301 if (NeedsAsan || NeedsMsan) { // Currently asan or msan is required. 302 if (Arg *A = Args.getLastArg(options::OPT_fsanitize_coverage)) { 303 StringRef S = A->getValue(); 304 // Legal values are 0..4. 305 if (S.getAsInteger(0, SanitizeCoverage) || SanitizeCoverage < 0 || 306 SanitizeCoverage > 4) 307 D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; 308 } 309 } 310 311 if (NeedsAsan) { 312 AsanSharedRuntime = 313 Args.hasArg(options::OPT_shared_libasan) || 314 (TC.getTriple().getEnvironment() == llvm::Triple::Android); 315 AsanZeroBaseShadow = 316 (TC.getTriple().getEnvironment() == llvm::Triple::Android); 317 if (Arg *A = 318 Args.getLastArg(options::OPT_fsanitize_address_field_padding)) { 319 StringRef S = A->getValue(); 320 // Legal values are 0 and 1, 2, but in future we may add more levels. 321 if (S.getAsInteger(0, AsanFieldPadding) || AsanFieldPadding < 0 || 322 AsanFieldPadding > 2) { 323 D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S; 324 } 325 } 326 327 if (Arg *WindowsDebugRTArg = 328 Args.getLastArg(options::OPT__SLASH_MTd, options::OPT__SLASH_MT, 329 options::OPT__SLASH_MDd, options::OPT__SLASH_MD, 330 options::OPT__SLASH_LDd, options::OPT__SLASH_LD)) { 331 switch (WindowsDebugRTArg->getOption().getID()) { 332 case options::OPT__SLASH_MTd: 333 case options::OPT__SLASH_MDd: 334 case options::OPT__SLASH_LDd: 335 D.Diag(clang::diag::err_drv_argument_not_allowed_with) 336 << WindowsDebugRTArg->getAsString(Args) 337 << lastArgumentForKind(D, Args, SanitizerKind::Address); 338 D.Diag(clang::diag::note_drv_address_sanitizer_debug_runtime); 339 } 340 } 341 } 342 343 // Parse -link-cxx-sanitizer flag. 344 LinkCXXRuntimes = 345 Args.hasArg(options::OPT_fsanitize_link_cxx_runtime) || D.CCCIsCXX(); 346 } 347 348 static std::string toString(const clang::SanitizerSet &Sanitizers) { 349 std::string Res; 350 #define SANITIZER(NAME, ID) \ 351 if (Sanitizers.has(clang::SanitizerKind::ID)) { \ 352 if (!Res.empty()) \ 353 Res += ","; \ 354 Res += NAME; \ 355 } 356 #include "clang/Basic/Sanitizers.def" 357 return Res; 358 } 359 360 void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args, 361 llvm::opt::ArgStringList &CmdArgs) const { 362 if (Sanitizers.empty()) 363 return; 364 CmdArgs.push_back(Args.MakeArgString("-fsanitize=" + toString(Sanitizers))); 365 366 if (!SanitizeRecover) 367 CmdArgs.push_back("-fno-sanitize-recover"); 368 369 if (UbsanTrapOnError) 370 CmdArgs.push_back("-fsanitize-undefined-trap-on-error"); 371 372 if (!BlacklistFile.empty()) { 373 SmallString<64> BlacklistOpt("-fsanitize-blacklist="); 374 BlacklistOpt += BlacklistFile; 375 CmdArgs.push_back(Args.MakeArgString(BlacklistOpt)); 376 } 377 378 if (MsanTrackOrigins) 379 CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins=" + 380 llvm::utostr(MsanTrackOrigins))); 381 if (AsanFieldPadding) 382 CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" + 383 llvm::utostr(AsanFieldPadding))); 384 if (SanitizeCoverage) 385 CmdArgs.push_back(Args.MakeArgString("-fsanitize-coverage=" + 386 llvm::utostr(SanitizeCoverage))); 387 // Workaround for PR16386. 388 if (Sanitizers.has(SanitizerKind::Memory)) 389 CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new")); 390 } 391 392 bool SanitizerArgs::getDefaultBlacklist(const Driver &D, std::string &BLPath) { 393 const char *BlacklistFile = nullptr; 394 if (Sanitizers.has(SanitizerKind::Address)) 395 BlacklistFile = "asan_blacklist.txt"; 396 else if (Sanitizers.has(SanitizerKind::Memory)) 397 BlacklistFile = "msan_blacklist.txt"; 398 else if (Sanitizers.has(SanitizerKind::Thread)) 399 BlacklistFile = "tsan_blacklist.txt"; 400 else if (Sanitizers.has(SanitizerKind::DataFlow)) 401 BlacklistFile = "dfsan_abilist.txt"; 402 403 if (BlacklistFile) { 404 SmallString<64> Path(D.ResourceDir); 405 llvm::sys::path::append(Path, BlacklistFile); 406 BLPath = Path.str(); 407 return true; 408 } 409 return false; 410 } 411 412 unsigned parseValue(const char *Value) { 413 unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value) 414 #define SANITIZER(NAME, ID) .Case(NAME, ID) 415 #define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group) 416 #include "clang/Basic/Sanitizers.def" 417 .Default(SanitizeKind()); 418 return ParsedKind; 419 } 420 421 unsigned expandGroups(unsigned Kinds) { 422 #define SANITIZER(NAME, ID) 423 #define SANITIZER_GROUP(NAME, ID, ALIAS) if (Kinds & ID##Group) Kinds |= ID; 424 #include "clang/Basic/Sanitizers.def" 425 return Kinds; 426 } 427 428 unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A, 429 bool DiagnoseErrors) { 430 unsigned Kind = 0; 431 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { 432 if (unsigned K = parseValue(A->getValue(I))) 433 Kind |= K; 434 else if (DiagnoseErrors) 435 D.Diag(clang::diag::err_drv_unsupported_option_argument) 436 << A->getOption().getName() << A->getValue(I); 437 } 438 return Kind; 439 } 440 441 bool parseArgument(const Driver &D, const llvm::opt::Arg *A, unsigned &Add, 442 unsigned &Remove, bool DiagnoseErrors) { 443 Add = 0; 444 Remove = 0; 445 if (A->getOption().matches(options::OPT_fsanitize_EQ)) { 446 Add = parseArgValues(D, A, DiagnoseErrors); 447 return true; 448 } 449 if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) { 450 Remove = parseArgValues(D, A, DiagnoseErrors); 451 return true; 452 } 453 return false; 454 } 455 456 std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args, 457 unsigned Mask) { 458 for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(), 459 E = Args.rend(); 460 I != E; ++I) { 461 unsigned Add, Remove; 462 if (parseArgument(D, *I, Add, Remove, false) && 463 (expandGroups(Add) & Mask)) 464 return describeSanitizeArg(*I, Mask); 465 Mask &= ~Remove; 466 } 467 llvm_unreachable("arg list didn't provide expected value"); 468 } 469 470 std::string describeSanitizeArg(const llvm::opt::Arg *A, unsigned Mask) { 471 assert(A->getOption().matches(options::OPT_fsanitize_EQ) 472 && "Invalid argument in describeSanitizerArg!"); 473 474 std::string Sanitizers; 475 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { 476 if (expandGroups(parseValue(A->getValue(I))) & Mask) { 477 if (!Sanitizers.empty()) 478 Sanitizers += ","; 479 Sanitizers += A->getValue(I); 480 } 481 } 482 483 assert(!Sanitizers.empty() && "arg didn't provide expected value"); 484 return "-fsanitize=" + Sanitizers; 485 } 486