1259701Sdim//===--- SanitizerArgs.cpp - Arguments for sanitizer tools ---------------===// 2259701Sdim// 3259701Sdim// The LLVM Compiler Infrastructure 4259701Sdim// 5259701Sdim// This file is distributed under the University of Illinois Open Source 6259701Sdim// License. See LICENSE.TXT for details. 7259701Sdim// 8259701Sdim//===----------------------------------------------------------------------===// 9259701Sdim#include "clang/Driver/SanitizerArgs.h" 10259701Sdim 11259701Sdim#include "clang/Driver/Driver.h" 12259701Sdim#include "clang/Driver/DriverDiagnostic.h" 13259701Sdim#include "clang/Driver/Options.h" 14259701Sdim#include "clang/Driver/ToolChain.h" 15259701Sdim#include "llvm/ADT/OwningPtr.h" 16259701Sdim#include "llvm/ADT/StringSwitch.h" 17259701Sdim#include "llvm/Support/FileSystem.h" 18259701Sdim#include "llvm/Support/Path.h" 19259701Sdim#include "llvm/Transforms/Utils/SpecialCaseList.h" 20259701Sdim 21259701Sdimusing namespace clang::driver; 22259701Sdimusing namespace llvm::opt; 23259701Sdim 24259701Sdimvoid SanitizerArgs::clear() { 25259701Sdim Kind = 0; 26259701Sdim BlacklistFile = ""; 27259701Sdim MsanTrackOrigins = false; 28259701Sdim AsanZeroBaseShadow = false; 29259701Sdim UbsanTrapOnError = false; 30259701Sdim} 31259701Sdim 32259701SdimSanitizerArgs::SanitizerArgs() { 33259701Sdim clear(); 34259701Sdim} 35259701Sdim 36259701SdimSanitizerArgs::SanitizerArgs(const ToolChain &TC, 37259701Sdim const llvm::opt::ArgList &Args) { 38259701Sdim clear(); 39259701Sdim unsigned AllAdd = 0; // All kinds of sanitizers that were turned on 40259701Sdim // at least once (possibly, disabled further). 41259701Sdim unsigned AllRemove = 0; // During the loop below, the accumulated set of 42259701Sdim // sanitizers disabled by the current sanitizer 43259701Sdim // argument or any argument after it. 44259701Sdim unsigned DiagnosedKinds = 0; // All Kinds we have diagnosed up to now. 45259701Sdim // Used to deduplicate diagnostics. 46259701Sdim const Driver &D = TC.getDriver(); 47259701Sdim for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend(); 48259701Sdim I != E; ++I) { 49259701Sdim unsigned Add, Remove; 50259701Sdim if (!parse(D, Args, *I, Add, Remove, true)) 51259701Sdim continue; 52259701Sdim (*I)->claim(); 53259701Sdim 54259701Sdim AllAdd |= expandGroups(Add); 55259701Sdim AllRemove |= expandGroups(Remove); 56259701Sdim 57259701Sdim // Avoid diagnosing any sanitizer which is disabled later. 58259701Sdim Add &= ~AllRemove; 59259701Sdim // At this point we have not expanded groups, so any unsupported sanitizers 60259701Sdim // in Add are those which have been explicitly enabled. Diagnose them. 61259701Sdim Add = filterUnsupportedKinds(TC, Add, Args, *I, /*DiagnoseErrors=*/true, 62259701Sdim DiagnosedKinds); 63259701Sdim Add = expandGroups(Add); 64259701Sdim // Group expansion may have enabled a sanitizer which is disabled later. 65259701Sdim Add &= ~AllRemove; 66259701Sdim // Silently discard any unsupported sanitizers implicitly enabled through 67259701Sdim // group expansion. 68259701Sdim Add = filterUnsupportedKinds(TC, Add, Args, *I, /*DiagnoseErrors=*/false, 69259701Sdim DiagnosedKinds); 70259701Sdim 71259701Sdim Kind |= Add; 72259701Sdim } 73259701Sdim 74259701Sdim UbsanTrapOnError = 75259701Sdim Args.hasArg(options::OPT_fcatch_undefined_behavior) || 76259701Sdim Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, 77259701Sdim options::OPT_fno_sanitize_undefined_trap_on_error, false); 78259701Sdim 79259701Sdim if (Args.hasArg(options::OPT_fcatch_undefined_behavior) && 80259701Sdim !Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, 81259701Sdim options::OPT_fno_sanitize_undefined_trap_on_error, true)) { 82259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 83259701Sdim << "-fcatch-undefined-behavior" 84259701Sdim << "-fno-sanitize-undefined-trap-on-error"; 85259701Sdim } 86259701Sdim 87259701Sdim // Warn about undefined sanitizer options that require runtime support. 88259701Sdim if (UbsanTrapOnError && notAllowedWithTrap()) { 89259701Sdim if (Args.hasArg(options::OPT_fcatch_undefined_behavior)) 90259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 91259701Sdim << lastArgumentForKind(D, Args, NotAllowedWithTrap) 92259701Sdim << "-fcatch-undefined-behavior"; 93259701Sdim else if (Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error, 94259701Sdim options::OPT_fno_sanitize_undefined_trap_on_error, 95259701Sdim false)) 96259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 97259701Sdim << lastArgumentForKind(D, Args, NotAllowedWithTrap) 98259701Sdim << "-fsanitize-undefined-trap-on-error"; 99259701Sdim } 100259701Sdim 101259701Sdim // Only one runtime library can be used at once. 102259701Sdim bool NeedsAsan = needsAsanRt(); 103259701Sdim bool NeedsTsan = needsTsanRt(); 104259701Sdim bool NeedsMsan = needsMsanRt(); 105259701Sdim bool NeedsLsan = needsLeakDetection(); 106259701Sdim if (NeedsAsan && NeedsTsan) 107259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 108259701Sdim << lastArgumentForKind(D, Args, NeedsAsanRt) 109259701Sdim << lastArgumentForKind(D, Args, NeedsTsanRt); 110259701Sdim if (NeedsAsan && NeedsMsan) 111259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 112259701Sdim << lastArgumentForKind(D, Args, NeedsAsanRt) 113259701Sdim << lastArgumentForKind(D, Args, NeedsMsanRt); 114259701Sdim if (NeedsTsan && NeedsMsan) 115259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 116259701Sdim << lastArgumentForKind(D, Args, NeedsTsanRt) 117259701Sdim << lastArgumentForKind(D, Args, NeedsMsanRt); 118259701Sdim if (NeedsLsan && NeedsTsan) 119259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 120259701Sdim << lastArgumentForKind(D, Args, NeedsLeakDetection) 121259701Sdim << lastArgumentForKind(D, Args, NeedsTsanRt); 122259701Sdim if (NeedsLsan && NeedsMsan) 123259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 124259701Sdim << lastArgumentForKind(D, Args, NeedsLeakDetection) 125259701Sdim << lastArgumentForKind(D, Args, NeedsMsanRt); 126259701Sdim // FIXME: Currenly -fsanitize=leak is silently ignored in the presence of 127259701Sdim // -fsanitize=address. Perhaps it should print an error, or perhaps 128259701Sdim // -f(-no)sanitize=leak should change whether leak detection is enabled by 129259701Sdim // default in ASan? 130259701Sdim 131259701Sdim // If -fsanitize contains extra features of ASan, it should also 132259701Sdim // explicitly contain -fsanitize=address (probably, turned off later in the 133259701Sdim // command line). 134259701Sdim if ((Kind & AddressFull) != 0 && (AllAdd & Address) == 0) 135259701Sdim D.Diag(diag::warn_drv_unused_sanitizer) 136259701Sdim << lastArgumentForKind(D, Args, AddressFull) 137259701Sdim << "-fsanitize=address"; 138259701Sdim 139259701Sdim // Parse -f(no-)sanitize-blacklist options. 140259701Sdim if (Arg *BLArg = Args.getLastArg(options::OPT_fsanitize_blacklist, 141259701Sdim options::OPT_fno_sanitize_blacklist)) { 142259701Sdim if (BLArg->getOption().matches(options::OPT_fsanitize_blacklist)) { 143259701Sdim std::string BLPath = BLArg->getValue(); 144259701Sdim if (llvm::sys::fs::exists(BLPath)) { 145259701Sdim // Validate the blacklist format. 146259701Sdim std::string BLError; 147259701Sdim llvm::OwningPtr<llvm::SpecialCaseList> SCL( 148259701Sdim llvm::SpecialCaseList::create(BLPath, BLError)); 149259701Sdim if (!SCL.get()) 150259701Sdim D.Diag(diag::err_drv_malformed_sanitizer_blacklist) << BLError; 151259701Sdim else 152259701Sdim BlacklistFile = BLPath; 153259701Sdim } else { 154259701Sdim D.Diag(diag::err_drv_no_such_file) << BLPath; 155259701Sdim } 156259701Sdim } 157259701Sdim } else { 158259701Sdim // If no -fsanitize-blacklist option is specified, try to look up for 159259701Sdim // blacklist in the resource directory. 160259701Sdim std::string BLPath; 161259701Sdim if (getDefaultBlacklistForKind(D, Kind, BLPath) && 162259701Sdim llvm::sys::fs::exists(BLPath)) 163259701Sdim BlacklistFile = BLPath; 164259701Sdim } 165259701Sdim 166259701Sdim // Parse -f(no-)sanitize-memory-track-origins options. 167259701Sdim if (NeedsMsan) 168259701Sdim MsanTrackOrigins = 169259701Sdim Args.hasFlag(options::OPT_fsanitize_memory_track_origins, 170259701Sdim options::OPT_fno_sanitize_memory_track_origins, 171259701Sdim /* Default */false); 172259701Sdim 173259701Sdim // Parse -f(no-)sanitize-address-zero-base-shadow options. 174259701Sdim if (NeedsAsan) { 175259701Sdim bool IsAndroid = (TC.getTriple().getEnvironment() == llvm::Triple::Android); 176259701Sdim bool ZeroBaseShadowDefault = IsAndroid; 177259701Sdim AsanZeroBaseShadow = 178259701Sdim Args.hasFlag(options::OPT_fsanitize_address_zero_base_shadow, 179259701Sdim options::OPT_fno_sanitize_address_zero_base_shadow, 180259701Sdim ZeroBaseShadowDefault); 181259701Sdim // Zero-base shadow is a requirement on Android. 182259701Sdim if (IsAndroid && !AsanZeroBaseShadow) { 183259701Sdim D.Diag(diag::err_drv_argument_not_allowed_with) 184259701Sdim << "-fno-sanitize-address-zero-base-shadow" 185259701Sdim << lastArgumentForKind(D, Args, Address); 186259701Sdim } 187259701Sdim } 188259701Sdim} 189259701Sdim 190259701Sdimvoid SanitizerArgs::addArgs(const llvm::opt::ArgList &Args, 191259701Sdim llvm::opt::ArgStringList &CmdArgs) const { 192259701Sdim if (!Kind) 193259701Sdim return; 194259701Sdim SmallString<256> SanitizeOpt("-fsanitize="); 195259701Sdim#define SANITIZER(NAME, ID) \ 196259701Sdim if (Kind & ID) \ 197259701Sdim SanitizeOpt += NAME ","; 198259701Sdim#include "clang/Basic/Sanitizers.def" 199259701Sdim SanitizeOpt.pop_back(); 200259701Sdim CmdArgs.push_back(Args.MakeArgString(SanitizeOpt)); 201259701Sdim if (!BlacklistFile.empty()) { 202259701Sdim SmallString<64> BlacklistOpt("-fsanitize-blacklist="); 203259701Sdim BlacklistOpt += BlacklistFile; 204259701Sdim CmdArgs.push_back(Args.MakeArgString(BlacklistOpt)); 205259701Sdim } 206259701Sdim 207259701Sdim if (MsanTrackOrigins) 208259701Sdim CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins")); 209259701Sdim 210259701Sdim if (AsanZeroBaseShadow) 211259701Sdim CmdArgs.push_back( 212259701Sdim Args.MakeArgString("-fsanitize-address-zero-base-shadow")); 213259701Sdim 214259701Sdim // Workaround for PR16386. 215259701Sdim if (needsMsanRt()) 216259701Sdim CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new")); 217259701Sdim} 218259701Sdim 219259701Sdimunsigned SanitizerArgs::parse(const char *Value) { 220259701Sdim unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value) 221259701Sdim#define SANITIZER(NAME, ID) .Case(NAME, ID) 222259701Sdim#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group) 223259701Sdim#include "clang/Basic/Sanitizers.def" 224259701Sdim .Default(SanitizeKind()); 225259701Sdim // Assume -fsanitize=address implies -fsanitize=init-order,use-after-return. 226259701Sdim // FIXME: This should be either specified in Sanitizers.def, or go away when 227259701Sdim // we get rid of "-fsanitize=init-order,use-after-return" flags at all. 228259701Sdim if (ParsedKind & Address) 229259701Sdim ParsedKind |= InitOrder | UseAfterReturn; 230259701Sdim return ParsedKind; 231259701Sdim} 232259701Sdim 233259701Sdimunsigned SanitizerArgs::expandGroups(unsigned Kinds) { 234259701Sdim#define SANITIZER(NAME, ID) 235259701Sdim#define SANITIZER_GROUP(NAME, ID, ALIAS) if (Kinds & ID##Group) Kinds |= ID; 236259701Sdim#include "clang/Basic/Sanitizers.def" 237259701Sdim return Kinds; 238259701Sdim} 239259701Sdim 240259701Sdimvoid SanitizerArgs::filterUnsupportedMask(const ToolChain &TC, unsigned &Kinds, 241259701Sdim unsigned Mask, 242259701Sdim const llvm::opt::ArgList &Args, 243259701Sdim const llvm::opt::Arg *A, 244259701Sdim bool DiagnoseErrors, 245259701Sdim unsigned &DiagnosedKinds) { 246259701Sdim unsigned MaskedKinds = Kinds & Mask; 247259701Sdim if (!MaskedKinds) 248259701Sdim return; 249259701Sdim Kinds &= ~Mask; 250259701Sdim // Do we have new kinds to diagnose? 251259701Sdim if (DiagnoseErrors && (DiagnosedKinds & MaskedKinds) != MaskedKinds) { 252259701Sdim // Only diagnose the new kinds. 253259701Sdim std::string Desc = 254259701Sdim describeSanitizeArg(Args, A, MaskedKinds & ~DiagnosedKinds); 255259701Sdim TC.getDriver().Diag(diag::err_drv_unsupported_opt_for_target) 256259701Sdim << Desc << TC.getTriple().str(); 257259701Sdim DiagnosedKinds |= MaskedKinds; 258259701Sdim } 259259701Sdim} 260259701Sdim 261259701Sdimunsigned SanitizerArgs::filterUnsupportedKinds(const ToolChain &TC, 262259701Sdim unsigned Kinds, 263259701Sdim const llvm::opt::ArgList &Args, 264259701Sdim const llvm::opt::Arg *A, 265259701Sdim bool DiagnoseErrors, 266259701Sdim unsigned &DiagnosedKinds) { 267259701Sdim bool IsLinux = TC.getTriple().getOS() == llvm::Triple::Linux; 268259701Sdim bool IsX86 = TC.getTriple().getArch() == llvm::Triple::x86; 269259701Sdim bool IsX86_64 = TC.getTriple().getArch() == llvm::Triple::x86_64; 270259701Sdim if (!(IsLinux && IsX86_64)) { 271259701Sdim filterUnsupportedMask(TC, Kinds, Thread | Memory | DataFlow, Args, A, 272259701Sdim DiagnoseErrors, DiagnosedKinds); 273259701Sdim } 274259701Sdim if (!(IsLinux && (IsX86 || IsX86_64))) { 275259701Sdim filterUnsupportedMask(TC, Kinds, Function, Args, A, DiagnoseErrors, 276259701Sdim DiagnosedKinds); 277259701Sdim } 278259701Sdim return Kinds; 279259701Sdim} 280259701Sdim 281259701Sdimunsigned SanitizerArgs::parse(const Driver &D, const llvm::opt::Arg *A, 282259701Sdim bool DiagnoseErrors) { 283259701Sdim unsigned Kind = 0; 284259701Sdim for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { 285259701Sdim if (unsigned K = parse(A->getValue(I))) 286259701Sdim Kind |= K; 287259701Sdim else if (DiagnoseErrors) 288259701Sdim D.Diag(diag::err_drv_unsupported_option_argument) 289259701Sdim << A->getOption().getName() << A->getValue(I); 290259701Sdim } 291259701Sdim return Kind; 292259701Sdim} 293259701Sdim 294259701Sdimbool SanitizerArgs::parse(const Driver &D, const llvm::opt::ArgList &Args, 295259701Sdim const llvm::opt::Arg *A, unsigned &Add, 296259701Sdim unsigned &Remove, bool DiagnoseErrors) { 297259701Sdim Add = 0; 298259701Sdim Remove = 0; 299259701Sdim const char *DeprecatedReplacement = 0; 300259701Sdim if (A->getOption().matches(options::OPT_faddress_sanitizer)) { 301259701Sdim Add = Address; 302259701Sdim DeprecatedReplacement = "-fsanitize=address"; 303259701Sdim } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) { 304259701Sdim Remove = Address; 305259701Sdim DeprecatedReplacement = "-fno-sanitize=address"; 306259701Sdim } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) { 307259701Sdim Add = Thread; 308259701Sdim DeprecatedReplacement = "-fsanitize=thread"; 309259701Sdim } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) { 310259701Sdim Remove = Thread; 311259701Sdim DeprecatedReplacement = "-fno-sanitize=thread"; 312259701Sdim } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) { 313259701Sdim Add = UndefinedTrap; 314259701Sdim DeprecatedReplacement = 315259701Sdim "-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error"; 316259701Sdim } else if (A->getOption().matches(options::OPT_fbounds_checking) || 317259701Sdim A->getOption().matches(options::OPT_fbounds_checking_EQ)) { 318259701Sdim Add = LocalBounds; 319259701Sdim DeprecatedReplacement = "-fsanitize=local-bounds"; 320259701Sdim } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) { 321259701Sdim Add = parse(D, A, DiagnoseErrors); 322259701Sdim } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) { 323259701Sdim Remove = parse(D, A, DiagnoseErrors); 324259701Sdim } else { 325259701Sdim // Flag is not relevant to sanitizers. 326259701Sdim return false; 327259701Sdim } 328259701Sdim // If this is a deprecated synonym, produce a warning directing users 329259701Sdim // towards the new spelling. 330259701Sdim if (DeprecatedReplacement && DiagnoseErrors) 331259701Sdim D.Diag(diag::warn_drv_deprecated_arg) 332259701Sdim << A->getAsString(Args) << DeprecatedReplacement; 333259701Sdim return true; 334259701Sdim} 335259701Sdim 336259701Sdimstd::string SanitizerArgs::lastArgumentForKind(const Driver &D, 337259701Sdim const llvm::opt::ArgList &Args, 338259701Sdim unsigned Kind) { 339259701Sdim for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(), 340259701Sdim E = Args.rend(); 341259701Sdim I != E; ++I) { 342259701Sdim unsigned Add, Remove; 343259701Sdim if (parse(D, Args, *I, Add, Remove, false) && 344259701Sdim (expandGroups(Add) & Kind)) 345259701Sdim return describeSanitizeArg(Args, *I, Kind); 346259701Sdim Kind &= ~Remove; 347259701Sdim } 348259701Sdim llvm_unreachable("arg list didn't provide expected value"); 349259701Sdim} 350259701Sdim 351259701Sdimstd::string SanitizerArgs::describeSanitizeArg(const llvm::opt::ArgList &Args, 352259701Sdim const llvm::opt::Arg *A, 353259701Sdim unsigned Mask) { 354259701Sdim if (!A->getOption().matches(options::OPT_fsanitize_EQ)) 355259701Sdim return A->getAsString(Args); 356259701Sdim 357259701Sdim std::string Sanitizers; 358259701Sdim for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { 359259701Sdim if (expandGroups(parse(A->getValue(I))) & Mask) { 360259701Sdim if (!Sanitizers.empty()) 361259701Sdim Sanitizers += ","; 362259701Sdim Sanitizers += A->getValue(I); 363259701Sdim } 364259701Sdim } 365259701Sdim 366259701Sdim assert(!Sanitizers.empty() && "arg didn't provide expected value"); 367259701Sdim return "-fsanitize=" + Sanitizers; 368259701Sdim} 369259701Sdim 370259701Sdimbool SanitizerArgs::getDefaultBlacklistForKind(const Driver &D, unsigned Kind, 371259701Sdim std::string &BLPath) { 372259701Sdim const char *BlacklistFile = 0; 373259701Sdim if (Kind & NeedsAsanRt) 374259701Sdim BlacklistFile = "asan_blacklist.txt"; 375259701Sdim else if (Kind & NeedsMsanRt) 376259701Sdim BlacklistFile = "msan_blacklist.txt"; 377259701Sdim else if (Kind & NeedsTsanRt) 378259701Sdim BlacklistFile = "tsan_blacklist.txt"; 379259701Sdim else if (Kind & NeedsDfsanRt) 380259701Sdim BlacklistFile = "dfsan_abilist.txt"; 381259701Sdim 382259701Sdim if (BlacklistFile) { 383259701Sdim SmallString<64> Path(D.ResourceDir); 384259701Sdim llvm::sys::path::append(Path, BlacklistFile); 385259701Sdim BLPath = Path.str(); 386259701Sdim return true; 387259701Sdim } 388259701Sdim return false; 389259701Sdim} 390