1 //===--- SanitizerArgs.h - Arguments for sanitizer tools -------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
9 #ifndef CLANG_LIB_DRIVER_SANITIZERARGS_H_
10 #define CLANG_LIB_DRIVER_SANITIZERARGS_H_
12 #include "clang/Driver/Arg.h"
13 #include "clang/Driver/ArgList.h"
14 #include "clang/Driver/Driver.h"
15 #include "clang/Driver/DriverDiagnostic.h"
16 #include "clang/Driver/Options.h"
17 #include "llvm/ADT/StringSwitch.h"
18 #include "llvm/Support/Path.h"
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"
32 /// Bugs to catch at runtime.
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,
40 NeedsUbsanRt = Undefined | Integer,
41 NotAllowedWithTrap = Vptr,
42 HasZeroBaseShadow = Thread | Memory
45 std::string BlacklistFile;
46 bool MsanTrackOrigins;
47 bool AsanZeroBaseShadow;
48 bool UbsanTrapOnError;
51 SanitizerArgs() : Kind(0), BlacklistFile(""), MsanTrackOrigins(false),
52 AsanZeroBaseShadow(false), UbsanTrapOnError(false) {}
53 /// Parses the sanitizer arguments from an argument list.
54 SanitizerArgs(const ToolChain &TC, const ArgList &Args);
56 bool needsAsanRt() const { return Kind & NeedsAsanRt; }
57 bool needsTsanRt() const { return Kind & NeedsTsanRt; }
58 bool needsMsanRt() const { return Kind & NeedsMsanRt; }
59 bool needsUbsanRt() const {
62 return Kind & NeedsUbsanRt;
65 bool sanitizesVptr() const { return Kind & Vptr; }
66 bool notAllowedWithTrap() const { return Kind & NotAllowedWithTrap; }
67 bool hasZeroBaseShadow() const {
68 return (Kind & HasZeroBaseShadow) || AsanZeroBaseShadow;
71 void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
74 SmallString<256> SanitizeOpt("-fsanitize=");
75 #define SANITIZER(NAME, ID) \
77 SanitizeOpt += NAME ",";
78 #include "clang/Basic/Sanitizers.def"
79 SanitizeOpt.pop_back();
80 CmdArgs.push_back(Args.MakeArgString(SanitizeOpt));
81 if (!BlacklistFile.empty()) {
82 SmallString<64> BlacklistOpt("-fsanitize-blacklist=");
83 BlacklistOpt += BlacklistFile;
84 CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
88 CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins"));
90 if (AsanZeroBaseShadow)
91 CmdArgs.push_back(Args.MakeArgString(
92 "-fsanitize-address-zero-base-shadow"));
96 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
97 /// Returns OR of members of the \c SanitizeKind enumeration, or \c 0
98 /// if \p Value is not known.
99 static unsigned parse(const char *Value) {
100 unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value)
101 #define SANITIZER(NAME, ID) .Case(NAME, ID)
102 #define SANITIZER_GROUP(NAME, ID, ALIAS) .Case(NAME, ID)
103 #include "clang/Basic/Sanitizers.def"
104 .Default(SanitizeKind());
105 // Assume -fsanitize=address implies -fsanitize=init-order.
106 // FIXME: This should be either specified in Sanitizers.def, or go away when
107 // we get rid of "-fsanitize=init-order" flag at all.
108 if (ParsedKind & Address)
109 ParsedKind |= InitOrder;
113 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
114 /// invalid components.
115 static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) {
117 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
118 if (unsigned K = parse(A->getValue(I)))
120 else if (DiagnoseErrors)
121 D.Diag(diag::err_drv_unsupported_option_argument)
122 << A->getOption().getName() << A->getValue(I);
127 /// Parse a single flag of the form -f[no]sanitize=, or
128 /// -f*-sanitizer. Sets the masks defining required change of Kind value.
129 /// Returns true if the flag was parsed successfully.
130 static bool parse(const Driver &D, const ArgList &Args, const Arg *A,
131 unsigned &Add, unsigned &Remove, bool DiagnoseErrors) {
134 const char *DeprecatedReplacement = 0;
135 if (A->getOption().matches(options::OPT_faddress_sanitizer)) {
137 DeprecatedReplacement = "-fsanitize=address";
138 } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) {
140 DeprecatedReplacement = "-fno-sanitize=address";
141 } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) {
143 DeprecatedReplacement = "-fsanitize=thread";
144 } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) {
146 DeprecatedReplacement = "-fno-sanitize=thread";
147 } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
149 DeprecatedReplacement =
150 "-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error";
151 } else if (A->getOption().matches(options::OPT_fbounds_checking) ||
152 A->getOption().matches(options::OPT_fbounds_checking_EQ)) {
154 DeprecatedReplacement = "-fsanitize=bounds";
155 } else if (A->getOption().matches(options::OPT_fsanitize_EQ)) {
156 Add = parse(D, A, DiagnoseErrors);
157 } else if (A->getOption().matches(options::OPT_fno_sanitize_EQ)) {
158 Remove = parse(D, A, DiagnoseErrors);
160 // Flag is not relevant to sanitizers.
163 // If this is a deprecated synonym, produce a warning directing users
164 // towards the new spelling.
165 if (DeprecatedReplacement && DiagnoseErrors)
166 D.Diag(diag::warn_drv_deprecated_arg)
167 << A->getAsString(Args) << DeprecatedReplacement;
171 /// Produce an argument string from ArgList \p Args, which shows how it
172 /// provides a sanitizer kind in \p Mask. For example, the argument list
173 /// "-fsanitize=thread,vptr -faddress-sanitizer" with mask \c NeedsUbsanRt
174 /// would produce "-fsanitize=vptr".
175 static std::string lastArgumentForKind(const Driver &D, const ArgList &Args,
177 for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
179 unsigned Add, Remove;
180 if (parse(D, Args, *I, Add, Remove, false) &&
182 return describeSanitizeArg(Args, *I, Kind);
185 llvm_unreachable("arg list didn't provide expected value");
188 /// Produce an argument string from argument \p A, which shows how it provides
189 /// a value in \p Mask. For instance, the argument
190 /// "-fsanitize=address,alignment" with mask \c NeedsUbsanRt would produce
191 /// "-fsanitize=alignment".
192 static std::string describeSanitizeArg(const ArgList &Args, const Arg *A,
194 if (!A->getOption().matches(options::OPT_fsanitize_EQ))
195 return A->getAsString(Args);
197 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I)
198 if (parse(A->getValue(I)) & Mask)
199 return std::string("-fsanitize=") + A->getValue(I);
201 llvm_unreachable("arg didn't provide expected value");
204 static bool getDefaultBlacklistForKind(const Driver &D, unsigned Kind,
205 std::string &BLPath) {
206 // For now, specify the default blacklist location for ASan only.
207 if (Kind & NeedsAsanRt) {
208 SmallString<64> Path(D.ResourceDir);
209 llvm::sys::path::append(Path, "asan_blacklist.txt");
217 } // namespace driver
220 #endif // CLANG_LIB_DRIVER_SANITIZERARGS_H_