| Index: tools/clang/plugins/CheckIPCVisitor.cpp
|
| diff --git a/tools/clang/plugins/CheckIPCVisitor.cpp b/tools/clang/plugins/CheckIPCVisitor.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b123b0130ed1c5dd3d5690476a149a84187271ab
|
| --- /dev/null
|
| +++ b/tools/clang/plugins/CheckIPCVisitor.cpp
|
| @@ -0,0 +1,288 @@
|
| +// 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
|
|
|