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

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: Use SmallVector; revert build script changes Created 4 years, 9 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 using namespace clang;
8
9 namespace chrome_checker {
10
11 namespace {
12
13 const char kWriteParamBadType[] =
14 "[chromium-ipc] IPC::WriteParam() is called on blacklisted type '%0'%1.";
15
16 const char kTupleBadType[] =
17 "[chromium-ipc] IPC tuple references banned type '%0'%1.";
18
19 const char kWriteParamBadSignature[] =
20 "[chromium-ipc] IPC::WriteParam() is expected to have two arguments.";
21
22 const char kNoteSeeHere[] =
23 "see here";
24
25 } // namespace
26
27 CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler)
28 : compiler_(compiler), context_(nullptr) {
29 auto& diagnostics = compiler_.getDiagnostics();
30 error_write_param_bad_type_ = diagnostics.getCustomDiagID(
31 DiagnosticsEngine::Error, kWriteParamBadType);
32 error_tuple_bad_type_ = diagnostics.getCustomDiagID(
33 DiagnosticsEngine::Error, kTupleBadType);
34 error_write_param_bad_signature_ = diagnostics.getCustomDiagID(
35 DiagnosticsEngine::Error, kWriteParamBadSignature);
36 note_see_here_ = diagnostics.getCustomDiagID(
37 DiagnosticsEngine::Note, kNoteSeeHere);
38
39 blacklisted_typedefs_ = llvm::StringSet<>({
40 "intmax_t",
41 "uintmax_t",
42 "intptr_t",
43 "uintptr_t",
44 "wint_t",
45 "size_t",
46 "rsize_t",
47 "ssize_t",
48 "ptrdiff_t",
49 "dev_t",
50 "off_t",
51 "clock_t",
52 "time_t",
53 "suseconds_t"
54 });
55 }
56
57 void CheckIPCVisitor::BeginDecl(Decl* decl) {
58 decl_stack_.push_back(decl);
59 }
60
61 void CheckIPCVisitor::EndDecl() {
62 decl_stack_.pop_back();
63 }
64
65 void CheckIPCVisitor::VisitTemplateSpecializationType(
66 TemplateSpecializationType* spec) {
67 ValidateCheckedTuple(spec);
68 }
69
70 void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
71 ValidateWriteParam(call_expr);
72 }
73
74 bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) {
75 const FunctionDecl* callee_decl = call_expr->getDirectCallee();
76 if (!callee_decl ||
77 callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") {
78 return true;
79 }
80
81 return ValidateWriteParamSignature(call_expr) &&
82 ValidateWriteParamArgument(call_expr->getArg(1));
83 }
84
85 // Checks that IPC::WriteParam() has expected signature.
86 bool CheckIPCVisitor::ValidateWriteParamSignature(
87 const CallExpr* call_expr) {
88 if (call_expr->getNumArgs() != 2) {
89 compiler_.getDiagnostics().Report(
90 call_expr->getExprLoc(), error_write_param_bad_signature_);
91 return false;
92 }
93 return true;
94 }
95
96 // Checks that IPC::WriteParam() argument type is allowed.
97 // See CheckType() for specifics.
98 bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) {
99 if (auto* parent_fn_decl = GetParentDecl<FunctionDecl>()) {
100 auto template_kind = parent_fn_decl->getTemplatedKind();
101 if (template_kind != FunctionDecl::TK_NonTemplate &&
102 template_kind != FunctionDecl::TK_FunctionTemplate) {
103 // Skip all specializations - we don't check WriteParam() on dependent
104 // types (typedef info gets lost), and we checked all non-dependent uses
105 // earlier (when we checked the template itself).
106 return true;
107 }
108 }
109
110 const Expr* arg_type_expr = arg_expr;
111 if (auto* tmp_expr = dyn_cast<MaterializeTemporaryExpr>(arg_type_expr)) {
112 arg_type_expr = tmp_expr->GetTemporaryExpr();
113 }
114
115 QualType arg_type;
116 if (auto* cast_expr = dyn_cast<ExplicitCastExpr>(arg_type_expr)) {
117 arg_type = cast_expr->getTypeAsWritten();
118 } else {
119 if (auto* cast_expr = dyn_cast<ImplicitCastExpr>(arg_type_expr)) {
dcheng 2016/03/07 23:44:39 Nit: } else if {auto* cast_expr = dyn_cast<Implic
Dmitry Skiba 2016/03/08 01:16:10 Hmm, but in original code arg_type assignment happ
dcheng 2016/03/08 01:30:32 Oh I see. How about just doing this instead then:
120 arg_type_expr = cast_expr->getSubExpr();
121 }
122 arg_type = arg_type_expr->getType();
123 }
124
125 CheckDetails details;
126 if (CheckType(arg_type, &details)) {
127 return true;
128 }
129
130 ReportCheckError(details,
131 arg_expr->getExprLoc(),
132 error_write_param_bad_type_);
133
134 return false;
135 }
136
137 // Checks that IPC::CheckedTuple<> is specialized with allowed types.
138 // See CheckType() above for specifics.
139 bool CheckIPCVisitor::ValidateCheckedTuple(
140 const TemplateSpecializationType* spec) {
141 TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl();
142 if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") {
143 return true;
144 }
145
146 bool valid = true;
147 for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
148 const TemplateArgument& arg = spec->getArg(i);
149 CheckDetails details;
150 if (CheckTemplateArgument(arg, &details)) {
151 continue;
152 }
153
154 valid = false;
155
156 auto* parent_decl = GetParentDecl<Decl>();
157 ReportCheckError(
158 details,
159 parent_decl ? parent_decl->getLocStart() : SourceLocation(),
160 error_tuple_bad_type_);
161 }
162
163 return valid;
164 }
165
166 template <typename T>
167 const T* CheckIPCVisitor::GetParentDecl() const {
168 for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) {
169 if (auto* parent = dyn_cast_or_null<T>(*i)) {
170 return parent;
171 }
172 }
173 return nullptr;
174 }
175
176
177 bool CheckIPCVisitor::IsBlacklistedType(QualType type) const {
178 return context_->hasSameUnqualifiedType(type, context_->LongTy) ||
179 context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy);
180 }
181
182 bool CheckIPCVisitor::IsBlacklistedTypedef(const TypedefNameDecl* tdef) const {
183 return blacklisted_typedefs_.find(tdef->getName()) !=
184 blacklisted_typedefs_.end();
185 }
186
187 // Checks that integer type is allowed (not blacklisted).
188 bool CheckIPCVisitor::CheckIntegerType(QualType type,
189 CheckDetails* details) const {
190 bool seen_typedef = false;
191 while (true) {
192 details->exit_type = type;
193
194 if (auto* tdef = dyn_cast<TypedefType>(type)) {
195 if (IsBlacklistedTypedef(tdef->getDecl())) {
196 return false;
197 }
198 details->typedefs.push_back(tdef);
199 seen_typedef = true;
200 }
201
202 QualType desugared_type =
203 type->getLocallyUnqualifiedSingleStepDesugaredType();
204 if (desugared_type == type) {
205 break;
206 }
207
208 type = desugared_type;
209 }
210
211 return seen_typedef || !IsBlacklistedType(type);
212 }
213
214 // Checks that |type| is allowed (not blacklisted), recursively visiting
215 // template specializations.
216 bool CheckIPCVisitor::CheckType(QualType type, CheckDetails* details) const {
217 if (type->isReferenceType()) {
218 type = type->getPointeeType();
219 }
220 type = type.getLocalUnqualifiedType();
221
222 if (details->entry_type == QualType()) {
dcheng 2016/03/07 23:44:39 Does details->entry_type.isNull() work here?
Dmitry Skiba 2016/03/08 01:16:11 Done.
223 details->entry_type = type;
224 }
225
226 if (type->isIntegerType()) {
227 return CheckIntegerType(type, details);
228 }
229
230 while (true) {
231 if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) {
232 for (const TemplateArgument& arg: *spec) {
233 if (!CheckTemplateArgument(arg, details)) {
234 return false;
235 }
236 }
237 return true;
238 }
239
240 if (auto* record = dyn_cast<RecordType>(type)) {
241 if (auto* spec = dyn_cast<ClassTemplateSpecializationDecl>(
242 record->getDecl())) {
243 const TemplateArgumentList& args = spec->getTemplateArgs();
244 for (unsigned i = 0; i != args.size(); ++i) {
245 if (!CheckTemplateArgument(args[i], details)) {
246 return false;
247 }
248 }
249 }
250 return true;
251 }
252
253 if (auto* tdef = dyn_cast<TypedefType>(type)) {
254 details->typedefs.push_back(tdef);
255 }
256
257 QualType desugared_type =
258 type->getLocallyUnqualifiedSingleStepDesugaredType();
259 if (desugared_type == type) {
260 break;
261 }
262
263 type = desugared_type;
264 }
265
266 return true;
267 }
268
269 bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg,
270 CheckDetails* details) const {
271 return arg.getKind() != TemplateArgument::Type ||
272 CheckType(arg.getAsType(), details);
273 }
274
275 void CheckIPCVisitor::ReportCheckError(const CheckDetails& details,
276 SourceLocation loc,
277 unsigned error) {
278 DiagnosticsEngine& diagnostics = compiler_.getDiagnostics();
279
280 std::string entry_type = details.entry_type.getAsString();
281 std::string exit_type = details.exit_type.getAsString();
282
283 std::string via;
284 if (entry_type != exit_type) {
285 via = " via '" + entry_type + "'";
286 }
287 diagnostics.Report(loc, error) << exit_type << via;
288
289 for (const TypedefType* tdef: details.typedefs) {
290 diagnostics.Report(tdef->getDecl()->getLocation(), note_see_here_);
291 }
292 }
293
294 } // namespace chrome_checker
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698