|  | // Copyright (c) 2016 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "CheckIPCVisitor.h" | 
|  |  | 
|  | using namespace clang; | 
|  |  | 
|  | namespace chrome_checker { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | const char kWriteParamBadType[] = | 
|  | "[chromium-ipc] IPC::WriteParam() is called on blacklisted type '%0'%1."; | 
|  |  | 
|  | const char kTupleBadType[] = | 
|  | "[chromium-ipc] IPC tuple references banned type '%0'%1."; | 
|  |  | 
|  | const char kWriteParamBadSignature[] = | 
|  | "[chromium-ipc] IPC::WriteParam() is expected to have two arguments."; | 
|  |  | 
|  | const char kNoteSeeHere[] = | 
|  | "see here"; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler) | 
|  | : compiler_(compiler), context_(nullptr) { | 
|  | auto& diagnostics = compiler_.getDiagnostics(); | 
|  | error_write_param_bad_type_ = diagnostics.getCustomDiagID( | 
|  | DiagnosticsEngine::Error, kWriteParamBadType); | 
|  | error_tuple_bad_type_ = diagnostics.getCustomDiagID( | 
|  | DiagnosticsEngine::Error, kTupleBadType); | 
|  | error_write_param_bad_signature_ = diagnostics.getCustomDiagID( | 
|  | DiagnosticsEngine::Error, kWriteParamBadSignature); | 
|  | note_see_here_ = diagnostics.getCustomDiagID( | 
|  | DiagnosticsEngine::Note, kNoteSeeHere); | 
|  |  | 
|  | blacklisted_typedefs_ = llvm::StringSet<>({ | 
|  | "intmax_t", | 
|  | "uintmax_t", | 
|  | "intptr_t", | 
|  | "uintptr_t", | 
|  | "wint_t", | 
|  | "size_t", | 
|  | "rsize_t", | 
|  | "ssize_t", | 
|  | "ptrdiff_t", | 
|  | "dev_t", | 
|  | "off_t", | 
|  | "clock_t", | 
|  | "time_t", | 
|  | "suseconds_t" | 
|  | }); | 
|  | } | 
|  |  | 
|  | void CheckIPCVisitor::BeginDecl(Decl* decl) { | 
|  | decl_stack_.push_back(decl); | 
|  | } | 
|  |  | 
|  | void CheckIPCVisitor::EndDecl() { | 
|  | decl_stack_.pop_back(); | 
|  | } | 
|  |  | 
|  | void CheckIPCVisitor::VisitTemplateSpecializationType( | 
|  | TemplateSpecializationType* spec) { | 
|  | ValidateCheckedTuple(spec); | 
|  | } | 
|  |  | 
|  | void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) { | 
|  | ValidateWriteParam(call_expr); | 
|  | } | 
|  |  | 
|  | bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) { | 
|  | const FunctionDecl* callee_decl = call_expr->getDirectCallee(); | 
|  | if (!callee_decl || | 
|  | callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | return ValidateWriteParamSignature(call_expr) && | 
|  | ValidateWriteParamArgument(call_expr->getArg(1)); | 
|  | } | 
|  |  | 
|  | // Checks that IPC::WriteParam() has expected signature. | 
|  | bool CheckIPCVisitor::ValidateWriteParamSignature( | 
|  | const CallExpr* call_expr) { | 
|  | if (call_expr->getNumArgs() != 2) { | 
|  | compiler_.getDiagnostics().Report( | 
|  | call_expr->getExprLoc(), error_write_param_bad_signature_); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Checks that IPC::WriteParam() argument type is allowed. | 
|  | // See CheckType() for specifics. | 
|  | bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) { | 
|  | if (auto* parent_fn_decl = GetParentDecl<FunctionDecl>()) { | 
|  | auto template_kind = parent_fn_decl->getTemplatedKind(); | 
|  | if (template_kind != FunctionDecl::TK_NonTemplate && | 
|  | template_kind != FunctionDecl::TK_FunctionTemplate) { | 
|  | // Skip all specializations - we don't check WriteParam() on dependent | 
|  | // types (typedef info gets lost), and we checked all non-dependent uses | 
|  | // earlier (when we checked the template itself). | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | QualType arg_type; | 
|  |  | 
|  | arg_expr = arg_expr->IgnoreImplicit(); | 
|  | if (auto* cast_expr = dyn_cast<ExplicitCastExpr>(arg_expr)) { | 
|  | arg_type = cast_expr->getTypeAsWritten(); | 
|  | } else { | 
|  | arg_type = arg_expr->getType(); | 
|  | } | 
|  |  | 
|  | CheckDetails details; | 
|  | if (CheckType(arg_type, &details)) { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | ReportCheckError(details, | 
|  | arg_expr->getExprLoc(), | 
|  | error_write_param_bad_type_); | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Checks that IPC::CheckedTuple<> is specialized with allowed types. | 
|  | // See CheckType() above for specifics. | 
|  | bool CheckIPCVisitor::ValidateCheckedTuple( | 
|  | const TemplateSpecializationType* spec) { | 
|  | TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl(); | 
|  | if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool valid = true; | 
|  | for (unsigned i = 0; i != spec->getNumArgs(); ++i) { | 
|  | const TemplateArgument& arg = spec->getArg(i); | 
|  | CheckDetails details; | 
|  | if (CheckTemplateArgument(arg, &details)) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | valid = false; | 
|  |  | 
|  | auto* parent_decl = GetParentDecl<Decl>(); | 
|  | ReportCheckError( | 
|  | details, | 
|  | parent_decl ? parent_decl->getLocStart() : SourceLocation(), | 
|  | error_tuple_bad_type_); | 
|  | } | 
|  |  | 
|  | return valid; | 
|  | } | 
|  |  | 
|  | template <typename T> | 
|  | const T* CheckIPCVisitor::GetParentDecl() const { | 
|  | for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) { | 
|  | if (auto* parent = dyn_cast_or_null<T>(*i)) { | 
|  | return parent; | 
|  | } | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  |  | 
|  | bool CheckIPCVisitor::IsBlacklistedType(QualType type) const { | 
|  | return context_->hasSameUnqualifiedType(type, context_->LongTy) || | 
|  | context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy); | 
|  | } | 
|  |  | 
|  | bool CheckIPCVisitor::IsBlacklistedTypedef(const TypedefNameDecl* tdef) const { | 
|  | return blacklisted_typedefs_.find(tdef->getName()) != | 
|  | blacklisted_typedefs_.end(); | 
|  | } | 
|  |  | 
|  | // Checks that integer type is allowed (not blacklisted). | 
|  | bool CheckIPCVisitor::CheckIntegerType(QualType type, | 
|  | CheckDetails* details) const { | 
|  | bool seen_typedef = false; | 
|  | while (true) { | 
|  | details->exit_type = type; | 
|  |  | 
|  | if (auto* tdef = dyn_cast<TypedefType>(type)) { | 
|  | if (IsBlacklistedTypedef(tdef->getDecl())) { | 
|  | return false; | 
|  | } | 
|  | details->typedefs.push_back(tdef); | 
|  | seen_typedef = true; | 
|  | } | 
|  |  | 
|  | QualType desugared_type = | 
|  | type->getLocallyUnqualifiedSingleStepDesugaredType(); | 
|  | if (desugared_type == type) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | type = desugared_type; | 
|  | } | 
|  |  | 
|  | return seen_typedef || !IsBlacklistedType(type); | 
|  | } | 
|  |  | 
|  | // Checks that |type| is allowed (not blacklisted), recursively visiting | 
|  | // template specializations. | 
|  | bool CheckIPCVisitor::CheckType(QualType type, CheckDetails* details) const { | 
|  | if (type->isReferenceType()) { | 
|  | type = type->getPointeeType(); | 
|  | } | 
|  | type = type.getLocalUnqualifiedType(); | 
|  |  | 
|  | if (details->entry_type.isNull()) { | 
|  | details->entry_type = type; | 
|  | } | 
|  |  | 
|  | if (type->isIntegerType()) { | 
|  | return CheckIntegerType(type, details); | 
|  | } | 
|  |  | 
|  | while (true) { | 
|  | if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) { | 
|  | for (const TemplateArgument& arg: *spec) { | 
|  | if (!CheckTemplateArgument(arg, details)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (auto* record = dyn_cast<RecordType>(type)) { | 
|  | if (auto* spec = dyn_cast<ClassTemplateSpecializationDecl>( | 
|  | record->getDecl())) { | 
|  | const TemplateArgumentList& args = spec->getTemplateArgs(); | 
|  | for (unsigned i = 0; i != args.size(); ++i) { | 
|  | if (!CheckTemplateArgument(args[i], details)) { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (auto* tdef = dyn_cast<TypedefType>(type)) { | 
|  | details->typedefs.push_back(tdef); | 
|  | } | 
|  |  | 
|  | QualType desugared_type = | 
|  | type->getLocallyUnqualifiedSingleStepDesugaredType(); | 
|  | if (desugared_type == type) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | type = desugared_type; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg, | 
|  | CheckDetails* details) const { | 
|  | return arg.getKind() != TemplateArgument::Type || | 
|  | CheckType(arg.getAsType(), details); | 
|  | } | 
|  |  | 
|  | void CheckIPCVisitor::ReportCheckError(const CheckDetails& details, | 
|  | SourceLocation loc, | 
|  | unsigned error) { | 
|  | DiagnosticsEngine& diagnostics = compiler_.getDiagnostics(); | 
|  |  | 
|  | std::string entry_type = details.entry_type.getAsString(); | 
|  | std::string exit_type = details.exit_type.getAsString(); | 
|  |  | 
|  | std::string via; | 
|  | if (entry_type != exit_type) { | 
|  | via = " via '" + entry_type + "'"; | 
|  | } | 
|  | diagnostics.Report(loc, error) << exit_type << via; | 
|  |  | 
|  | for (const TypedefType* tdef: details.typedefs) { | 
|  | diagnostics.Report(tdef->getDecl()->getLocation(), note_see_here_); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace chrome_checker |