// 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