Home | History | Annotate | Download | only in Driver
      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