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

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: Blacklist types instead 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 <vector>
8
9 #include "llvm/ADT/StringSet.h"
10
11 using namespace clang;
12
13 namespace chrome_checker {
14
15 namespace {
16
17 const char kWriteParamBadType[] =
18 "[chromium-ipc] IPC::WriteParam() is called on blacklisted type '%0'%1.";
19
20 const char kTupleBadType[] =
21 "[chromium-ipc] IPC tuple references banned type '%0'%1.";
22
23 const char kWriteParamTemplateArg[] =
24 "[chromium-ipc] IPC::WriteParam() must explicitly specify template "
25 "argument (use WriteParam<T>(...)).";
26
27 const char kWriteParamWrongTemplate[] =
28 "[chromium-ipc] IPC::WriteParam() can only be used in IPC::ParamTraits<> "
29 "templates.";
30
31 const char kWriteParamBadSignature[] =
32 "[chromium-ipc] IPC::WriteParam() is expected to have two arguments.";
33
34 const char kNoteSeeHere[] =
35 "";
36
37 struct CheckDetails {
38 QualType entry_type;
39 QualType exit_type;
40 std::vector<const TypedefType*> typedefs;
41 };
42
43 bool IsInterestingType(QualType type) {
44 return type->isIntegerType();
45 }
46
47 bool IsBlacklistedType(ASTContext& context, QualType type) {
48 return context.hasSameUnqualifiedType(type, context.LongTy) ||
49 context.hasSameUnqualifiedType(type, context.UnsignedLongTy);
50 }
51
52 bool IsBlacklistedTypedef(const TypedefNameDecl* tdef) {
53 static llvm::StringSet<> blacklist({
54 "intmax_t",
55 "uintmax_t",
56 "intptr_t",
57 "uintptr_t",
58 "wint_t",
59 "size_t",
60 "rsize_t",
61 "ssize_t",
62 "ptrdiff_t",
63 "dev_t",
64 "off_t",
65 "clock_t",
66 "time_t",
67 "suseconds_t"
68 });
69 return blacklist.find(tdef->getName()) != blacklist.end();
70 }
71
72 QualType UnqualifyEntryType(QualType type, CheckDetails* details) {
73 if (type->isReferenceType()) {
74 type = type->getPointeeType();
75 }
76 type = type.getLocalUnqualifiedType();
77 if (details && details->entry_type == QualType()) {
78 details->entry_type = type;
79 }
80 return type;
81 }
82
83 // Checks that type is allowed. See comment in the header for details.
84 bool CheckType(ASTContext& context,
85 QualType type,
86 CheckDetails* details = nullptr) {
87 type = UnqualifyEntryType(type, details);
88
89 if (!IsInterestingType(type)) {
90 return true;
91 }
92
93 bool seen_typedef = false;
94 while (true) {
95 if (details) {
96 details->exit_type = type;
97 }
98
99 if (auto tdef = dyn_cast<TypedefType>(type)) {
100 if (IsBlacklistedTypedef(tdef->getDecl())) {
101 return false;
102 }
103 if (details) {
104 details->typedefs.push_back(tdef);
105 }
106 seen_typedef = true;
107 }
108
109 QualType desugared_type =
110 type->getLocallyUnqualifiedSingleStepDesugaredType();
111 if (desugared_type == type) {
112 break;
113 }
114
115 type = desugared_type;
116 }
117
118 return seen_typedef || !IsBlacklistedType(context, type);
119 }
120
121 // Same as CheckType(), but recurses into template specializations.
122 bool CheckTemplateType(ASTContext& context,
123 QualType type,
124 CheckDetails* details = nullptr) {
125 type = UnqualifyEntryType(type, details);
126
127 if (IsInterestingType(type)) {
128 return CheckType(context, type, details);
129 }
130
131 if (auto tdef = type->getAs<TypedefType>()) {
132 if (details) {
133 details->typedefs.push_back(tdef);
134 }
135 return CheckTemplateType(context, tdef->desugar(), details);
136 }
137
138 if (auto spec = type->getAs<TemplateSpecializationType>()) {
139 for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
140 if (!CheckTemplateType(context, spec->getArg(i).getAsType(), details)) {
141 return false;
142 }
143 }
144 return true;
145 }
146
147 return true;
148 }
149
150 void ReportCheckError(clang::DiagnosticsEngine& diagnostics,
151 const CheckDetails& details,
152 SourceLocation loc,
153 unsigned error, unsigned typedef_note) {
154 std::string entry_type = details.entry_type.getAsString();
155 std::string exit_type = details.exit_type.getAsString();
156
157 std::string via;
158 if (entry_type != exit_type) {
159 via = " via '" + entry_type + "'";
160 }
161 diagnostics.Report(loc, error) << exit_type << via;
162
163 for (const TypedefType* tdef: details.typedefs) {
164 diagnostics.Report(tdef->getDecl()->getLocation(), typedef_note);
165 }
166 }
167
168 } // namespace
169
170 CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler)
171 : compiler_(compiler), context_(nullptr),
172 current_decl_(nullptr), current_fn_decl_(nullptr),
173 current_spec_decl_(nullptr) {
174 auto& diagnostics = compiler_.getDiagnostics();
175 error_write_param_bad_type_ = diagnostics.getCustomDiagID(
176 DiagnosticsEngine::Error, kWriteParamBadType);
177 error_tuple_bad_type_ = diagnostics.getCustomDiagID(
178 DiagnosticsEngine::Error, kTupleBadType);
179 error_write_param_template_arg_ = diagnostics.getCustomDiagID(
180 DiagnosticsEngine::Error, kWriteParamTemplateArg);
181 error_write_param_wrong_template_ = diagnostics.getCustomDiagID(
182 DiagnosticsEngine::Error, kWriteParamWrongTemplate);
183 error_write_param_bad_signature_ = diagnostics.getCustomDiagID(
184 DiagnosticsEngine::Error, kWriteParamBadSignature);
185 note_see_here_ = diagnostics.getCustomDiagID(
186 DiagnosticsEngine::Note, kNoteSeeHere);
187 }
188
189 void CheckIPCVisitor::Visit(ASTContext& context) {
190 context_ = &context;
191 TraverseDecl(context_->getTranslationUnitDecl());
192 context_ = nullptr;
193 }
194
195 bool CheckIPCVisitor::TraverseDecl(Decl* decl) {
196 // Update current function
197 const FunctionDecl* last_fn_decl = current_fn_decl_;
198 auto fn_decl = dyn_cast_or_null<FunctionDecl>(decl);
199 if (fn_decl) {
200 current_fn_decl_ = fn_decl;
201 }
202
203 // Update current specialization
204 const ClassTemplateSpecializationDecl* last_spec_decl = current_spec_decl_;
205 auto spec_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(decl);
206 if (spec_decl) {
207 current_spec_decl_ = spec_decl;
208 }
209
210 // Update current decl
211 const Decl* last_decl = current_decl_;
212 current_decl_ = decl;
213
214 bool result = Base::TraverseDecl(decl);
215
216 if (fn_decl) {
217 current_fn_decl_ = last_fn_decl;
218 }
219 if (spec_decl) {
220 current_spec_decl_ = last_spec_decl;
221 }
222 current_decl_ = last_decl;
223
224 return result;
225 }
226
227 bool CheckIPCVisitor::VisitTemplateSpecializationType(
228 TemplateSpecializationType* spec) {
229 ValidateCheckedTuple(spec);
230 return true;
231 }
232
233 bool CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
234 ValidateWriteParam(call_expr);
235 return true;
236 }
237
238 bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) const {
239 if (auto lookup_expr = dyn_cast<UnresolvedLookupExpr>(
240 call_expr->getCallee())) {
241 return ValidateWriteParamInsideTemplate(call_expr, lookup_expr);
242 }
243
244 const FunctionDecl* callee_decl = call_expr->getDirectCallee();
245 if (!callee_decl ||
246 callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") {
247 return true;
248 }
249
250 return ValidateWriteParamSignature(call_expr) &&
251 ValidateWriteParamCaller(call_expr) &&
252 ValidateWriteParamArgument(call_expr->getArg(1));
253 }
254
255 // Checks that IPC::WriteParam() has expected signature.
256 bool CheckIPCVisitor::ValidateWriteParamSignature(
257 const CallExpr* call_expr) const {
258 if (call_expr->getNumArgs() != 2) {
259 compiler_.getDiagnostics().Report(
260 call_expr->getExprLoc(), error_write_param_bad_signature_);
261 return false;
262 }
263 return true;
264 }
265
266 // Checks that when WriteParam() is used inside a template, it explicitly
267 // depends on template argument: WriteParam<T>(...)
268 bool CheckIPCVisitor::ValidateWriteParamInsideTemplate(
269 const CallExpr* call_expr,
270 const UnresolvedLookupExpr* lookup_expr) const {
271 if (!IsInsideParamTraits() ||
272 lookup_expr->getName().getAsString() != "WriteParam") {
273 return true;
274 }
275
276 if (!ValidateWriteParamSignature(call_expr)) {
277 return false;
278 }
279
280 if (lookup_expr->getNumTemplateArgs() == 1) {
281 const TemplateArgument& template_arg =
282 lookup_expr->getTemplateArgs()->getArgument();
283 bool explicitly_dependent =
284 template_arg.getKind() == TemplateArgument::Type &&
285 isa<TemplateTypeParmType>(template_arg.getAsType());
286 if (explicitly_dependent) {
287 return true;
288 }
289 }
290
291 compiler_.getDiagnostics().Report(
292 call_expr->getExprLoc(), error_write_param_template_arg_);
293 return false;
294 }
295
296 // Checks that IPC::WriteParam() argument type is allowed.
297 // See CheckType() above for specifics.
298 bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) const {
299 if (current_fn_decl_ &&
300 current_fn_decl_->getTemplatedKind() != FunctionDecl::TK_NonTemplate) {
301 // Skip all specializations - we checked templates earlier in
302 // ValidateWriteParamInsideTemplate().
303 return true;
304 }
305
306 const Expr* arg_type_expr = arg_expr;
307 if (auto tmp_expr = dyn_cast<MaterializeTemporaryExpr>(arg_type_expr)) {
308 arg_type_expr = tmp_expr->GetTemporaryExpr();
309 }
310
311 QualType arg_type;
312 if (auto cast_expr = dyn_cast<ExplicitCastExpr>(arg_type_expr)) {
313 arg_type = cast_expr->getTypeAsWritten();
314 } else {
315 if (auto cast_expr = dyn_cast<ImplicitCastExpr>(arg_type_expr)) {
316 arg_type_expr = cast_expr->getSubExpr();
317 }
318 arg_type = arg_type_expr->getType();
319 }
320
321 if (CheckType(*context_, arg_type)) {
322 return true;
323 }
324
325 CheckDetails details;
326 CheckType(*context_, arg_type, &details);
327
328 ReportCheckError(compiler_.getDiagnostics(),
329 details,
330 arg_expr->getExprLoc(),
331 error_write_param_bad_type_, note_see_here_);
332
333 return false;
334 }
335
336 // Checks that when WriteParam() is called from a template, it's
337 // in fact IPC::ParamTraits<> template.
338 bool CheckIPCVisitor::ValidateWriteParamCaller(
339 const CallExpr* call_expr) const {
340 if (!current_fn_decl_ ||
341 current_fn_decl_->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
342 // Skip if we're not in template.
343 return true;
344 }
345
346 // If we're in a template of any kind, check that it's ParamTraits
347 if (IsInsideParamTraits()) {
348 return true;
349 }
350
351 compiler_.getDiagnostics().Report(
352 call_expr->getExprLoc(), error_write_param_wrong_template_);
353 return false;
354 }
355
356 // Checks that IPC::CheckedTuple<> is specialized with allowed types.
357 // See CheckType() above for specifics.
358 bool CheckIPCVisitor::ValidateCheckedTuple(
359 const TemplateSpecializationType* spec) const {
360 TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl();
361 if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") {
362 return true;
363 }
364
365 bool valid = true;
366 for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
367 QualType arg_type = spec->getArg(i).getAsType();
368 if (CheckTemplateType(*context_, arg_type)) {
369 continue;
370 }
371
372 valid = false;
373
374 CheckDetails details;
375 CheckTemplateType(*context_, arg_type, &details);
376
377 ReportCheckError(compiler_.getDiagnostics(),
378 details,
379 current_decl_->getLocStart(),
380 error_tuple_bad_type_, note_see_here_);
381 }
382
383 return valid;
384 }
385
386 bool CheckIPCVisitor::IsInsideParamTraits() const {
387 return current_spec_decl_ &&
388 current_spec_decl_->getQualifiedNameAsString() == "IPC::ParamTraits";
389 }
390
391 } // namespace chrome_checker
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698