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 #include "clang/Driver/Driver.h"
11 #include "clang/Driver/DriverDiagnostic.h"
12 #include "clang/Driver/Options.h"
13 #include "clang/Driver/ToolChain.h"
14 #include "llvm/ADT/StringExtras.h"
15 #include "llvm/ADT/StringSwitch.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/SpecialCaseList.h"
19 #include <memory>
20 
21 using namespace clang::driver;
22 using namespace llvm::opt;
23 
24 namespace {
25 /// Assign ordinals to possible values of -fsanitize= flag.
26 /// We use the ordinal values as bit positions within \c SanitizeKind.
27 enum SanitizeOrdinal {
28 #define SANITIZER(NAME, ID) SO_##ID,
29 #define SANITIZER_GROUP(NAME, ID, ALIAS) SO_##ID##Group,
30 #include "clang/Basic/Sanitizers.def"
31   SO_Count
32 };
33 
34 /// Represents a set of sanitizer kinds. It is also used to define:
35 /// 1) set of sanitizers each sanitizer group expands into.
36 /// 2) set of sanitizers sharing a specific property (e.g.
37 ///    all sanitizers with zero-base shadow).
38 enum SanitizeKind {
39 #define SANITIZER(NAME, ID) ID = 1 << SO_##ID,
40 #define SANITIZER_GROUP(NAME, ID, ALIAS)                                       \
41 ID = ALIAS, ID##Group = 1 << SO_##ID##Group,
42 #include "clang/Basic/Sanitizers.def"
43   NeedsUbsanRt = Undefined | Integer,
44   NotAllowedWithTrap = Vptr,
45   RequiresPIE = Memory | DataFlow,
46   NeedsUnwindTables = Address | Thread | Memory | DataFlow
47 };
48 }
49 
50 /// Returns true if set of \p Sanitizers contain at least one sanitizer from
51 /// \p Kinds.
52 static bool hasOneOf(const clang::SanitizerSet &Sanitizers, unsigned Kinds) {
53 #define SANITIZER(NAME, ID)                                                    \
54   if (Sanitizers.has(clang::SanitizerKind::ID) && (Kinds & ID))                \
55     return true;
56 #include "clang/Basic/Sanitizers.def"
57   return false;
58 }
59 
60 /// Adds all sanitizers from \p Kinds to \p Sanitizers.
61 static void addAllOf(clang::SanitizerSet &Sanitizers, unsigned Kinds) {
62 #define SANITIZER(NAME, ID) \
63   if (Kinds & ID) \
64     Sanitizers.set(clang::SanitizerKind::ID, true);
65 #include "clang/Basic/Sanitizers.def"
66 }
67 
68 static unsigned toSanitizeKind(clang::SanitizerKind K) {
69 #define SANITIZER(NAME, ID) \
70   if (K == clang::SanitizerKind::ID) \
71     return ID;
72 #include "clang/Basic/Sanitizers.def"
73   llvm_unreachable("Invalid SanitizerKind!");
74 }
75 
76 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
77 /// Returns a member of the \c SanitizeKind enumeration, or \c 0
78 /// if \p Value is not known.
79 static unsigned parseValue(const char *Value);
80 
81 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
82 /// invalid components. Returns OR of members of \c SanitizeKind enumeration.
83 static unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A,
84                                bool DiagnoseErrors);
85 
86 /// Parse a single flag of the form -f[no]sanitize=.
87 /// Sets the masks defining required change of the set of sanitizers.
88 /// Returns true if the flag was parsed successfully.
89 static bool parseArgument(const Driver &D, const llvm::opt::Arg *A,
90                           unsigned &Add, unsigned &Remove, bool DiagnoseErrors);
91 
92 /// Produce an argument string from ArgList \p Args, which shows how it
93 /// provides some sanitizer kind from \p Mask. For example, the argument list
94 /// "-fsanitize=thread,vptr -fsanitize=address" with mask \c NeedsUbsanRt
95 /// would produce "-fsanitize=vptr".
96 static std::string lastArgumentForMask(const Driver &D,
97                                        const llvm::opt::ArgList &Args,
98                                        unsigned Mask);
99 
100 static std::string lastArgumentForKind(const Driver &D,
101                                        const llvm::opt::ArgList &Args,
102                                        clang::SanitizerKind K) {
103   return lastArgumentForMask(D, Args, toSanitizeKind(K));
104 }
105 
106 /// Produce an argument string from argument \p A, which shows how it provides
107 /// a value in \p Mask. For instance, the argument
108 /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce
109 /// "-fsanitize=alignment".
110 static std::string describeSanitizeArg(const llvm::opt::Arg *A, unsigned Mask);
111 
112 /// Produce a string containing comma-separated names of sanitizers in \p
113 /// Sanitizers set.
114 static std::string toString(const clang::SanitizerSet &Sanitizers);
115 
116 /// For each sanitizer group bit set in \p Kinds, set the bits for sanitizers
117 /// this group enables.
118 static unsigned expandGroups(unsigned Kinds);
119 
120 static unsigned getToolchainUnsupportedKinds(const ToolChain &TC) {
121   bool IsFreeBSD = TC.getTriple().getOS() == llvm::Triple::FreeBSD;
122   bool IsLinux = TC.getTriple().getOS() == llvm::Triple::Linux;
123   bool IsX86 = TC.getTriple().getArch() == llvm::Triple::x86;
124   bool IsX86_64 = TC.getTriple().getArch() == llvm::Triple::x86_64;
125 
126   unsigned Unsupported = 0;
127   if (!(IsLinux && IsX86_64)) {
128     Unsupported |= Memory | DataFlow;
129   }
130   if (!((IsLinux || IsFreeBSD) && IsX86_64)) {
131     Unsupported |= Thread;
132   }
133   if (!(IsLinux && (IsX86 || IsX86_64))) {
134     Unsupported |= Function;
135   }
136   return Unsupported;
137 }
138 
139 bool SanitizerArgs::needsUbsanRt() const {
140   return !UbsanTrapOnError && hasOneOf(Sanitizers, NeedsUbsanRt);
141 }
142 
143 bool SanitizerArgs::requiresPIE() const {
144   return AsanZeroBaseShadow || hasOneOf(Sanitizers, RequiresPIE);
145 }
146 
147 bool SanitizerArgs::needsUnwindTables() const {
148   return hasOneOf(Sanitizers, NeedsUnwindTables);
149 }
150 
151 void SanitizerArgs::clear() {
152   Sanitizers.clear();
153   SanitizeRecover = false;
154   BlacklistFile = "";
155   SanitizeCoverage = 0;
156   MsanTrackOrigins = 0;
157   AsanFieldPadding = 0;
158   AsanZeroBaseShadow = false;
159   UbsanTrapOnError = false;
160   AsanSharedRuntime = false;
161   LinkCXXRuntimes = false;
162 }
163 
164 SanitizerArgs::SanitizerArgs(const ToolChain &TC,
165                              const llvm::opt::ArgList &Args) {
166   clear();
167   unsigned AllRemove = 0;  // During the loop below, the accumulated set of
168                            // sanitizers disabled by the current sanitizer
169                            // argument or any argument after it.
170   unsigned DiagnosedKinds = 0;  // All Kinds we have diagnosed up to now.
171                                 // Used to deduplicate diagnostics.
172   unsigned Kinds = 0;
173   unsigned NotSupported = getToolchainUnsupportedKinds(TC);
174   const Driver &D = TC.getDriver();
175   for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
176        I != E; ++I) {
177     unsigned Add, Remove;
178     if (!parseArgument(D, *I, Add, Remove, true))
179       continue;
180     (*I)->claim();
181 
182     AllRemove |= expandGroups(Remove);
183 
184     // Avoid diagnosing any sanitizer which is disabled later.
185     Add &= ~AllRemove;
186 
187     // At this point we have not expanded groups, so any unsupported sanitizers
188     // in Add are those which have been explicitly enabled. Diagnose them.
189     if (unsigned KindsToDiagnose = Add & NotSupported & ~DiagnosedKinds) {
190       // Only diagnose the new kinds.
191       std::string Desc = describeSanitizeArg(*I, KindsToDiagnose);
192       D.Diag(diag::err_drv_unsupported_opt_for_target) << Desc
193                                                        << TC.getTriple().str();
194       DiagnosedKinds |= KindsToDiagnose;
195     }
196     Add &= ~NotSupported;
197 
198     Add = expandGroups(Add);
199     // Group expansion may have enabled a sanitizer which is disabled later.
200     Add &= ~AllRemove;
201     // Silently discard any unsupported sanitizers implicitly enabled through
202     // group expansion.
203     Add &= ~NotSupported;
204 
205     Kinds |= Add;
206   }
207   addAllOf(Sanitizers, Kinds);
208 
209   SanitizeRecover = Args.hasFlag(options::OPT_fsanitize_recover,
210                                  options::OPT_fno_sanitize_recover, true);
211 
212   UbsanTrapOnError =
213     Args.hasFlag(options::OPT_fsanitize_undefined_trap_on_error,
214                  options::OPT_fno_sanitize_undefined_trap_on_error, false);
215 
216   // Warn about undefined sanitizer options that require runtime support.
217   if (UbsanTrapOnError && hasOneOf(Sanitizers, NotAllowedWithTrap)) {
218     D.Diag(clang::diag::err_drv_argument_not_allowed_with)
219       << lastArgumentForMask(D, Args, NotAllowedWithTrap)
220       << "-fsanitize-undefined-trap-on-error";
221   }
222 
223   // Check for incompatible sanitizers.
224   bool NeedsAsan = Sanitizers.has(SanitizerKind::Address);
225   bool NeedsTsan = Sanitizers.has(SanitizerKind::Thread);
226   bool NeedsMsan = Sanitizers.has(SanitizerKind::Memory);
227   bool NeedsLsan = Sanitizers.has(SanitizerKind::Leak);
228   if (NeedsAsan && NeedsTsan)
229     D.Diag(clang::diag::err_drv_argument_not_allowed_with)
230       << lastArgumentForKind(D, Args, SanitizerKind::Address)
231       << lastArgumentForKind(D, Args, SanitizerKind::Thread);
232   if (NeedsAsan && NeedsMsan)
233     D.Diag(clang::diag::err_drv_argument_not_allowed_with)
234       << lastArgumentForKind(D, Args, SanitizerKind::Address)
235       << lastArgumentForKind(D, Args, SanitizerKind::Memory);
236   if (NeedsTsan && NeedsMsan)
237     D.Diag(clang::diag::err_drv_argument_not_allowed_with)
238       << lastArgumentForKind(D, Args, SanitizerKind::Thread)
239       << lastArgumentForKind(D, Args, SanitizerKind::Memory);
240   if (NeedsLsan && NeedsTsan)
241     D.Diag(clang::diag::err_drv_argument_not_allowed_with)
242       << lastArgumentForKind(D, Args, SanitizerKind::Leak)
243       << lastArgumentForKind(D, Args, SanitizerKind::Thread);
244   if (NeedsLsan && NeedsMsan)
245     D.Diag(clang::diag::err_drv_argument_not_allowed_with)
246       << lastArgumentForKind(D, Args, SanitizerKind::Leak)
247       << lastArgumentForKind(D, Args, SanitizerKind::Memory);
248   // FIXME: Currently -fsanitize=leak is silently ignored in the presence of
249   // -fsanitize=address. Perhaps it should print an error, or perhaps
250   // -f(-no)sanitize=leak should change whether leak detection is enabled by
251   // default in ASan?
252 
253   // Parse -f(no-)sanitize-blacklist options.
254   if (Arg *BLArg = Args.getLastArg(options::OPT_fsanitize_blacklist,
255                                    options::OPT_fno_sanitize_blacklist)) {
256     if (BLArg->getOption().matches(options::OPT_fsanitize_blacklist)) {
257       std::string BLPath = BLArg->getValue();
258       if (llvm::sys::fs::exists(BLPath)) {
259         // Validate the blacklist format.
260         std::string BLError;
261         std::unique_ptr<llvm::SpecialCaseList> SCL(
262             llvm::SpecialCaseList::create(BLPath, BLError));
263         if (!SCL.get())
264           D.Diag(clang::diag::err_drv_malformed_sanitizer_blacklist) << BLError;
265         else
266           BlacklistFile = BLPath;
267       } else {
268         D.Diag(clang::diag::err_drv_no_such_file) << BLPath;
269       }
270     }
271   } else {
272     // If no -fsanitize-blacklist option is specified, try to look up for
273     // blacklist in the resource directory.
274     std::string BLPath;
275     if (getDefaultBlacklist(D, BLPath) && llvm::sys::fs::exists(BLPath))
276       BlacklistFile = BLPath;
277   }
278 
279   // Parse -f[no-]sanitize-memory-track-origins[=level] options.
280   if (NeedsMsan) {
281     if (Arg *A =
282             Args.getLastArg(options::OPT_fsanitize_memory_track_origins_EQ,
283                             options::OPT_fsanitize_memory_track_origins,
284                             options::OPT_fno_sanitize_memory_track_origins)) {
285       if (A->getOption().matches(options::OPT_fsanitize_memory_track_origins)) {
286         MsanTrackOrigins = 1;
287       } else if (A->getOption().matches(
288                      options::OPT_fno_sanitize_memory_track_origins)) {
289         MsanTrackOrigins = 0;
290       } else {
291         StringRef S = A->getValue();
292         if (S.getAsInteger(0, MsanTrackOrigins) || MsanTrackOrigins < 0 ||
293             MsanTrackOrigins > 2) {
294           D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
295         }
296       }
297     }
298   }
299 
300   // Parse -fsanitize-coverage=N
301   if (NeedsAsan || NeedsMsan) {  // Currently asan or msan is required.
302     if (Arg *A = Args.getLastArg(options::OPT_fsanitize_coverage)) {
303       StringRef S = A->getValue();
304       // Legal values are 0..4.
305       if (S.getAsInteger(0, SanitizeCoverage) || SanitizeCoverage < 0 ||
306           SanitizeCoverage > 4)
307         D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
308     }
309   }
310 
311   if (NeedsAsan) {
312     AsanSharedRuntime =
313         Args.hasArg(options::OPT_shared_libasan) ||
314         (TC.getTriple().getEnvironment() == llvm::Triple::Android);
315     AsanZeroBaseShadow =
316         (TC.getTriple().getEnvironment() == llvm::Triple::Android);
317     if (Arg *A =
318             Args.getLastArg(options::OPT_fsanitize_address_field_padding)) {
319         StringRef S = A->getValue();
320         // Legal values are 0 and 1, 2, but in future we may add more levels.
321         if (S.getAsInteger(0, AsanFieldPadding) || AsanFieldPadding < 0 ||
322             AsanFieldPadding > 2) {
323           D.Diag(clang::diag::err_drv_invalid_value) << A->getAsString(Args) << S;
324         }
325     }
326 
327     if (Arg *WindowsDebugRTArg =
328             Args.getLastArg(options::OPT__SLASH_MTd, options::OPT__SLASH_MT,
329                             options::OPT__SLASH_MDd, options::OPT__SLASH_MD,
330                             options::OPT__SLASH_LDd, options::OPT__SLASH_LD)) {
331       switch (WindowsDebugRTArg->getOption().getID()) {
332       case options::OPT__SLASH_MTd:
333       case options::OPT__SLASH_MDd:
334       case options::OPT__SLASH_LDd:
335         D.Diag(clang::diag::err_drv_argument_not_allowed_with)
336             << WindowsDebugRTArg->getAsString(Args)
337             << lastArgumentForKind(D, Args, SanitizerKind::Address);
338         D.Diag(clang::diag::note_drv_address_sanitizer_debug_runtime);
339       }
340     }
341   }
342 
343   // Parse -link-cxx-sanitizer flag.
344   LinkCXXRuntimes =
345       Args.hasArg(options::OPT_fsanitize_link_cxx_runtime) || D.CCCIsCXX();
346 }
347 
348 static std::string toString(const clang::SanitizerSet &Sanitizers) {
349   std::string Res;
350 #define SANITIZER(NAME, ID)                                                    \
351   if (Sanitizers.has(clang::SanitizerKind::ID)) {                              \
352     if (!Res.empty())                                                          \
353       Res += ",";                                                              \
354     Res += NAME;                                                               \
355   }
356 #include "clang/Basic/Sanitizers.def"
357   return Res;
358 }
359 
360 void SanitizerArgs::addArgs(const llvm::opt::ArgList &Args,
361                             llvm::opt::ArgStringList &CmdArgs) const {
362   if (Sanitizers.empty())
363     return;
364   CmdArgs.push_back(Args.MakeArgString("-fsanitize=" + toString(Sanitizers)));
365 
366   if (!SanitizeRecover)
367     CmdArgs.push_back("-fno-sanitize-recover");
368 
369   if (UbsanTrapOnError)
370     CmdArgs.push_back("-fsanitize-undefined-trap-on-error");
371 
372   if (!BlacklistFile.empty()) {
373     SmallString<64> BlacklistOpt("-fsanitize-blacklist=");
374     BlacklistOpt += BlacklistFile;
375     CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
376   }
377 
378   if (MsanTrackOrigins)
379     CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins=" +
380                                          llvm::utostr(MsanTrackOrigins)));
381   if (AsanFieldPadding)
382     CmdArgs.push_back(Args.MakeArgString("-fsanitize-address-field-padding=" +
383                                          llvm::utostr(AsanFieldPadding)));
384   if (SanitizeCoverage)
385     CmdArgs.push_back(Args.MakeArgString("-fsanitize-coverage=" +
386                                          llvm::utostr(SanitizeCoverage)));
387   // Workaround for PR16386.
388   if (Sanitizers.has(SanitizerKind::Memory))
389     CmdArgs.push_back(Args.MakeArgString("-fno-assume-sane-operator-new"));
390 }
391 
392 bool SanitizerArgs::getDefaultBlacklist(const Driver &D, std::string &BLPath) {
393   const char *BlacklistFile = nullptr;
394   if (Sanitizers.has(SanitizerKind::Address))
395     BlacklistFile = "asan_blacklist.txt";
396   else if (Sanitizers.has(SanitizerKind::Memory))
397     BlacklistFile = "msan_blacklist.txt";
398   else if (Sanitizers.has(SanitizerKind::Thread))
399     BlacklistFile = "tsan_blacklist.txt";
400   else if (Sanitizers.has(SanitizerKind::DataFlow))
401     BlacklistFile = "dfsan_abilist.txt";
402 
403   if (BlacklistFile) {
404     SmallString<64> Path(D.ResourceDir);
405     llvm::sys::path::append(Path, BlacklistFile);
406     BLPath = Path.str();
407     return true;
408   }
409   return false;
410 }
411 
412 unsigned parseValue(const char *Value) {
413   unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value)
414 #define SANITIZER(NAME, ID) .Case(NAME, ID)
415 #define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID##Group)
416 #include "clang/Basic/Sanitizers.def"
417     .Default(SanitizeKind());
418   return ParsedKind;
419 }
420 
421 unsigned expandGroups(unsigned Kinds) {
422 #define SANITIZER(NAME, ID)
423 #define SANITIZER_GROUP(NAME, ID, ALIAS) if (Kinds & ID##Group) Kinds |= ID;
424 #include "clang/Basic/Sanitizers.def"
425   return Kinds;
426 }
427 
428 unsigned parseArgValues(const Driver &D, const llvm::opt::Arg *A,
429                         bool DiagnoseErrors) {
430   unsigned Kind = 0;
431   for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
432     if (unsigned K = parseValue(A->getValue(I)))
433       Kind |= K;
434     else if (DiagnoseErrors)
435       D.Diag(clang::diag::err_drv_unsupported_option_argument)
436         << A->getOption().getName() << A->getValue(I);
437   }
438   return Kind;
439 }
440 
441 bool parseArgument(const Driver &D, const llvm::opt::Arg *A, unsigned &Add,
442                    unsigned &Remove, bool DiagnoseErrors) {
443   Add = 0;
444   Remove = 0;
445   if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
446     Add = parseArgValues(D, A, DiagnoseErrors);
447     return true;
448   }
449   if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
450     Remove = parseArgValues(D, A, DiagnoseErrors);
451     return true;
452   }
453   return false;
454 }
455 
456 std::string lastArgumentForMask(const Driver &D, const llvm::opt::ArgList &Args,
457                                 unsigned Mask) {
458   for (llvm::opt::ArgList::const_reverse_iterator I = Args.rbegin(),
459                                                   E = Args.rend();
460        I != E; ++I) {
461     unsigned Add, Remove;
462     if (parseArgument(D, *I, Add, Remove, false) &&
463         (expandGroups(Add) & Mask))
464       return describeSanitizeArg(*I, Mask);
465     Mask &= ~Remove;
466   }
467   llvm_unreachable("arg list didn't provide expected value");
468 }
469 
470 std::string describeSanitizeArg(const llvm::opt::Arg *A, unsigned Mask) {
471   assert(A->getOption().matches(options::OPT_fsanitize_EQ)
472          && "Invalid argument in describeSanitizerArg!");
473 
474   std::string Sanitizers;
475   for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
476     if (expandGroups(parseValue(A->getValue(I))) & Mask) {
477       if (!Sanitizers.empty())
478         Sanitizers += ",";
479       Sanitizers += A->getValue(I);
480     }
481   }
482 
483   assert(!Sanitizers.empty() && "arg didn't provide expected value");
484   return "-fsanitize=" + Sanitizers;
485 }
486