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 QualType arg_type; | |
111 | |
112 arg_expr = arg_expr->IgnoreImplicit(); | |
113 if (auto* cast_expr = dyn_cast<ExplicitCastExpr>(arg_expr)) { | |
114 arg_type = cast_expr->getTypeAsWritten(); | |
115 } else { | |
116 arg_type = arg_expr->getType(); | |
117 } | |
118 | |
119 CheckDetails details; | |
120 if (CheckType(arg_type, &details)) { | |
121 return true; | |
122 } | |
123 | |
124 ReportCheckError(details, | |
125 arg_expr->getExprLoc(), | |
126 error_write_param_bad_type_); | |
127 | |
128 return false; | |
129 } | |
130 | |
131 // Checks that IPC::CheckedTuple<> is specialized with allowed types. | |
132 // See CheckType() above for specifics. | |
133 bool CheckIPCVisitor::ValidateCheckedTuple( | |
134 const TemplateSpecializationType* spec) { | |
135 TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl(); | |
136 if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") { | |
137 return true; | |
138 } | |
139 | |
140 bool valid = true; | |
141 for (unsigned i = 0; i != spec->getNumArgs(); ++i) { | |
142 const TemplateArgument& arg = spec->getArg(i); | |
143 CheckDetails details; | |
144 if (CheckTemplateArgument(arg, &details)) { | |
145 continue; | |
146 } | |
147 | |
148 valid = false; | |
149 | |
150 auto* parent_decl = GetParentDecl<Decl>(); | |
151 ReportCheckError( | |
152 details, | |
153 parent_decl ? parent_decl->getLocStart() : SourceLocation(), | |
154 error_tuple_bad_type_); | |
155 } | |
156 | |
157 return valid; | |
158 } | |
159 | |
160 template <typename T> | |
161 const T* CheckIPCVisitor::GetParentDecl() const { | |
162 for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) { | |
163 if (auto* parent = dyn_cast_or_null<T>(*i)) { | |
164 return parent; | |
165 } | |
166 } | |
167 return nullptr; | |
168 } | |
169 | |
170 | |
171 bool CheckIPCVisitor::IsBlacklistedType(QualType type) const { | |
172 return context_->hasSameUnqualifiedType(type, context_->LongTy) || | |
173 context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy); | |
174 } | |
175 | |
176 bool CheckIPCVisitor::IsBlacklistedTypedef(const TypedefNameDecl* tdef) const { | |
177 return blacklisted_typedefs_.find(tdef->getName()) != | |
178 blacklisted_typedefs_.end(); | |
179 } | |
180 | |
181 // Checks that integer type is allowed (not blacklisted). | |
182 bool CheckIPCVisitor::CheckIntegerType(QualType type, | |
183 CheckDetails* details) const { | |
184 bool seen_typedef = false; | |
185 while (true) { | |
186 details->exit_type = type; | |
187 | |
188 if (auto* tdef = dyn_cast<TypedefType>(type)) { | |
189 if (IsBlacklistedTypedef(tdef->getDecl())) { | |
190 return false; | |
191 } | |
192 details->typedefs.push_back(tdef); | |
193 seen_typedef = true; | |
194 } | |
195 | |
196 QualType desugared_type = | |
197 type->getLocallyUnqualifiedSingleStepDesugaredType(); | |
198 if (desugared_type == type) { | |
199 break; | |
200 } | |
201 | |
202 type = desugared_type; | |
203 } | |
204 | |
205 return seen_typedef || !IsBlacklistedType(type); | |
206 } | |
207 | |
208 // Checks that |type| is allowed (not blacklisted), recursively visiting | |
209 // template specializations. | |
210 bool CheckIPCVisitor::CheckType(QualType type, CheckDetails* details) const { | |
211 if (type->isReferenceType()) { | |
212 type = type->getPointeeType(); | |
213 } | |
214 type = type.getLocalUnqualifiedType(); | |
215 | |
216 if (details->entry_type.isNull()) { | |
217 details->entry_type = type; | |
218 } | |
219 | |
220 if (type->isIntegerType()) { | |
221 return CheckIntegerType(type, details); | |
222 } | |
223 | |
224 while (true) { | |
225 if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) { | |
226 for (const TemplateArgument& arg: *spec) { | |
227 if (!CheckTemplateArgument(arg, details)) { | |
228 return false; | |
229 } | |
230 } | |
231 return true; | |
232 } | |
233 | |
234 if (auto* record = dyn_cast<RecordType>(type)) { | |
235 if (auto* spec = dyn_cast<ClassTemplateSpecializationDecl>( | |
236 record->getDecl())) { | |
237 const TemplateArgumentList& args = spec->getTemplateArgs(); | |
238 for (unsigned i = 0; i != args.size(); ++i) { | |
239 if (!CheckTemplateArgument(args[i], details)) { | |
240 return false; | |
241 } | |
242 } | |
243 } | |
244 return true; | |
245 } | |
246 | |
247 if (auto* tdef = dyn_cast<TypedefType>(type)) { | |
248 details->typedefs.push_back(tdef); | |
249 } | |
250 | |
251 QualType desugared_type = | |
252 type->getLocallyUnqualifiedSingleStepDesugaredType(); | |
253 if (desugared_type == type) { | |
254 break; | |
255 } | |
256 | |
257 type = desugared_type; | |
258 } | |
259 | |
260 return true; | |
261 } | |
262 | |
263 bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg, | |
264 CheckDetails* details) const { | |
265 return arg.getKind() != TemplateArgument::Type || | |
266 CheckType(arg.getAsType(), details); | |
267 } | |
268 | |
269 void CheckIPCVisitor::ReportCheckError(const CheckDetails& details, | |
270 SourceLocation loc, | |
271 unsigned error) { | |
272 DiagnosticsEngine& diagnostics = compiler_.getDiagnostics(); | |
273 | |
274 std::string entry_type = details.entry_type.getAsString(); | |
275 std::string exit_type = details.exit_type.getAsString(); | |
276 | |
277 std::string via; | |
278 if (entry_type != exit_type) { | |
279 via = " via '" + entry_type + "'"; | |
280 } | |
281 diagnostics.Report(loc, error) << exit_type << via; | |
282 | |
283 for (const TypedefType* tdef: details.typedefs) { | |
284 diagnostics.Report(tdef->getDecl()->getLocation(), note_see_here_); | |
285 } | |
286 } | |
287 | |
288 } // namespace chrome_checker | |
OLD | NEW |