1243791Sdim//===--- SanitizerArgs.h - Arguments for sanitizer tools -------*- C++ -*-===// 2243791Sdim// 3243791Sdim// The LLVM Compiler Infrastructure 4243791Sdim// 5243791Sdim// This file is distributed under the University of Illinois Open Source 6243791Sdim// License. See LICENSE.TXT for details. 7243791Sdim// 8243791Sdim//===----------------------------------------------------------------------===// 9243791Sdim#ifndef CLANG_LIB_DRIVER_SANITIZERARGS_H_ 10243791Sdim#define CLANG_LIB_DRIVER_SANITIZERARGS_H_ 11243791Sdim 12249423Sdim#include "clang/Driver/Arg.h" 13243791Sdim#include "clang/Driver/ArgList.h" 14249423Sdim#include "clang/Driver/Driver.h" 15249423Sdim#include "clang/Driver/DriverDiagnostic.h" 16249423Sdim#include "clang/Driver/Options.h" 17249423Sdim#include "llvm/ADT/StringSwitch.h" 18249423Sdim#include "llvm/Support/Path.h" 19243791Sdim 20243791Sdimnamespace clang { 21243791Sdimnamespace driver { 22243791Sdim 23243791Sdimclass SanitizerArgs { 24243791Sdim /// Assign ordinals to sanitizer flags. We'll use the ordinal values as 25243791Sdim /// bit positions within \c Kind. 26243791Sdim enum SanitizeOrdinal { 27243791Sdim#define SANITIZER(NAME, ID) SO_##ID, 28243791Sdim#include "clang/Basic/Sanitizers.def" 29243791Sdim SO_Count 30243791Sdim }; 31243791Sdim 32243791Sdim /// Bugs to catch at runtime. 33243791Sdim enum SanitizeKind { 34243791Sdim#define SANITIZER(NAME, ID) ID = 1 << SO_##ID, 35243791Sdim#define SANITIZER_GROUP(NAME, ID, ALIAS) ID = ALIAS, 36243791Sdim#include "clang/Basic/Sanitizers.def" 37243791Sdim NeedsAsanRt = Address, 38243791Sdim NeedsTsanRt = Thread, 39249423Sdim NeedsMsanRt = Memory, 40249423Sdim NeedsUbsanRt = Undefined | Integer, 41251662Sdim NotAllowedWithTrap = Vptr, 42251662Sdim HasZeroBaseShadow = Thread | Memory 43243791Sdim }; 44243791Sdim unsigned Kind; 45249423Sdim std::string BlacklistFile; 46249423Sdim bool MsanTrackOrigins; 47249423Sdim bool AsanZeroBaseShadow; 48249423Sdim bool UbsanTrapOnError; 49243791Sdim 50243791Sdim public: 51249423Sdim SanitizerArgs() : Kind(0), BlacklistFile(""), MsanTrackOrigins(false), 52249423Sdim AsanZeroBaseShadow(false), UbsanTrapOnError(false) {} 53243791Sdim /// Parses the sanitizer arguments from an argument list. 54251662Sdim SanitizerArgs(const ToolChain &TC, const ArgList &Args); 55243791Sdim 56243791Sdim bool needsAsanRt() const { return Kind & NeedsAsanRt; } 57243791Sdim bool needsTsanRt() const { return Kind & NeedsTsanRt; } 58249423Sdim bool needsMsanRt() const { return Kind & NeedsMsanRt; } 59249423Sdim bool needsUbsanRt() const { 60249423Sdim if (UbsanTrapOnError) 61249423Sdim return false; 62249423Sdim return Kind & NeedsUbsanRt; 63249423Sdim } 64243791Sdim 65243791Sdim bool sanitizesVptr() const { return Kind & Vptr; } 66249423Sdim bool notAllowedWithTrap() const { return Kind & NotAllowedWithTrap; } 67251662Sdim bool hasZeroBaseShadow() const { 68251662Sdim return (Kind & HasZeroBaseShadow) || AsanZeroBaseShadow; 69251662Sdim } 70249423Sdim 71243791Sdim void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const { 72243791Sdim if (!Kind) 73243791Sdim return; 74249423Sdim SmallString<256> SanitizeOpt("-fsanitize="); 75243791Sdim#define SANITIZER(NAME, ID) \ 76243791Sdim if (Kind & ID) \ 77243791Sdim SanitizeOpt += NAME ","; 78243791Sdim#include "clang/Basic/Sanitizers.def" 79243791Sdim SanitizeOpt.pop_back(); 80243791Sdim CmdArgs.push_back(Args.MakeArgString(SanitizeOpt)); 81249423Sdim if (!BlacklistFile.empty()) { 82249423Sdim SmallString<64> BlacklistOpt("-fsanitize-blacklist="); 83249423Sdim BlacklistOpt += BlacklistFile; 84249423Sdim CmdArgs.push_back(Args.MakeArgString(BlacklistOpt)); 85249423Sdim } 86249423Sdim 87249423Sdim if (MsanTrackOrigins) 88249423Sdim CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins")); 89249423Sdim 90249423Sdim if (AsanZeroBaseShadow) 91249423Sdim CmdArgs.push_back(Args.MakeArgString( 92249423Sdim "-fsanitize-address-zero-base-shadow")); 93243791Sdim } 94243791Sdim 95243791Sdim private: 96243791Sdim /// Parse a single value from a -fsanitize= or -fno-sanitize= value list. 97249423Sdim /// Returns OR of members of the \c SanitizeKind enumeration, or \c 0 98249423Sdim /// if \p Value is not known. 99243791Sdim static unsigned parse(const char *Value) { 100249423Sdim unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value) 101243791Sdim#define SANITIZER(NAME, ID) .Case(NAME, ID) 102243791Sdim#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID) 103243791Sdim#include "clang/Basic/Sanitizers.def" 104243791Sdim .Default(SanitizeKind()); 105249423Sdim // Assume -fsanitize=address implies -fsanitize=init-order. 106249423Sdim // FIXME: This should be either specified in Sanitizers.def, or go away when 107249423Sdim // we get rid of "-fsanitize=init-order" flag at all. 108249423Sdim if (ParsedKind & Address) 109249423Sdim ParsedKind |= InitOrder; 110249423Sdim return ParsedKind; 111243791Sdim } 112243791Sdim 113243791Sdim /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any 114243791Sdim /// invalid components. 115249423Sdim static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) { 116243791Sdim unsigned Kind = 0; 117243791Sdim for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) { 118243791Sdim if (unsigned K = parse(A->getValue(I))) 119243791Sdim Kind |= K; 120249423Sdim else if (DiagnoseErrors) 121243791Sdim D.Diag(diag::err_drv_unsupported_option_argument) 122243791Sdim << A->getOption().getName() << A->getValue(I); 123243791Sdim } 124243791Sdim return Kind; 125243791Sdim } 126243791Sdim 127249423Sdim /// Parse a single flag of the form -f[no]sanitize=, or 128249423Sdim /// -f*-sanitizer. Sets the masks defining required change of Kind value. 129249423Sdim /// Returns true if the flag was parsed successfully. 130249423Sdim static bool parse(const Driver &D, const ArgList &Args, const Arg *A, 131249423Sdim unsigned &Add, unsigned &Remove, bool DiagnoseErrors) { 132249423Sdim Add = 0; 133249423Sdim Remove = 0; 134249423Sdim const char *DeprecatedReplacement = 0; 135249423Sdim if (A->getOption().matches(options::OPT_faddress_sanitizer)) { 136249423Sdim Add = Address; 137249423Sdim DeprecatedReplacement = "-fsanitize=address"; 138249423Sdim } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) { 139249423Sdim Remove = Address; 140249423Sdim DeprecatedReplacement = "-fno-sanitize=address"; 141249423Sdim } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) { 142249423Sdim Add = Thread; 143249423Sdim DeprecatedReplacement = "-fsanitize=thread"; 144249423Sdim } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) { 145249423Sdim Remove = Thread; 146249423Sdim DeprecatedReplacement = "-fno-sanitize=thread"; 147249423Sdim } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) { 148249423Sdim Add = UndefinedTrap; 149249423Sdim DeprecatedReplacement = 150249423Sdim "-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error"; 151249423Sdim } else if (A->getOption().matches(options::OPT_fbounds_checking) || 152249423Sdim A->getOption().matches(options::OPT_fbounds_checking_EQ)) { 153249423Sdim Add = Bounds; 154249423Sdim DeprecatedReplacement = "-fsanitize=bounds"; 155249423Sdim } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) { 156249423Sdim Add = parse(D, A, DiagnoseErrors); 157249423Sdim } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) { 158249423Sdim Remove = parse(D, A, DiagnoseErrors); 159249423Sdim } else { 160249423Sdim // Flag is not relevant to sanitizers. 161249423Sdim return false; 162249423Sdim } 163249423Sdim // If this is a deprecated synonym, produce a warning directing users 164249423Sdim // towards the new spelling. 165249423Sdim if (DeprecatedReplacement && DiagnoseErrors) 166249423Sdim D.Diag(diag::warn_drv_deprecated_arg) 167249423Sdim << A->getAsString(Args) << DeprecatedReplacement; 168249423Sdim return true; 169249423Sdim } 170249423Sdim 171249423Sdim /// Produce an argument string from ArgList \p Args, which shows how it 172249423Sdim /// provides a sanitizer kind in \p Mask. For example, the argument list 173249423Sdim /// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt 174249423Sdim /// would produce "-fsanitize=vptr". 175249423Sdim static std::string lastArgumentForKind(const Driver &D, const ArgList &Args, 176249423Sdim unsigned Kind) { 177249423Sdim for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend(); 178249423Sdim I != E; ++I) { 179249423Sdim unsigned Add, Remove; 180249423Sdim if (parse(D, Args, *I, Add, Remove, false) && 181249423Sdim (Add & Kind)) 182249423Sdim return describeSanitizeArg(Args, *I, Kind); 183249423Sdim Kind &= ~Remove; 184249423Sdim } 185249423Sdim llvm_unreachable("arg list didn't provide expected value"); 186249423Sdim } 187249423Sdim 188243791Sdim /// Produce an argument string from argument \p A, which shows how it provides 189243791Sdim /// a value in \p Mask. For instance, the argument 190243791Sdim /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce 191243791Sdim /// "-fsanitize=alignment". 192243791Sdim static std::string describeSanitizeArg(const ArgList &Args, const Arg *A, 193243791Sdim unsigned Mask) { 194243791Sdim if (!A->getOption().matches(options::OPT_fsanitize_EQ)) 195243791Sdim return A->getAsString(Args); 196243791Sdim 197243791Sdim for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) 198243791Sdim if (parse(A->getValue(I)) & Mask) 199243791Sdim return std::string("-fsanitize=") + A->getValue(I); 200243791Sdim 201243791Sdim llvm_unreachable("arg didn't provide expected value"); 202243791Sdim } 203249423Sdim 204249423Sdim static bool getDefaultBlacklistForKind(const Driver &D, unsigned Kind, 205249423Sdim std::string &BLPath) { 206249423Sdim // For now, specify the default blacklist location for ASan only. 207249423Sdim if (Kind & NeedsAsanRt) { 208249423Sdim SmallString<64> Path(D.ResourceDir); 209249423Sdim llvm::sys::path::append(Path, "asan_blacklist.txt"); 210249423Sdim BLPath = Path.str(); 211249423Sdim return true; 212249423Sdim } 213249423Sdim return false; 214249423Sdim } 215243791Sdim}; 216243791Sdim 217243791Sdim} // namespace driver 218243791Sdim} // namespace clang 219243791Sdim 220243791Sdim#endif // CLANG_LIB_DRIVER_SANITIZERARGS_H_ 221