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
7using namespace clang;
8
9namespace chrome_checker {
10
11namespace {
12
13const char kWriteParamBadType[] =
14    "[chromium-ipc] IPC::WriteParam() is called on blacklisted type '%0'%1.";
15
16const char kTupleBadType[] =
17    "[chromium-ipc] IPC tuple references banned type '%0'%1.";
18
19const char kWriteParamBadSignature[] =
20    "[chromium-ipc] IPC::WriteParam() is expected to have two arguments.";
21
22const char kNoteSeeHere[] =
23    "see here";
24
25}  // namespace
26
27CheckIPCVisitor::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
57void CheckIPCVisitor::BeginDecl(Decl* decl) {
58  decl_stack_.push_back(decl);
59}
60
61void CheckIPCVisitor::EndDecl() {
62  decl_stack_.pop_back();
63}
64
65void CheckIPCVisitor::VisitTemplateSpecializationType(
66    TemplateSpecializationType* spec) {
67  ValidateCheckedTuple(spec);
68}
69
70void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
71  ValidateWriteParam(call_expr);
72}
73
74bool 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.
86bool 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.
98bool 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.
133bool 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
160template <typename T>
161const 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
171bool CheckIPCVisitor::IsBlacklistedType(QualType type) const {
172  return context_->hasSameUnqualifiedType(type, context_->LongTy) ||
173      context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy);
174}
175
176bool 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).
182bool 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.
210bool 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
263bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg,
264                                            CheckDetails* details) const {
265  return arg.getKind() != TemplateArgument::Type ||
266      CheckType(arg.getAsType(), details);
267}
268
269void 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