• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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