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
CheckIPCVisitor(CompilerInstance & compiler)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
BeginDecl(Decl * decl)57 void CheckIPCVisitor::BeginDecl(Decl* decl) {
58 decl_stack_.push_back(decl);
59 }
60
EndDecl()61 void CheckIPCVisitor::EndDecl() {
62 decl_stack_.pop_back();
63 }
64
VisitTemplateSpecializationType(TemplateSpecializationType * spec)65 void CheckIPCVisitor::VisitTemplateSpecializationType(
66 TemplateSpecializationType* spec) {
67 ValidateCheckedTuple(spec);
68 }
69
VisitCallExpr(CallExpr * call_expr)70 void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
71 ValidateWriteParam(call_expr);
72 }
73
ValidateWriteParam(const CallExpr * call_expr)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.
ValidateWriteParamSignature(const CallExpr * call_expr)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.
ValidateWriteParamArgument(const Expr * arg_expr)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.
ValidateCheckedTuple(const TemplateSpecializationType * spec)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>
GetParentDecl() const161 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
IsBlacklistedType(QualType type) const171 bool CheckIPCVisitor::IsBlacklistedType(QualType type) const {
172 return context_->hasSameUnqualifiedType(type, context_->LongTy) ||
173 context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy);
174 }
175
IsBlacklistedTypedef(const TypedefNameDecl * tdef) const176 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).
CheckIntegerType(QualType type,CheckDetails * details) const182 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.
CheckType(QualType type,CheckDetails * details) const210 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
CheckTemplateArgument(const TemplateArgument & arg,CheckDetails * details) const263 bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg,
264 CheckDetails* details) const {
265 return arg.getKind() != TemplateArgument::Type ||
266 CheckType(arg.getAsType(), details);
267 }
268
ReportCheckError(const CheckDetails & details,SourceLocation loc,unsigned error)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
289