SanitizerArgs.cpp revision f064ad5beaaa133752135456b33ded1c18b5d918
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
11#include "clang/Driver/Driver.h"
12#include "clang/Driver/DriverDiagnostic.h"
13#include "clang/Driver/Options.h"
14#include "clang/Driver/ToolChain.h"
15#include "llvm/ADT/OwningPtr.h"
16#include "llvm/ADT/StringSwitch.h"
17#include "llvm/Support/FileSystem.h"
18#include "llvm/Support/Path.h"
19#include "llvm/Transforms/Utils/SpecialCaseList.h"
20
21using namespace clang::driver;
22using namespace llvm::opt;
23
24void SanitizerArgs::clear() {
25  Kind = 0;
26  BlacklistFile = "";
27  MsanTrackOrigins = false;
28  AsanZeroBaseShadow = AZBSK_Default;
29  UbsanTrapOnError = false;
30}
31
32SanitizerArgs::SanitizerArgs() {
33  clear();
34}
35
36SanitizerArgs::SanitizerArgs(const Driver &D, const llvm::opt::ArgList &Args) {
37  clear();
38  unsigned AllKinds = 0;  // All kinds of sanitizers that were turned on
39                          // at least once (possibly, disabled further).
40  for (ArgList::const_iterator I = Args.begin(), E = Args.end(); I != E; ++I) {
41    unsigned Add, Remove;
42    if (!parse(D, Args, *I, Add, Remove, true))
43      continue;
44    (*I)->claim();
45    Kind |= Add;
46    Kind &= ~Remove;
47    AllKinds |= Add;
48  }
49
50  UbsanTrapOnError =
51    Args.hasArg(options::OPT_fcatch_undefined_behavior) ||
52    Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error,
53                 options::OPT_fno_sanitize_undefined_trap_on_error, false);
54
55  if (Args.hasArg(options::OPT_fcatch_undefined_behavior) &&
56      !Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error,
57                    options::OPT_fno_sanitize_undefined_trap_on_error, true)) {
58    D.Diag(diag::err_drv_argument_not_allowed_with)
59      << "-fcatch-undefined-behavior"
60      << "-fno-sanitize-undefined-trap-on-error";
61  }
62
63  // Warn about undefined sanitizer options that require runtime support.
64  if (UbsanTrapOnError && notAllowedWithTrap()) {
65    if (Args.hasArg(options::OPT_fcatch_undefined_behavior))
66      D.Diag(diag::err_drv_argument_not_allowed_with)
67        << lastArgumentForKind(D, Args, NotAllowedWithTrap)
68        << "-fcatch-undefined-behavior";
69    else if (Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error,
70                          options::OPT_fno_sanitize_undefined_trap_on_error,
71                          false))
72      D.Diag(diag::err_drv_argument_not_allowed_with)
73        << lastArgumentForKind(D, Args, NotAllowedWithTrap)
74        << "-fsanitize-undefined-trap-on-error";
75  }
76
77  // Only one runtime library can be used at once.
78  bool NeedsAsan = needsAsanRt();
79  bool NeedsTsan = needsTsanRt();
80  bool NeedsMsan = needsMsanRt();
81  bool NeedsLsan = needsLeakDetection();
82  if (NeedsAsan && NeedsTsan)
83    D.Diag(diag::err_drv_argument_not_allowed_with)
84      << lastArgumentForKind(D, Args, NeedsAsanRt)
85      << lastArgumentForKind(D, Args, NeedsTsanRt);
86  if (NeedsAsan && NeedsMsan)
87    D.Diag(diag::err_drv_argument_not_allowed_with)
88      << lastArgumentForKind(D, Args, NeedsAsanRt)
89      << lastArgumentForKind(D, Args, NeedsMsanRt);
90  if (NeedsTsan && NeedsMsan)
91    D.Diag(diag::err_drv_argument_not_allowed_with)
92      << lastArgumentForKind(D, Args, NeedsTsanRt)
93      << lastArgumentForKind(D, Args, NeedsMsanRt);
94  if (NeedsLsan && NeedsTsan)
95    D.Diag(diag::err_drv_argument_not_allowed_with)
96      << lastArgumentForKind(D, Args, NeedsLeakDetection)
97      << lastArgumentForKind(D, Args, NeedsTsanRt);
98  if (NeedsLsan && NeedsMsan)
99    D.Diag(diag::err_drv_argument_not_allowed_with)
100      << lastArgumentForKind(D, Args, NeedsLeakDetection)
101      << lastArgumentForKind(D, Args, NeedsMsanRt);
102  // FIXME: Currenly -fsanitize=leak is silently ignored in the presence of
103  // -fsanitize=address. Perhaps it should print an error, or perhaps
104  // -f(-no)sanitize=leak should change whether leak detection is enabled by
105  // default in ASan?
106
107  // If -fsanitize contains extra features of ASan, it should also
108  // explicitly contain -fsanitize=address (probably, turned off later in the
109  // command line).
110  if ((Kind & AddressFull) != 0 && (AllKinds & Address) == 0)
111    D.Diag(diag::warn_drv_unused_sanitizer)
112     << lastArgumentForKind(D, Args, AddressFull)
113     << "-fsanitize=address";
114
115  // Parse -f(no-)sanitize-blacklist options.
116  if (Arg *BLArg = Args.getLastArg(options::OPT_fsanitize_blacklist,
117                                   options::OPT_fno_sanitize_blacklist)) {
118    if (BLArg->getOption().matches(options::OPT_fsanitize_blacklist)) {
119      std::string BLPath = BLArg->getValue();
120      if (llvm::sys::fs::exists(BLPath)) {
121        // Validate the blacklist format.
122        std::string BLError;
123        llvm::OwningPtr<llvm::SpecialCaseList> SCL(
124            llvm::SpecialCaseList::create(BLPath, BLError));
125        if (!SCL.get())
126          D.Diag(diag::err_drv_malformed_sanitizer_blacklist) << BLError;
127        else
128          BlacklistFile = BLPath;
129      } else {
130        D.Diag(diag::err_drv_no_such_file) << BLPath;
131      }
132    }
133  } else {
134    // If no -fsanitize-blacklist option is specified, try to look up for
135    // blacklist in the resource directory.
136    std::string BLPath;
137    if (getDefaultBlacklistForKind(D, Kind, BLPath) &&
138        llvm::sys::fs::exists(BLPath))
139      BlacklistFile = BLPath;
140  }
141
142  // Parse -f(no-)sanitize-memory-track-origins options.
143  if (NeedsMsan)
144    MsanTrackOrigins =
145      Args.hasFlag(options::OPT_fsanitize_memory_track_origins,
146                   options::OPT_fno_sanitize_memory_track_origins,
147                   /* Default */false);
148
149  // Parse -f(no-)sanitize-address-zero-base-shadow options.
150  if (NeedsAsan) {
151    if (Arg *A = Args.getLastArg(
152        options::OPT_fsanitize_address_zero_base_shadow,
153        options::OPT_fno_sanitize_address_zero_base_shadow))
154      AsanZeroBaseShadow = A->getOption().matches(
155                               options::OPT_fsanitize_address_zero_base_shadow)
156                               ? AZBSK_On
157                               : AZBSK_Off;
158  }
159}
160
161void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args,
162                            llvm::opt::ArgStringList &CmdArgs) const {
163  if (!Kind)
164    return;
165  const Driver &D = TC.getDriver();
166  SmallString<256> SanitizeOpt("-fsanitize=");
167#define SANITIZER(NAME, ID) \
168  if (Kind & ID) \
169    SanitizeOpt += NAME ",";
170#include "clang/Basic/Sanitizers.def"
171  SanitizeOpt.pop_back();
172  CmdArgs.push_back(Args.MakeArgString(SanitizeOpt));
173  if (!BlacklistFile.empty()) {
174    SmallString<64> BlacklistOpt("-fsanitize-blacklist=");
175    BlacklistOpt += BlacklistFile;
176    CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
177  }
178
179  if (MsanTrackOrigins)
180    CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins"));
181
182  if (needsAsanRt()) {
183    if (hasAsanZeroBaseShadow(TC)) {
184      CmdArgs.push_back(
185          Args.MakeArgString("-fsanitize-address-zero-base-shadow"));
186    } else if (TC.getTriple().getEnvironment() == llvm::Triple::Android) {
187      // Zero-base shadow is a requirement on Android.
188      D.Diag(diag::err_drv_argument_not_allowed_with)
189          << "-fno-sanitize-address-zero-base-shadow"
190          << lastArgumentForKind(D, Args, Address);
191    }
192  }
193
194  // Workaround for PR16386.
195  if (needsMsanRt())
196    CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new"));
197}
198
199bool SanitizerArgs::hasAsanZeroBaseShadow(const ToolChain &TC) const {
200  if (!needsAsanRt())
201    return false;
202  if (AsanZeroBaseShadow != AZBSK_Default)
203    return AsanZeroBaseShadow == AZBSK_On;
204  // Zero-base shadow is used by default only on Android.
205  return TC.getTriple().getEnvironment() == llvm::Triple::Android;
206}
207
208unsigned SanitizerArgs::parse(const char *Value) {
209  unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value)
210#define SANITIZER(NAME, ID) .Case(NAME, ID)
211#define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID)
212#include "clang/Basic/Sanitizers.def"
213    .Default(SanitizeKind());
214  // Assume -fsanitize=address implies -fsanitize=init-order.
215  // FIXME: This should be either specified in Sanitizers.def, or go away when
216  // we get rid of "-fsanitize=init-order" flag at all.
217  if (ParsedKind & Address)
218    ParsedKind |= InitOrder;
219  return ParsedKind;
220}
221
222unsigned SanitizerArgs::parse(const Driver &D, const llvm::opt::Arg *A,
223                              bool DiagnoseErrors) {
224  unsigned Kind = 0;
225  for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
226    if (unsigned K = parse(A->getValue(I)))
227      Kind |= K;
228    else if (DiagnoseErrors)
229      D.Diag(diag::err_drv_unsupported_option_argument)
230        << A->getOption().getName() << A->getValue(I);
231  }
232  return Kind;
233}
234
235bool SanitizerArgs::parse(const Driver &D, const llvm::opt::ArgList &Args,
236                          const llvm::opt::Arg *A, unsigned &Add,
237                          unsigned &Remove, bool DiagnoseErrors) {
238  Add = 0;
239  Remove = 0;
240  const char *DeprecatedReplacement = 0;
241  if (A->getOption().matches(options::OPT_faddress_sanitizer)) {
242    Add = Address;
243    DeprecatedReplacement = "-fsanitize=address";
244  } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) {
245    Remove = Address;
246    DeprecatedReplacement = "-fno-sanitize=address";
247  } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) {
248    Add = Thread;
249    DeprecatedReplacement = "-fsanitize=thread";
250  } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) {
251    Remove = Thread;
252    DeprecatedReplacement = "-fno-sanitize=thread";
253  } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
254    Add = UndefinedTrap;
255    DeprecatedReplacement =
256      "-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error";
257  } else if (A->getOption().matches(options::OPT_fbounds_checking) ||
258             A->getOption().matches(options::OPT_fbounds_checking_EQ)) {
259    Add = Bounds;
260    DeprecatedReplacement = "-fsanitize=bounds";
261  } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
262    Add = parse(D, A, DiagnoseErrors);
263  } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
264    Remove = parse(D, A, DiagnoseErrors);
265  } else {
266    // Flag is not relevant to sanitizers.
267    return false;
268  }
269  // If this is a deprecated synonym, produce a warning directing users
270  // towards the new spelling.
271  if (DeprecatedReplacement && DiagnoseErrors)
272    D.Diag(diag::warn_drv_deprecated_arg)
273      << A->getAsString(Args) << DeprecatedReplacement;
274  return true;
275}
276
277std::string SanitizerArgs::lastArgumentForKind(const Driver &D,
278                                               const llvm::opt::ArgList &Args,
279                                               unsigned Kind) {
280  for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(),
281                                                  E = Args.rend();
282       I != E; ++I) {
283    unsigned Add, Remove;
284    if (parse(D, Args, *I, Add, Remove, false) &&
285        (Add & Kind))
286      return describeSanitizeArg(Args, *I, Kind);
287    Kind &= ~Remove;
288  }
289  llvm_unreachable("arg list didn't provide expected value");
290}
291
292std::string SanitizerArgs::describeSanitizeArg(const llvm::opt::ArgList &Args,
293                                               const llvm::opt::Arg *A,
294                                               unsigned Mask) {
295  if (!A->getOption().matches(options::OPT_fsanitize_EQ))
296    return A->getAsString(Args);
297
298  for (unsigned I = 0, N = A->getNumValues(); I != N; ++I)
299    if (parse(A->getValue(I)) & Mask)
300      return std::string("-fsanitize=") + A->getValue(I);
301
302  llvm_unreachable("arg didn't provide expected value");
303}
304
305bool SanitizerArgs::getDefaultBlacklistForKind(const Driver &D, unsigned Kind,
306                                               std::string &BLPath) {
307  const char *BlacklistFile = 0;
308  if (Kind & NeedsAsanRt)
309    BlacklistFile = "asan_blacklist.txt";
310  else if (Kind & NeedsMsanRt)
311    BlacklistFile = "msan_blacklist.txt";
312  else if (Kind & NeedsTsanRt)
313    BlacklistFile = "tsan_blacklist.txt";
314  else if (Kind & NeedsDfsanRt)
315    BlacklistFile = "dfsan_abilist.txt";
316
317  if (BlacklistFile) {
318    SmallString<64> Path(D.ResourceDir);
319    llvm::sys::path::append(Path, BlacklistFile);
320    BLPath = Path.str();
321    return true;
322  }
323  return false;
324}
325