Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(275)

Side by Side Diff: tools/clang/plugins/CheckIPCVisitor.cpp

Issue 1665363002: Clang plugin to check that unstable types are not used in IPC. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix typos Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright (c) 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "CheckIPCVisitor.h"
6
7 #include "llvm/ADT/StringSet.h"
8
9 using namespace clang;
10
11 namespace chrome_checker {
12
13 namespace {
14
15 const char kWriteParamBadType[] =
16 "[chromium-ipc] IPC::WriteParam() is called on blacklisted type '%0'%1.";
17
18 const char kTupleBadType[] =
19 "[chromium-ipc] IPC tuple references banned type '%0'%1.";
20
21 const char kWriteParamTemplateArg[] =
22 "[chromium-ipc] IPC::WriteParam() must explicitly specify template "
23 "argument (use WriteParam<T>(...)).";
24
25 const char kWriteParamWrongTemplate[] =
26 "[chromium-ipc] IPC::WriteParam() can only be used in IPC::ParamTraits<> "
27 "templates.";
28
29 const char kWriteParamBadSignature[] =
30 "[chromium-ipc] IPC::WriteParam() is expected to have two arguments.";
31
32 const char kNoteSeeHere[] =
33 "";
dcheng 2016/02/23 19:46:46 Add some content to this string?
Dmitry Skiba 2016/02/24 14:39:04 Done.
34
35 struct CheckDetails {
36 QualType entry_type;
37 QualType exit_type;
38 std::vector<const TypedefType*> typedefs;
39 };
40
41 bool IsBlacklistedType(ASTContext& context, QualType type) {
42 return context.hasSameUnqualifiedType(type, context.LongTy) ||
43 context.hasSameUnqualifiedType(type, context.UnsignedLongTy);
44 }
45
46 bool IsBlacklistedTypedef(const TypedefNameDecl* tdef) {
47 static llvm::StringSet<> blacklist({
dcheng 2016/02/23 19:46:46 No static initializers. Just make this an instance
Dmitry Skiba 2016/02/24 14:39:04 TBD
48 "intmax_t",
49 "uintmax_t",
50 "intptr_t",
51 "uintptr_t",
52 "wint_t",
53 "size_t",
54 "rsize_t",
55 "ssize_t",
56 "ptrdiff_t",
57 "dev_t",
58 "off_t",
59 "clock_t",
60 "time_t",
61 "suseconds_t"
62 });
63 return blacklist.find(tdef->getName()) != blacklist.end();
64 }
65
66 QualType UnqualifyType(QualType type) {
67 if (type->isReferenceType()) {
68 type = type->getPointeeType();
69 }
70 type = type.getLocalUnqualifiedType();
71 return type;
72 }
73
74 // Checks that integer type is not blacklisted.
75 bool CheckIntegerType(ASTContext& context,
76 QualType type,
77 CheckDetails* details = nullptr) {
78 bool seen_typedef = false;
79 while (true) {
80 if (details) {
81 details->exit_type = type;
82 }
83
84 if (auto tdef = dyn_cast<TypedefType>(type)) {
dcheng 2016/02/23 19:46:46 Please qualify auto with * or & as appropriate (it
Dmitry Skiba 2016/02/24 14:39:04 Done.
85 if (IsBlacklistedTypedef(tdef->getDecl())) {
86 return false;
87 }
88 if (details) {
89 details->typedefs.push_back(tdef);
90 }
91 seen_typedef = true;
92 }
93
94 QualType desugared_type =
95 type->getLocallyUnqualifiedSingleStepDesugaredType();
dcheng 2016/02/23 19:46:46 The documentation for this says to prefer getSingl
Dmitry Skiba 2016/02/24 14:39:04 Previously I was calling that function / stripping
96 if (desugared_type == type) {
97 break;
98 }
99
100 type = desugared_type;
101 }
102
103 return seen_typedef || !IsBlacklistedType(context, type);
104 }
105
106 bool CheckTemplateArgument(ASTContext& context,
107 const TemplateArgument& arg,
108 CheckDetails* details = nullptr);
109
110 bool CheckType(ASTContext& context,
111 QualType type,
112 CheckDetails* details = nullptr) {
113 type = UnqualifyType(type);
114 if (details && details->entry_type == QualType()) {
115 details->entry_type = type;
116 }
117
118 if (type->isIntegerType()) {
119 return CheckIntegerType(context, type, details);
120 }
121
122 if (auto tdef = type->getAs<TypedefType>()) {
123 if (details) {
124 details->typedefs.push_back(tdef);
125 }
126 return CheckType(context, tdef->desugar(), details);
127 }
128
129 if (auto spec = type->getAs<TemplateSpecializationType>()) {
130 for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
dcheng 2016/02/23 19:46:46 I think you can just do for (TemplateArgument* arg
Dmitry Skiba 2016/02/24 14:39:04 Done.
131 if (!CheckTemplateArgument(context, spec->getArg(i), details)) {
132 return false;
133 }
134 }
135 return true;
136 }
137
138 if (auto record = type->getAs<RecordType>()) {
139 auto spec = dyn_cast<ClassTemplateSpecializationDecl>(record->getDecl());
dcheng 2016/02/23 19:46:46 Fold into 140 for consistency.
Dmitry Skiba 2016/02/24 14:39:04 Done.
140 if (spec) {
141 const TemplateArgumentList& args = spec->getTemplateArgs();
142 for (unsigned i = 0; i != args.size(); ++i) {
143 if (!CheckTemplateArgument(context, args[i], details)) {
144 return false;
145 }
146 }
147 }
148 return true;
149 }
150
151 return true;
152 }
153
154 bool CheckTemplateArgument(ASTContext& context,
155 const TemplateArgument& arg,
156 CheckDetails* details) {
157 return arg.getKind() != TemplateArgument::Type ||
158 CheckType(context, arg.getAsType(), details);
159 }
160
161 void ReportCheckError(clang::DiagnosticsEngine& diagnostics,
162 const CheckDetails& details,
163 SourceLocation loc,
164 unsigned error, unsigned typedef_note) {
dcheng 2016/02/23 19:46:46 What's the point of typedef_note: aren't we always
Dmitry Skiba 2016/02/24 14:39:04 Yes, but it's not known here, since ReportCheckErr
165 std::string entry_type = details.entry_type.getAsString();
166 std::string exit_type = details.exit_type.getAsString();
167
168 std::string via;
169 if (entry_type != exit_type) {
170 via = " via '" + entry_type + "'";
171 }
172 diagnostics.Report(loc, error) << exit_type << via;
173
174 for (const TypedefType* tdef: details.typedefs) {
175 diagnostics.Report(tdef->getDecl()->getLocation(), typedef_note);
176 }
177 }
178
179 } // namespace
180
181 CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler)
182 : compiler_(compiler), context_(nullptr) {
183 auto& diagnostics = compiler_.getDiagnostics();
184 error_write_param_bad_type_ = diagnostics.getCustomDiagID(
185 DiagnosticsEngine::Error, kWriteParamBadType);
186 error_tuple_bad_type_ = diagnostics.getCustomDiagID(
187 DiagnosticsEngine::Error, kTupleBadType);
188 error_write_param_template_arg_ = diagnostics.getCustomDiagID(
189 DiagnosticsEngine::Error, kWriteParamTemplateArg);
190 error_write_param_wrong_template_ = diagnostics.getCustomDiagID(
191 DiagnosticsEngine::Error, kWriteParamWrongTemplate);
192 error_write_param_bad_signature_ = diagnostics.getCustomDiagID(
193 DiagnosticsEngine::Error, kWriteParamBadSignature);
194 note_see_here_ = diagnostics.getCustomDiagID(
195 DiagnosticsEngine::Note, kNoteSeeHere);
196 }
197
198 void CheckIPCVisitor::BeginDecl(Decl* decl) {
199 decl_stack_.push_back(decl);
200 }
201
202 void CheckIPCVisitor::EndDecl() {
203 decl_stack_.pop_back();
204 }
205
206 void CheckIPCVisitor::VisitTemplateSpecializationType(
207 TemplateSpecializationType* spec) {
208 ValidateCheckedTuple(spec);
209 }
210
211 void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
212 ValidateWriteParam(call_expr);
213 }
214
215 bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) const {
216 if (auto lookup_expr = dyn_cast<UnresolvedLookupExpr>(
217 call_expr->getCallee())) {
218 return ValidateWriteParamInsideTemplate(call_expr, lookup_expr);
219 }
220
221 const FunctionDecl* callee_decl = call_expr->getDirectCallee();
222 if (!callee_decl ||
223 callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") {
224 return true;
225 }
226
227 return ValidateWriteParamSignature(call_expr) &&
228 ValidateWriteParamCaller(call_expr) &&
229 ValidateWriteParamArgument(call_expr->getArg(1));
230 }
231
232 // Checks that IPC::WriteParam() has expected signature.
233 bool CheckIPCVisitor::ValidateWriteParamSignature(
234 const CallExpr* call_expr) const {
235 if (call_expr->getNumArgs() != 2) {
236 compiler_.getDiagnostics().Report(
237 call_expr->getExprLoc(), error_write_param_bad_signature_);
238 return false;
239 }
240 return true;
241 }
242
243 // Checks that when WriteParam() is used inside a template, it explicitly
244 // depends on template argument: WriteParam<T>(...)
245 bool CheckIPCVisitor::ValidateWriteParamInsideTemplate(
246 const CallExpr* call_expr,
247 const UnresolvedLookupExpr* lookup_expr) const {
248 if (!IsInsideParamTraits() ||
249 lookup_expr->getName().getAsString() != "WriteParam") {
250 return true;
251 }
252
253 if (!ValidateWriteParamSignature(call_expr)) {
254 return false;
255 }
256
257 if (lookup_expr->getNumTemplateArgs() == 1) {
258 const TemplateArgument& template_arg =
259 lookup_expr->getTemplateArgs()->getArgument();
260 bool explicitly_dependent =
261 template_arg.getKind() == TemplateArgument::Type &&
262 isa<TemplateTypeParmType>(template_arg.getAsType());
263 if (explicitly_dependent) {
264 return true;
265 }
266 }
267
268 compiler_.getDiagnostics().Report(
269 call_expr->getExprLoc(), error_write_param_template_arg_);
270 return false;
271 }
272
273 // Checks that IPC::WriteParam() argument type is allowed.
274 // See CheckType() above for specifics.
275 bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) const {
276 auto parent_fn_decl = GetParentDecl<FunctionDecl>();
277 if (parent_fn_decl &&
278 parent_fn_decl->getTemplatedKind() != FunctionDecl::TK_NonTemplate) {
279 // Skip all specializations - we checked templates earlier in
280 // ValidateWriteParamInsideTemplate().
281 return true;
282 }
283
284 const Expr* arg_type_expr = arg_expr;
285 if (auto tmp_expr = dyn_cast<MaterializeTemporaryExpr>(arg_type_expr)) {
286 arg_type_expr = tmp_expr->GetTemporaryExpr();
287 }
288
289 QualType arg_type;
290 if (auto cast_expr = dyn_cast<ExplicitCastExpr>(arg_type_expr)) {
291 arg_type = cast_expr->getTypeAsWritten();
292 } else {
293 if (auto cast_expr = dyn_cast<ImplicitCastExpr>(arg_type_expr)) {
294 arg_type_expr = cast_expr->getSubExpr();
295 }
296 arg_type = arg_type_expr->getType();
297 }
298
299 if (CheckType(*context_, arg_type)) {
300 return true;
301 }
302
303 CheckDetails details;
304 CheckType(*context_, arg_type, &details);
dcheng 2016/02/23 19:46:46 Why not just pass details in to begin with?
Dmitry Skiba 2016/02/24 14:39:04 It's just an optimization. I expect CheckType() to
dcheng 2016/02/24 22:57:42 I'd just use llvm::SmallVector<> and always pass i
305
306 ReportCheckError(compiler_.getDiagnostics(),
307 details,
308 arg_expr->getExprLoc(),
309 error_write_param_bad_type_, note_see_here_);
310
311 return false;
312 }
313
314 // Checks that when WriteParam() is called from a template, it's
315 // in fact IPC::ParamTraits<> template.
316 bool CheckIPCVisitor::ValidateWriteParamCaller(
317 const CallExpr* call_expr) const {
318 auto parent_fn_decl = GetParentDecl<FunctionDecl>();
319 if (!parent_fn_decl ||
320 parent_fn_decl->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
321 // Skip if we're not in template.
322 return true;
323 }
324
325 // If we're in a template of any kind, check that it's ParamTraits
326 if (IsInsideParamTraits()) {
327 return true;
328 }
329
330 compiler_.getDiagnostics().Report(
331 call_expr->getExprLoc(), error_write_param_wrong_template_);
332 return false;
333 }
334
335 // Checks that IPC::CheckedTuple<> is specialized with allowed types.
336 // See CheckType() above for specifics.
337 bool CheckIPCVisitor::ValidateCheckedTuple(
338 const TemplateSpecializationType* spec) const {
339 TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl();
340 if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") {
341 return true;
342 }
343
344 bool valid = true;
345 for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
346 const TemplateArgument& arg = spec->getArg(i);
347 if (CheckTemplateArgument(*context_, arg)) {
348 continue;
349 }
350
351 valid = false;
352
353 CheckDetails details;
354 CheckTemplateArgument(*context_, arg, &details);
355
356 auto parent_decl = GetParentDecl<Decl>();
357 SourceLocation error_loc = parent_decl ?
358 parent_decl->getLocStart() : SourceLocation();
359 ReportCheckError(compiler_.getDiagnostics(),
360 details,
361 error_loc,
362 error_tuple_bad_type_, note_see_here_);
363 }
364
365 return valid;
366 }
367
368 bool CheckIPCVisitor::IsInsideParamTraits() const {
369 auto parent_spec_decl = GetParentDecl<ClassTemplateSpecializationDecl>();
370 return parent_spec_decl &&
371 parent_spec_decl->getQualifiedNameAsString() == "IPC::ParamTraits";
372 }
373
374 template <typename T>
375 const T* CheckIPCVisitor::GetParentDecl() const {
376 for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) {
377 if (auto parent = dyn_cast_or_null<T>(*i)) {
378 return parent;
379 }
380 }
381 return nullptr;
382 }
383
384 } // namespace chrome_checker
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698