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