| 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..611433989713afb96d23c63c177fb0c7caabebde
|
| --- /dev/null
|
| +++ b/tools/clang/plugins/CheckIPCVisitor.cpp
|
| @@ -0,0 +1,391 @@
|
| +// 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"
|
| +
|
| +#include <vector>
|
| +
|
| +#include "llvm/ADT/StringSet.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 kWriteParamTemplateArg[] =
|
| + "[chromium-ipc] IPC::WriteParam() must explicitly specify template "
|
| + "argument (use WriteParam<T>(...)).";
|
| +
|
| +const char kWriteParamWrongTemplate[] =
|
| + "[chromium-ipc] IPC::WriteParam() can only be used in IPC::ParamTraits<> "
|
| + "templates.";
|
| +
|
| +const char kWriteParamBadSignature[] =
|
| + "[chromium-ipc] IPC::WriteParam() is expected to have two arguments.";
|
| +
|
| +const char kNoteSeeHere[] =
|
| + "";
|
| +
|
| +struct CheckDetails {
|
| + QualType entry_type;
|
| + QualType exit_type;
|
| + std::vector<const TypedefType*> typedefs;
|
| +};
|
| +
|
| +bool IsInterestingType(QualType type) {
|
| + return type->isIntegerType();
|
| +}
|
| +
|
| +bool IsBlacklistedType(ASTContext& context, QualType type) {
|
| + return context.hasSameUnqualifiedType(type, context.LongTy) ||
|
| + context.hasSameUnqualifiedType(type, context.UnsignedLongTy);
|
| +}
|
| +
|
| +bool IsBlacklistedTypedef(const TypedefNameDecl* tdef) {
|
| + static llvm::StringSet<> blacklist({
|
| + "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"
|
| + });
|
| + return blacklist.find(tdef->getName()) != blacklist.end();
|
| +}
|
| +
|
| +QualType UnqualifyEntryType(QualType type, CheckDetails* details) {
|
| + if (type->isReferenceType()) {
|
| + type = type->getPointeeType();
|
| + }
|
| + type = type.getLocalUnqualifiedType();
|
| + if (details && details->entry_type == QualType()) {
|
| + details->entry_type = type;
|
| + }
|
| + return type;
|
| +}
|
| +
|
| +// Checks that type is allowed. See comment in the header for details.
|
| +bool CheckType(ASTContext& context,
|
| + QualType type,
|
| + CheckDetails* details = nullptr) {
|
| + type = UnqualifyEntryType(type, details);
|
| +
|
| + if (!IsInterestingType(type)) {
|
| + return true;
|
| + }
|
| +
|
| + bool seen_typedef = false;
|
| + while (true) {
|
| + if (details) {
|
| + details->exit_type = type;
|
| + }
|
| +
|
| + if (auto tdef = dyn_cast<TypedefType>(type)) {
|
| + if (IsBlacklistedTypedef(tdef->getDecl())) {
|
| + return false;
|
| + }
|
| + if (details) {
|
| + 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(context, type);
|
| +}
|
| +
|
| +// Same as CheckType(), but recurses into template specializations.
|
| +bool CheckTemplateType(ASTContext& context,
|
| + QualType type,
|
| + CheckDetails* details = nullptr) {
|
| + type = UnqualifyEntryType(type, details);
|
| +
|
| + if (IsInterestingType(type)) {
|
| + return CheckType(context, type, details);
|
| + }
|
| +
|
| + if (auto tdef = type->getAs<TypedefType>()) {
|
| + if (details) {
|
| + details->typedefs.push_back(tdef);
|
| + }
|
| + return CheckTemplateType(context, tdef->desugar(), details);
|
| + }
|
| +
|
| + if (auto spec = type->getAs<TemplateSpecializationType>()) {
|
| + for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
|
| + if (!CheckTemplateType(context, spec->getArg(i).getAsType(), details)) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void ReportCheckError(clang::DiagnosticsEngine& diagnostics,
|
| + const CheckDetails& details,
|
| + SourceLocation loc,
|
| + unsigned error, unsigned typedef_note) {
|
| + 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(), typedef_note);
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler)
|
| + : compiler_(compiler), context_(nullptr),
|
| + current_decl_(nullptr), current_fn_decl_(nullptr),
|
| + current_spec_decl_(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_template_arg_ = diagnostics.getCustomDiagID(
|
| + DiagnosticsEngine::Error, kWriteParamTemplateArg);
|
| + error_write_param_wrong_template_ = diagnostics.getCustomDiagID(
|
| + DiagnosticsEngine::Error, kWriteParamWrongTemplate);
|
| + error_write_param_bad_signature_ = diagnostics.getCustomDiagID(
|
| + DiagnosticsEngine::Error, kWriteParamBadSignature);
|
| + note_see_here_ = diagnostics.getCustomDiagID(
|
| + DiagnosticsEngine::Note, kNoteSeeHere);
|
| +}
|
| +
|
| +void CheckIPCVisitor::Visit(ASTContext& context) {
|
| + context_ = &context;
|
| + TraverseDecl(context_->getTranslationUnitDecl());
|
| + context_ = nullptr;
|
| +}
|
| +
|
| +bool CheckIPCVisitor::TraverseDecl(Decl* decl) {
|
| + // Update current function
|
| + const FunctionDecl* last_fn_decl = current_fn_decl_;
|
| + auto fn_decl = dyn_cast_or_null<FunctionDecl>(decl);
|
| + if (fn_decl) {
|
| + current_fn_decl_ = fn_decl;
|
| + }
|
| +
|
| + // Update current specialization
|
| + const ClassTemplateSpecializationDecl* last_spec_decl = current_spec_decl_;
|
| + auto spec_decl = dyn_cast_or_null<ClassTemplateSpecializationDecl>(decl);
|
| + if (spec_decl) {
|
| + current_spec_decl_ = spec_decl;
|
| + }
|
| +
|
| + // Update current decl
|
| + const Decl* last_decl = current_decl_;
|
| + current_decl_ = decl;
|
| +
|
| + bool result = Base::TraverseDecl(decl);
|
| +
|
| + if (fn_decl) {
|
| + current_fn_decl_ = last_fn_decl;
|
| + }
|
| + if (spec_decl) {
|
| + current_spec_decl_ = last_spec_decl;
|
| + }
|
| + current_decl_ = last_decl;
|
| +
|
| + return result;
|
| +}
|
| +
|
| +bool CheckIPCVisitor::VisitTemplateSpecializationType(
|
| + TemplateSpecializationType* spec) {
|
| + ValidateCheckedTuple(spec);
|
| + return true;
|
| +}
|
| +
|
| +bool CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) {
|
| + ValidateWriteParam(call_expr);
|
| + return true;
|
| +}
|
| +
|
| +bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) const {
|
| + if (auto lookup_expr = dyn_cast<UnresolvedLookupExpr>(
|
| + call_expr->getCallee())) {
|
| + return ValidateWriteParamInsideTemplate(call_expr, lookup_expr);
|
| + }
|
| +
|
| + const FunctionDecl* callee_decl = call_expr->getDirectCallee();
|
| + if (!callee_decl ||
|
| + callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") {
|
| + return true;
|
| + }
|
| +
|
| + return ValidateWriteParamSignature(call_expr) &&
|
| + ValidateWriteParamCaller(call_expr) &&
|
| + ValidateWriteParamArgument(call_expr->getArg(1));
|
| +}
|
| +
|
| +// Checks that IPC::WriteParam() has expected signature.
|
| +bool CheckIPCVisitor::ValidateWriteParamSignature(
|
| + const CallExpr* call_expr) const {
|
| + if (call_expr->getNumArgs() != 2) {
|
| + compiler_.getDiagnostics().Report(
|
| + call_expr->getExprLoc(), error_write_param_bad_signature_);
|
| + return false;
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +// Checks that when WriteParam() is used inside a template, it explicitly
|
| +// depends on template argument: WriteParam<T>(...)
|
| +bool CheckIPCVisitor::ValidateWriteParamInsideTemplate(
|
| + const CallExpr* call_expr,
|
| + const UnresolvedLookupExpr* lookup_expr) const {
|
| + if (!IsInsideParamTraits() ||
|
| + lookup_expr->getName().getAsString() != "WriteParam") {
|
| + return true;
|
| + }
|
| +
|
| + if (!ValidateWriteParamSignature(call_expr)) {
|
| + return false;
|
| + }
|
| +
|
| + if (lookup_expr->getNumTemplateArgs() == 1) {
|
| + const TemplateArgument& template_arg =
|
| + lookup_expr->getTemplateArgs()->getArgument();
|
| + bool explicitly_dependent =
|
| + template_arg.getKind() == TemplateArgument::Type &&
|
| + isa<TemplateTypeParmType>(template_arg.getAsType());
|
| + if (explicitly_dependent) {
|
| + return true;
|
| + }
|
| + }
|
| +
|
| + compiler_.getDiagnostics().Report(
|
| + call_expr->getExprLoc(), error_write_param_template_arg_);
|
| + return false;
|
| +}
|
| +
|
| +// Checks that IPC::WriteParam() argument type is allowed.
|
| +// See CheckType() above for specifics.
|
| +bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) const {
|
| + if (current_fn_decl_ &&
|
| + current_fn_decl_->getTemplatedKind() != FunctionDecl::TK_NonTemplate) {
|
| + // Skip all specializations - we checked templates earlier in
|
| + // ValidateWriteParamInsideTemplate().
|
| + return true;
|
| + }
|
| +
|
| + const Expr* arg_type_expr = arg_expr;
|
| + if (auto tmp_expr = dyn_cast<MaterializeTemporaryExpr>(arg_type_expr)) {
|
| + arg_type_expr = tmp_expr->GetTemporaryExpr();
|
| + }
|
| +
|
| + QualType arg_type;
|
| + if (auto cast_expr = dyn_cast<ExplicitCastExpr>(arg_type_expr)) {
|
| + arg_type = cast_expr->getTypeAsWritten();
|
| + } else {
|
| + if (auto cast_expr = dyn_cast<ImplicitCastExpr>(arg_type_expr)) {
|
| + arg_type_expr = cast_expr->getSubExpr();
|
| + }
|
| + arg_type = arg_type_expr->getType();
|
| + }
|
| +
|
| + if (CheckType(*context_, arg_type)) {
|
| + return true;
|
| + }
|
| +
|
| + CheckDetails details;
|
| + CheckType(*context_, arg_type, &details);
|
| +
|
| + ReportCheckError(compiler_.getDiagnostics(),
|
| + details,
|
| + arg_expr->getExprLoc(),
|
| + error_write_param_bad_type_, note_see_here_);
|
| +
|
| + return false;
|
| +}
|
| +
|
| +// Checks that when WriteParam() is called from a template, it's
|
| +// in fact IPC::ParamTraits<> template.
|
| +bool CheckIPCVisitor::ValidateWriteParamCaller(
|
| + const CallExpr* call_expr) const {
|
| + if (!current_fn_decl_ ||
|
| + current_fn_decl_->getTemplatedKind() == FunctionDecl::TK_NonTemplate) {
|
| + // Skip if we're not in template.
|
| + return true;
|
| + }
|
| +
|
| + // If we're in a template of any kind, check that it's ParamTraits
|
| + if (IsInsideParamTraits()) {
|
| + return true;
|
| + }
|
| +
|
| + compiler_.getDiagnostics().Report(
|
| + call_expr->getExprLoc(), error_write_param_wrong_template_);
|
| + return false;
|
| +}
|
| +
|
| +// Checks that IPC::CheckedTuple<> is specialized with allowed types.
|
| +// See CheckType() above for specifics.
|
| +bool CheckIPCVisitor::ValidateCheckedTuple(
|
| + const TemplateSpecializationType* spec) const {
|
| + TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl();
|
| + if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") {
|
| + return true;
|
| + }
|
| +
|
| + bool valid = true;
|
| + for (unsigned i = 0; i != spec->getNumArgs(); ++i) {
|
| + QualType arg_type = spec->getArg(i).getAsType();
|
| + if (CheckTemplateType(*context_, arg_type)) {
|
| + continue;
|
| + }
|
| +
|
| + valid = false;
|
| +
|
| + CheckDetails details;
|
| + CheckTemplateType(*context_, arg_type, &details);
|
| +
|
| + ReportCheckError(compiler_.getDiagnostics(),
|
| + details,
|
| + current_decl_->getLocStart(),
|
| + error_tuple_bad_type_, note_see_here_);
|
| + }
|
| +
|
| + return valid;
|
| +}
|
| +
|
| +bool CheckIPCVisitor::IsInsideParamTraits() const {
|
| + return current_spec_decl_ &&
|
| + current_spec_decl_->getQualifiedNameAsString() == "IPC::ParamTraits";
|
| +}
|
| +
|
| +} // namespace chrome_checker
|
|
|