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