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"); |