Chromium Code Reviews| Index: tools/clang/plugins/CheckIPCAction.cpp |
| diff --git a/tools/clang/plugins/CheckIPCAction.cpp b/tools/clang/plugins/CheckIPCAction.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..1fa58fec5ab8d18d14ca80029337efb0446e71ed |
| --- /dev/null |
| +++ b/tools/clang/plugins/CheckIPCAction.cpp |
| @@ -0,0 +1,249 @@ |
| +// 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 "clang/Frontend/FrontendPluginRegistry.h" |
| +#include "clang/AST/AST.h" |
| +#include "clang/AST/ASTConsumer.h" |
| +#include "clang/AST/RecursiveASTVisitor.h" |
| +#include "clang/Frontend/CompilerInstance.h" |
| +#include "llvm/Support/raw_ostream.h" |
| + |
| +#include <stack> |
| + |
| +using namespace clang; |
| + |
| +namespace { |
| + |
| +const char kWriteParamSizeT[] = |
| + "[chromium-ipc] IPC::WriteParam() is called on size_t."; |
| + |
| +const char kWriteParamTemplate[] = |
| + "[chromium-ipc] IPC::WriteParam() is called in a template instantiation " |
| + "on a type equivalent to size_t."; |
| + |
| +const char kNoteInstantiation[] = |
| + "instantiation: %0"; |
| + |
| +const char kTupleSizeT[] = |
| + "[chromium-ipc] IPC::ParamTuple specialized with size_t."; |
| + |
| + |
| +bool IsSizeT(ASTContext& context, QualType type) { |
| + type = type.getLocalUnqualifiedType(); |
| + if (!context.hasSameUnqualifiedType(type, context.getSizeType())) { |
| + return false; |
| + } |
| + while (true) { |
| + if (type.getAsString() == "size_t") { |
| + return true; |
| + } |
| + QualType desugaredType = type.getSingleStepDesugaredType(context); |
|
dcheng
2016/02/05 19:42:27
Nit: desugared_type
|
| + if (desugaredType == type) { |
| + return false; |
| + } |
| + type = desugaredType.getLocalUnqualifiedType(); |
| + } |
| +} |
| + |
| +class SizeTExprFinder: RecursiveASTVisitor<SizeTExprFinder> { |
| +public: |
| + static Expr* Find(ASTContext& context, Expr* expr) { |
| + SizeTExprFinder finder(context); |
| + finder.TraverseStmt(expr); |
| + return finder.found_; |
| + } |
| + |
| +private: |
| + typedef RecursiveASTVisitor<SizeTExprFinder> Base; |
| + friend Base; |
| + |
| + SizeTExprFinder(ASTContext& context): context_(context), found_(nullptr) {} |
|
dcheng
2016/02/05 19:42:27
explicit
|
| + |
| + bool shouldVisitTemplateInstantiations() const { return true; } |
| + |
| + bool TraverseStmt(Stmt* stmt) { |
| + if (found_) { |
| + return false; |
| + } |
| + Expr* expr = dyn_cast_or_null<Expr>(stmt); |
| + if (!expr) { |
| + return false; |
| + } |
| + QualType type = expr->getType(); |
| + if (!context_.hasSameUnqualifiedType(type, context_.getSizeType())) { |
| + return false; |
| + } |
| + if (IsSizeT(context_, type)) { |
| + found_ = expr; |
| + return false; |
| + } |
| + return Base::TraverseStmt(expr); |
| + } |
| + |
| + ASTContext& context_; |
| + Expr* found_; |
| +}; |
| + |
| +class Visitor: public RecursiveASTVisitor<Visitor> { |
| + typedef RecursiveASTVisitor<Visitor> Base; |
| +public: |
| + Visitor(CompilerInstance& compiler, ASTContext& context) |
| + : compiler_(compiler), context_(context) { |
| + auto& diagnostics = compiler_.getDiagnostics(); |
| + error_write_param_size_t_ = diagnostics.getCustomDiagID( |
| + DiagnosticsEngine::Error, kWriteParamSizeT); |
| + error_write_param_template_ = diagnostics.getCustomDiagID( |
| + DiagnosticsEngine::Error, kWriteParamTemplate); |
| + note_instantiation_ = diagnostics.getCustomDiagID( |
| + DiagnosticsEngine::Note, kNoteInstantiation); |
| + error_tuple_size_t_ = diagnostics.getCustomDiagID( |
| + DiagnosticsEngine::Error, kTupleSizeT); |
| + } |
| + |
| + bool shouldVisitTemplateInstantiations() const { return true; } |
| + |
| + bool VisitTemplateSpecializationType(TemplateSpecializationType* spec) { |
| + TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl(); |
| + if (decl && decl->getQualifiedNameAsString() == "IPC::ParamTuple") { |
| + for (unsigned i = 0; i != spec->getNumArgs(); ++i) { |
| + QualType arg_type = spec->getArg(i).getAsType(); |
| + if (IsSizeT(context_, arg_type)) { |
| + compiler_.getDiagnostics().Report( |
| + getParentDecl()->getLocStart(), error_tuple_size_t_); |
| + break; |
| + } |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + bool VisitCallExpr(CallExpr* call) { |
| + const FunctionDecl* callee = call->getDirectCallee(); |
| + if (!callee || callee->getQualifiedNameAsString() != "IPC::WriteParam") { |
| + return true; |
| + } |
| + |
| + // TODO: what about WriteParam<size_t>((__SIZE_TYPE__)0)? |
| + // It will be caught by missing unsigned long ParamTraits |
| + // specialization, but we can also catch it here. |
| + |
| + // Don't bother if we're not called with __SIZE_TYPE__ |
| + bool called_with_size_type = false; |
| + for (const Expr* argument: call->arguments()) { |
| + if (context_.hasSameUnqualifiedType( |
| + argument->getType(), context_.getSizeType())) { |
| + called_with_size_type = true; |
| + break; |
| + } |
| + } |
| + if (!called_with_size_type) { |
| + return true; |
| + } |
| + |
| + // TODO: what about lambdas? Do we need to search more? |
| + if (auto parent = dyn_cast_or_null<FunctionDecl>(getParentDecl())) { |
| + if (CheckWriteParamInstantiation(call, parent)) { |
| + return true; |
| + } |
| + } |
| + |
| + for (Expr* argument: call->arguments()) { |
| + Expr* sizet_expr = SizeTExprFinder::Find(context_, argument); |
| + if (sizet_expr) { |
| + compiler_.getDiagnostics().Report( |
| + sizet_expr->getExprLoc(), error_write_param_size_t_); |
| + } |
| + } |
| + |
| + return true; |
| + } |
| + |
| + bool TraverseDecl(Decl* decl) { |
| + parent_decls_.push(decl); |
| + bool result = Base::TraverseDecl(decl); |
| + parent_decls_.pop(); |
| + return result; |
| + } |
| + |
| +private: |
| + Decl* getParentDecl() const { |
|
dcheng
2016/02/05 19:42:27
GetParentDecl()?
|
| + return parent_decls_.empty() ? nullptr : parent_decls_.top(); |
| + } |
| + |
| + bool CheckWriteParamInstantiation(CallExpr* call, FunctionDecl* parent) { |
| + // Only check implicit instantiations |
| + if (parent->getTemplatedKind() == FunctionDecl::TK_NonTemplate || |
| + parent->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) { |
| + return false; |
| + } |
| + |
| + // Check that we are inside IPC::ParamTraits<IPC::ParamTuple<..>> |
| + bool param_tuple_specialization = false; |
| + if (auto cxx_method = dyn_cast<CXXMethodDecl>(parent)) { |
| + auto spec = dyn_cast<ClassTemplateSpecializationDecl>( |
| + cxx_method->getParent()); |
| + if (spec && spec->getQualifiedNameAsString() == "IPC::ParamTraits") { |
| + const TemplateArgument& arg = spec->getTemplateArgs().get(0); |
| + auto arg_type = dyn_cast<TagType>(arg.getAsType().getTypePtr()); |
| + param_tuple_specialization = |
| + arg_type && |
| + arg_type->getDecl()->getQualifiedNameAsString() == |
| + "IPC::ParamTuple"; |
| + } |
| + } |
| + if (param_tuple_specialization) { |
| + return false; |
| + } |
| + |
| + compiler_.getDiagnostics().Report( |
| + call->getExprLoc(), error_write_param_template_); |
| + |
| + // TODO: use TemplateSpecializationType::PrintTemplateArgumentList to |
| + // include function's template arguments |
| + compiler_.getDiagnostics().Report(call->getExprLoc(), note_instantiation_) |
| + << parent->getQualifiedNameAsString(); |
| + return true; |
| + } |
| + |
| + CompilerInstance& compiler_; |
| + ASTContext& context_; |
| + |
| + unsigned error_write_param_size_t_; |
| + unsigned error_write_param_template_; |
| + unsigned error_tuple_size_t_; |
| + unsigned note_instantiation_; |
| + |
| + std::stack<Decl*> parent_decls_; |
| +}; |
| + |
| +class Consumer: public ASTConsumer { |
| +public: |
| + Consumer(CompilerInstance& compiler): compiler_(compiler) {} |
| + |
| + void HandleTranslationUnit(ASTContext& context) override { |
| + Visitor(compiler_, context).TraverseDecl(context.getTranslationUnitDecl()); |
| + } |
| + |
| +private: |
| + CompilerInstance& compiler_; |
| +}; |
| + |
| +class Plugin: public PluginASTAction { |
| +protected: |
| + std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance& compiler, |
| + llvm::StringRef) override { |
| + return llvm::make_unique<Consumer>(compiler); |
| + } |
| + |
| + bool ParseArgs(const CompilerInstance&, |
| + const std::vector<std::string>&) override { |
| + return true; |
| + } |
| +}; |
| + |
| +} // namespace |
| + |
| +static FrontendPluginRegistry::Add<Plugin> X( |
| + "check-ipc", |
| + "Checks for size_t in IPC messages"); |