blob: 1f7ec9913ba4444af1ab55e3cb028440c932ac0e [file] [log] [blame]
Alexey Samsonovbb1071c2012-11-06 15:09:03 +00001//===--- SanitizerArgs.h - Arguments for sanitizer tools -------*- C++ -*-===//
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#ifndef CLANG_LIB_DRIVER_SANITIZERARGS_H_
10#define CLANG_LIB_DRIVER_SANITIZERARGS_H_
11
Chandler Carruth55fc8732012-12-04 09:13:33 +000012#include "clang/Driver/Arg.h"
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000013#include "clang/Driver/ArgList.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000014#include "clang/Driver/Driver.h"
15#include "clang/Driver/DriverDiagnostic.h"
16#include "clang/Driver/Options.h"
17#include "llvm/ADT/StringSwitch.h"
Alexey Samsonov24697b02013-02-19 11:25:29 +000018#include "llvm/Support/Path.h"
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000019
20namespace clang {
21namespace driver {
22
23class SanitizerArgs {
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"
29 SO_Count
30 };
31
32 /// Bugs to catch at runtime.
33 enum SanitizeKind {
34#define SANITIZER(NAME, ID) ID = 1 << SO_##ID,
35#define SANITIZER_GROUP(NAME, ID, ALIAS) ID = ALIAS,
36#include "clang/Basic/Sanitizers.def"
Alexey Samsonov3e335c12013-01-28 07:20:44 +000037 NeedsAsanRt = Address,
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000038 NeedsTsanRt = Thread,
Evgeniy Stepanov09ccf392012-12-03 13:20:43 +000039 NeedsMsanRt = Memory,
Richard Smitha0a628f2013-02-23 02:53:19 +000040 NeedsUbsanRt = Undefined | Integer,
Peter Collingbourne52ca70d2013-04-09 04:35:11 +000041 NotAllowedWithTrap = Vptr,
42 HasZeroBaseShadow = Thread | Memory
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000043 };
44 unsigned Kind;
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000045 std::string BlacklistFile;
Evgeniy Stepanov34ef11b2012-12-24 08:42:34 +000046 bool MsanTrackOrigins;
Alexey Samsonov4bdc6042013-01-20 13:12:12 +000047 bool AsanZeroBaseShadow;
Chad Rosier78d85b12013-01-29 23:31:22 +000048 bool UbsanTrapOnError;
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000049
50 public:
Alexey Samsonov4bdc6042013-01-20 13:12:12 +000051 SanitizerArgs() : Kind(0), BlacklistFile(""), MsanTrackOrigins(false),
Chad Rosier78d85b12013-01-29 23:31:22 +000052 AsanZeroBaseShadow(false), UbsanTrapOnError(false) {}
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000053 /// Parses the sanitizer arguments from an argument list.
Peter Collingbourne52ca70d2013-04-09 04:35:11 +000054 SanitizerArgs(const ToolChain &TC, const ArgList &Args);
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000055
56 bool needsAsanRt() const { return Kind & NeedsAsanRt; }
57 bool needsTsanRt() const { return Kind & NeedsTsanRt; }
Evgeniy Stepanov09ccf392012-12-03 13:20:43 +000058 bool needsMsanRt() const { return Kind & NeedsMsanRt; }
Chad Rosier78d85b12013-01-29 23:31:22 +000059 bool needsUbsanRt() const {
60 if (UbsanTrapOnError)
61 return false;
62 return Kind & NeedsUbsanRt;
63 }
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000064
65 bool sanitizesVptr() const { return Kind & Vptr; }
Chad Rosier78d85b12013-01-29 23:31:22 +000066 bool notAllowedWithTrap() const { return Kind & NotAllowedWithTrap; }
Peter Collingbourne52ca70d2013-04-09 04:35:11 +000067 bool hasZeroBaseShadow() const {
68 return (Kind & HasZeroBaseShadow) || AsanZeroBaseShadow;
69 }
Alexey Samsonov4d1a6e42012-11-29 22:36:21 +000070
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000071 void addArgs(const ArgList &Args, ArgStringList &CmdArgs) const {
72 if (!Kind)
73 return;
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +000074 SmallString<256> SanitizeOpt("-fsanitize=");
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000075#define SANITIZER(NAME, ID) \
76 if (Kind & ID) \
77 SanitizeOpt += NAME ",";
78#include "clang/Basic/Sanitizers.def"
79 SanitizeOpt.pop_back();
80 CmdArgs.push_back(Args.MakeArgString(SanitizeOpt));
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000081 if (!BlacklistFile.empty()) {
Dmitri Gribenkocfa88f82013-01-12 19:30:44 +000082 SmallString<64> BlacklistOpt("-fsanitize-blacklist=");
Alexey Samsonov91ecfa62012-12-03 19:12:58 +000083 BlacklistOpt += BlacklistFile;
84 CmdArgs.push_back(Args.MakeArgString(BlacklistOpt));
85 }
Evgeniy Stepanov34ef11b2012-12-24 08:42:34 +000086
87 if (MsanTrackOrigins)
88 CmdArgs.push_back(Args.MakeArgString("-fsanitize-memory-track-origins"));
Alexey Samsonov4bdc6042013-01-20 13:12:12 +000089
90 if (AsanZeroBaseShadow)
91 CmdArgs.push_back(Args.MakeArgString(
92 "-fsanitize-address-zero-base-shadow"));
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000093 }
94
95 private:
96 /// Parse a single value from a -fsanitize= or -fno-sanitize= value list.
Alexey Samsonov7d0d85f2013-03-19 10:48:37 +000097 /// Returns OR of members of the \c SanitizeKind enumeration, or \c 0
98 /// if \p Value is not known.
Alexey Samsonovbb1071c2012-11-06 15:09:03 +000099 static unsigned parse(const char *Value) {
Alexey Samsonov7d0d85f2013-03-19 10:48:37 +0000100 unsigned ParsedKind = llvm::StringSwitch<SanitizeKind>(Value)
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000101#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());
Alexey Samsonov7d0d85f2013-03-19 10:48:37 +0000105 // 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;
110 return ParsedKind;
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000111 }
112
113 /// Parse a -fsanitize= or -fno-sanitize= argument's values, diagnosing any
114 /// invalid components.
Alexey Samsonov3325b162012-11-28 17:34:24 +0000115 static unsigned parse(const Driver &D, const Arg *A, bool DiagnoseErrors) {
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000116 unsigned Kind = 0;
117 for (unsigned I = 0, N = A->getNumValues(); I != N; ++I) {
118 if (unsigned K = parse(A->getValue(I)))
119 Kind |= K;
Alexey Samsonov3325b162012-11-28 17:34:24 +0000120 else if (DiagnoseErrors)
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000121 D.Diag(diag::err_drv_unsupported_option_argument)
122 << A->getOption().getName() << A->getValue(I);
123 }
124 return Kind;
125 }
126
Alexey Samsonov3325b162012-11-28 17:34:24 +0000127 /// 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) {
132 Add = 0;
133 Remove = 0;
134 const char *DeprecatedReplacement = 0;
135 if (A->getOption().matches(options::OPT_faddress_sanitizer)) {
136 Add = Address;
137 DeprecatedReplacement = "-fsanitize=address";
138 } else if (A->getOption().matches(options::OPT_fno_address_sanitizer)) {
139 Remove = Address;
140 DeprecatedReplacement = "-fno-sanitize=address";
141 } else if (A->getOption().matches(options::OPT_fthread_sanitizer)) {
142 Add = Thread;
143 DeprecatedReplacement = "-fsanitize=thread";
144 } else if (A->getOption().matches(options::OPT_fno_thread_sanitizer)) {
145 Remove = Thread;
146 DeprecatedReplacement = "-fno-sanitize=thread";
147 } else if (A->getOption().matches(options::OPT_fcatch_undefined_behavior)) {
Chad Rosier78d85b12013-01-29 23:31:22 +0000148 Add = UndefinedTrap;
149 DeprecatedReplacement =
150 "-fsanitize=undefined-trap -fsanitize-undefined-trap-on-error";
Alexey Samsonov3325b162012-11-28 17:34:24 +0000151 } else if (A->getOption().matches(options::OPT_fbounds_checking) ||
152 A->getOption().matches(options::OPT_fbounds_checking_EQ)) {
153 Add = Bounds;
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);
159 } else {
160 // Flag is not relevant to sanitizers.
161 return false;
162 }
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;
168 return true;
169 }
170
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,
176 unsigned Kind) {
177 for (ArgList::const_reverse_iterator I = Args.rbegin(), E = Args.rend();
178 I != E; ++I) {
179 unsigned Add, Remove;
180 if (parse(D, Args, *I, Add, Remove, false) &&
181 (Add & Kind))
182 return describeSanitizeArg(Args, *I, Kind);
183 Kind &= ~Remove;
184 }
185 llvm_unreachable("arg list didn't provide expected value");
186 }
187
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000188 /// 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,
193 unsigned Mask) {
194 if (!A->getOption().matches(options::OPT_fsanitize_EQ))
195 return A->getAsString(Args);
196
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);
200
201 llvm_unreachable("arg didn't provide expected value");
202 }
Alexey Samsonov24697b02013-02-19 11:25:29 +0000203
204 static bool getDefaultBlacklistForKind(const Driver &D, unsigned Kind,
205 std::string &BLPath) {
Evgeniy Stepanov26afaf02013-05-20 14:10:58 +0000206 const char *BlacklistFile = 0;
207 if (Kind & NeedsAsanRt)
208 BlacklistFile = "asan_blacklist.txt";
209 else if (Kind & NeedsMsanRt)
210 BlacklistFile = "msan_blacklist.txt";
211 else if (Kind & NeedsTsanRt)
212 BlacklistFile = "tsan_blacklist.txt";
213 if (BlacklistFile) {
Alexey Samsonov24697b02013-02-19 11:25:29 +0000214 SmallString<64> Path(D.ResourceDir);
Evgeniy Stepanov26afaf02013-05-20 14:10:58 +0000215 llvm::sys::path::append(Path, BlacklistFile);
Alexey Samsonov24697b02013-02-19 11:25:29 +0000216 BLPath = Path.str();
217 return true;
218 }
219 return false;
220 }
Alexey Samsonovbb1071c2012-11-06 15:09:03 +0000221};
222
223} // namespace driver
224} // namespace clang
225
226#endif // CLANG_LIB_DRIVER_SANITIZERARGS_H_