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 #include <vector> |
| 8 |
| 9 #include "llvm/ADT/StringSet.h" |
| 10 |
| 11 using namespace clang; |
| 12 |
| 13 namespace chrome_checker { |
| 14 |
| 15 namespace { |
| 16 |
| 17 const char kWriteParamBadType[] = |
| 18 "[chromium-ipc] IPC::WriteParam() is called on blacklisted type '%0'%1."; |
| 19 |
| 20 const char kTupleBadType[] = |
| 21 "[chromium-ipc] IPC tuple references banned type '%0'%1."; |
| 22 |
| 23 const char kWriteParamTemplateArg[] = |
| 24 "[chromium-ipc] IPC::WriteParam() must explicitly specify template " |
| 25 "argument (use WriteParam<T>(...))."; |
| 26 |
| 27 const char kWriteParamWrongTemplate[] = |
| 28 "[chromium-ipc] IPC::WriteParam() can only be used in IPC::ParamTraits<> " |
| 29 "templates."; |
| 30 |
| 31 const char kWriteParamBadSignature[] = |
| 32 "[chromium-ipc] IPC::WriteParam() is expected to have two arguments."; |
| 33 |
| 34 const char kNoteSeeHere[] = |
| 35 ""; |
| 36 |
| 37 struct CheckDetails { |
| 38 QualType entry_type; |
| 39 QualType exit_type; |
| 40 std::vector<const TypedefType*> typedefs; |
| 41 }; |
| 42 |
| 43 bool IsInterestingType(QualType type) { |
| 44 return type->isIntegerType(); |
| 45 } |
| 46 |
| 47 bool IsBlacklistedType(ASTContext& context, QualType type) { |
| 48 return context.hasSameUnqualifiedType(type, context.LongTy) || |
| 49 context.hasSameUnqualifiedType(type, context.UnsignedLongTy); |
| 50 } |
| 51 |
| 52 bool IsBlacklistedTypedef(const TypedefNameDecl* tdef) { |
| 53 static llvm::StringSet<> blacklist({ |
| 54 "intmax_t", |
| 55 "uintmax_t", |
| 56 "intptr_t", |
| 57 "uintptr_t", |
| 58 "wint_t", |
| 59 "size_t", |
| 60 "rsize_t", |
| 61 "ssize_t", |
| 62 "ptrdiff_t", |
| 63 "dev_t", |
| 64 "off_t", |
| 65 "clock_t", |
| 66 "time_t", |
| 67 "suseconds_t" |
| 68 }); |
| 69 return blacklist.find(tdef->getName()) != blacklist.end(); |
| 70 } |
| 71 |
| 72 QualType UnqualifyEntryType(QualType type, CheckDetails* details) { |
| 73 if (type->isReferenceType()) { |
| 74 type = type->getPointeeType(); |
| 75 } |
| 76 type = type.getLocalUnqualifiedType(); |
| 77 if (details && details->entry_type == QualType()) { |
| 78 details->entry_type = type; |
| 79 } |
| 80 return type; |
| 81 } |
| 82 |
| 83 // Checks that type is allowed. See comment in the header for details. |
| 84 bool CheckType(ASTContext& context, |
| 85 QualType type, |
| 86 CheckDetails* details = nullptr) { |
| 87 type = UnqualifyEntryType(type, details); |
| 88 |
| 89 if (!IsInterestingType(type)) { |
| 90 return true; |
| 91 } |
| 92 |
| 93 bool seen_typedef = false; |
| 94 while (true) { |
| 95 if (details) { |
| 96 details->exit_type = type; |
| 97 } |
| 98 |
| 99 if (auto tdef = dyn_cast<TypedefType>(type)) { |
| 100 if (IsBlacklistedTypedef(tdef->getDecl())) { |
| 101 return false; |
| 102 } |
| 103 if (details) { |
| 104 details->typedefs.push_back(tdef); |
| 105 } |
| 106 seen_typedef = true; |
| 107 } |
| 108 |
| 109 QualType desugared_type = |
| 110 type->getLocallyUnqualifiedSingleStepDesugaredType(); |
| 111 if (desugared_type == type) { |
| 112 break; |
| 113 } |
| 114 |
| 115 type = desugared_type; |
| 116 } |
| 117 |
| 118 return seen_typedef || !IsBlacklistedType(context, type); |
| 119 } |
| 120 |
| 121 // Same as CheckType(), but recurses into template specializations. |
| 122 bool CheckTemplateType(ASTContext& context, |
| 123 QualType type, |
| 124 CheckDetails* details = nullptr) { |
| 125 type = UnqualifyEntryType(type, details); |
| 126 |
| 127 if (IsInterestingType(type)) { |
| 128 return CheckType(context, type, details); |
| 129 } |
| 130 |
| 131 if (auto tdef = type->getAs<TypedefType>()) { |
| 132 if (details) { |
| 133 details->typedefs.push_back(tdef); |
| 134 } |
| 135 return CheckTemplateType(context, tdef->desugar(), details); |
| 136 } |
| 137 |
| 138 if (auto spec = type->getAs<TemplateSpecializationType>()) { |
| 139 for (unsigned i = 0; i != spec->getNumArgs(); ++i) { |
| 140 if (!CheckTemplateType(context, spec->getArg(i).getAsType(), details)) { |
| 141 return false; |
| 142 } |
| 143 } |
| 144 return true; |
| 145 } |
| 146 |
| 147 return true; |
| 148 } |
| 149 |
| 150 void ReportCheckError(clang::DiagnosticsEngine& diagnostics, |
| 151 const CheckDetails& details, |
| 152 SourceLocation loc, |
| 153 unsigned error, unsigned typedef_note) { |
| 154 std::string entry_type = details.entry_type.getAsString(); |
| 155 std::string exit_type = details.exit_type.getAsString(); |
| 156 |
| 157 std::string via; |
| 158 if (entry_type != exit_type) { |
| 159 via = " via '" + entry_type + "'"; |
| 160 } |
| 161 diagnostics.Report(loc, error) << exit_type << via; |
| 162 |
| 163 for (const TypedefType* tdef: details.typedefs) { |
| 164 diagnostics.Report(tdef->getDecl()->getLocation(), typedef_note); |
| 165 } |
| 166 } |
| 167 |
| 168 } // namespace |
| 169 |
| 170 CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler) |
| 171 : compiler_(compiler), context_(nullptr), |
| 172 current_decl_(nullptr), current_fn_decl_(nullptr), |
| 173 current_spec_decl_(nullptr) { |
| 174 auto& diagnostics = compiler_.getDiagnostics(); |
| 175 error_write_param_bad_type_ = diagnostics.getCustomDiagID( |
| 176 DiagnosticsEngine::Error, kWriteParamBadType); |
| 177 error_tuple_bad_type_ = diagnostics.getCustomDiagID( |
| 178 DiagnosticsEngine::Error, kTupleBadType); |
| 179 error_write_param_template_arg_ = diagnostics.getCustomDiagID( |
| 180 DiagnosticsEngine::Error, kWriteParamTemplateArg); |
| 181 error_write_param_wrong_template_ = diagnostics.getCustomDiagID( |
| 182 DiagnosticsEngine::Error, kWriteParamWrongTemplate); |
| 183 error_write_param_bad_signature_ = diagnostics.getCustomDiagID( |
| 184 DiagnosticsEngine::Error, kWriteParamBadSignature); |
| 185 note_see_here_ = diagnostics.getCustomDiagID( |
| 186 DiagnosticsEngine::Note, kNoteSeeHere); |
| 187 } |
| 188 |
| 189 void CheckIPCVisitor::Visit(ASTContext& context) { |
| 190 context_ = &context; |
| 191 TraverseDecl(context_->getTranslationUnitDecl()); |
| 192 context_ = nullptr; |
| 193 } |
| 194 |
| 195 bool CheckIPCVisitor::TraverseDecl(Decl* decl) { |
| 196 // Update current function |
| 197 const FunctionDecl* last_fn_decl = current_fn_decl_; |
| 198 auto fn_decl = dyn_cast_or_null<FunctionDecl>(decl); |
| 199 if (fn_decl) { |
| 200 current_fn_decl_ = fn_decl; |
| 201 } |
| 202 |
| 203 // Update current specialization |
| 204 const ClassTemplateSpecializationDecl* last_spec_decl = current_spec_decl_; |
| 205 auto spec_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(decl); |
| 206 if (spec_decl) { |
| 207 current_spec_decl_ = spec_decl; |
| 208 } |
| 209 |
| 210 // Update current decl |
| 211 const Decl* last_decl = current_decl_; |
| 212 current_decl_ = decl; |
| 213 |
| 214 bool result = Base::TraverseDecl(decl); |
| 215 |
| 216 if (fn_decl) { |
| 217 current_fn_decl_ = last_fn_decl; |
| 218 } |
| 219 if (spec_decl) { |
| 220 current_spec_decl_ = last_spec_decl; |
| 221 } |
| 222 current_decl_ = last_decl; |
| 223 |
| 224 return result; |
| 225 } |
| 226 |
| 227 bool CheckIPCVisitor::VisitTemplateSpecializationType( |
| 228 TemplateSpecializationType* spec) { |
| 229 ValidateCheckedTuple(spec); |
| 230 return true; |
| 231 } |
| 232 |
| 233 bool CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) { |
| 234 ValidateWriteParam(call_expr); |
| 235 return true; |
| 236 } |
| 237 |
| 238 bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) const { |
| 239 if (auto lookup_expr = dyn_cast<UnresolvedLookupExpr>( |
| 240 call_expr->getCallee())) { |
| 241 return ValidateWriteParamInsideTemplate(call_expr, lookup_expr); |
| 242 } |
| 243 |
| 244 const FunctionDecl* callee_decl = call_expr->getDirectCallee(); |
| 245 if (!callee_decl || |
| 246 callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") { |
| 247 return true; |
| 248 } |
| 249 |
| 250 return ValidateWriteParamSignature(call_expr) && |
| 251 ValidateWriteParamCaller(call_expr) && |
| 252 ValidateWriteParamArgument(call_expr->getArg(1)); |
| 253 } |
| 254 |
| 255 // Checks that IPC::WriteParam() has expected signature. |
| 256 bool CheckIPCVisitor::ValidateWriteParamSignature( |
| 257 const CallExpr* call_expr) const { |
| 258 if (call_expr->getNumArgs() != 2) { |
| 259 compiler_.getDiagnostics().Report( |
| 260 call_expr->getExprLoc(), error_write_param_bad_signature_); |
| 261 return false; |
| 262 } |
| 263 return true; |
| 264 } |
| 265 |
| 266 // Checks that when WriteParam() is used inside a template, it explicitly |
| 267 // depends on template argument: WriteParam<T>(...) |
| 268 bool CheckIPCVisitor::ValidateWriteParamInsideTemplate( |
| 269 const CallExpr* call_expr, |
| 270 const UnresolvedLookupExpr* lookup_expr) const { |
| 271 if (!IsInsideParamTraits() || |
| 272 lookup_expr->getName().getAsString() != "WriteParam") { |
| 273 return true; |
| 274 } |
| 275 |
| 276 if (!ValidateWriteParamSignature(call_expr)) { |
| 277 return false; |
| 278 } |
| 279 |
| 280 if (lookup_expr->getNumTemplateArgs() == 1) { |
| 281 const TemplateArgument& template_arg = |
| 282 lookup_expr->getTemplateArgs()->getArgument(); |
| 283 bool explicitly_dependent = |
| 284 template_arg.getKind() == TemplateArgument::Type && |
| 285 isa<TemplateTypeParmType>(template_arg.getAsType()); |
| 286 if (explicitly_dependent) { |
| 287 return true; |
| 288 } |
| 289 } |
| 290 |
| 291 compiler_.getDiagnostics().Report( |
| 292 call_expr->getExprLoc(), error_write_param_template_arg_); |
| 293 return false; |
| 294 } |
| 295 |
| 296 // Checks that IPC::WriteParam() argument type is allowed. |
| 297 // See CheckType() above for specifics. |
| 298 bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) const { |
| 299 if (current_fn_decl_ && |
| 300 current_fn_decl_->getTemplatedKind() != FunctionDecl::TK_NonTemplate) { |
| 301 // Skip all specializations - we checked templates earlier in |
| 302 // ValidateWriteParamInsideTemplate(). |
| 303 return true; |
| 304 } |
| 305 |
| 306 const Expr* arg_type_expr = arg_expr; |
| 307 if (auto tmp_expr = dyn_cast<MaterializeTemporaryExpr>(arg_type_expr)) { |
| 308 arg_type_expr = tmp_expr->GetTemporaryExpr(); |
| 309 } |
| 310 |
| 311 QualType arg_type; |
| 312 if (auto cast_expr = dyn_cast<ExplicitCastExpr>(arg_type_expr)) { |
| 313 arg_type = cast_expr->getTypeAsWritten(); |
| 314 } else { |
| 315 if (auto cast_expr = dyn_cast<ImplicitCastExpr>(arg_type_expr)) { |
| 316 arg_type_expr = cast_expr->getSubExpr(); |
| 317 } |
| 318 arg_type = arg_type_expr->getType(); |
| 319 } |
| 320 |
| 321 if (CheckType(*context_, arg_type)) { |
| 322 return true; |
| 323 } |
| 324 |
| 325 CheckDetails details; |
| 326 CheckType(*context_, arg_type, &details); |
| 327 |
| 328 ReportCheckError(compiler_.getDiagnostics(), |
| 329 details, |
| 330 arg_expr->getExprLoc(), |
| 331 error_write_param_bad_type_, note_see_here_); |
| 332 |
| 333 return false; |
| 334 } |
| 335 |
| 336 // Checks that when WriteParam() is called from a template, it's |
| 337 // in fact IPC::ParamTraits<> template. |
| 338 bool CheckIPCVisitor::ValidateWriteParamCaller( |
| 339 const CallExpr* call_expr) const { |
| 340 if (!current_fn_decl_ || |
| 341 current_fn_decl_->getTemplatedKind() == FunctionDecl::TK_NonTemplate) { |
| 342 // Skip if we're not in template. |
| 343 return true; |
| 344 } |
| 345 |
| 346 // If we're in a template of any kind, check that it's ParamTraits |
| 347 if (IsInsideParamTraits()) { |
| 348 return true; |
| 349 } |
| 350 |
| 351 compiler_.getDiagnostics().Report( |
| 352 call_expr->getExprLoc(), error_write_param_wrong_template_); |
| 353 return false; |
| 354 } |
| 355 |
| 356 // Checks that IPC::CheckedTuple<> is specialized with allowed types. |
| 357 // See CheckType() above for specifics. |
| 358 bool CheckIPCVisitor::ValidateCheckedTuple( |
| 359 const TemplateSpecializationType* spec) const { |
| 360 TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl(); |
| 361 if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") { |
| 362 return true; |
| 363 } |
| 364 |
| 365 bool valid = true; |
| 366 for (unsigned i = 0; i != spec->getNumArgs(); ++i) { |
| 367 QualType arg_type = spec->getArg(i).getAsType(); |
| 368 if (CheckTemplateType(*context_, arg_type)) { |
| 369 continue; |
| 370 } |
| 371 |
| 372 valid = false; |
| 373 |
| 374 CheckDetails details; |
| 375 CheckTemplateType(*context_, arg_type, &details); |
| 376 |
| 377 ReportCheckError(compiler_.getDiagnostics(), |
| 378 details, |
| 379 current_decl_->getLocStart(), |
| 380 error_tuple_bad_type_, note_see_here_); |
| 381 } |
| 382 |
| 383 return valid; |
| 384 } |
| 385 |
| 386 bool CheckIPCVisitor::IsInsideParamTraits() const { |
| 387 return current_spec_decl_ && |
| 388 current_spec_decl_->getQualifiedNameAsString() == "IPC::ParamTraits"; |
| 389 } |
| 390 |
| 391 } // namespace chrome_checker |
OLD | NEW |