OLD | NEW |
---|---|
(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 | |
OLD | NEW |