Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1057)

Unified Diff: tools/clang/plugins/CheckIPCAction.cpp

Issue 1665363002: Clang plugin to check that unstable types are not used in IPC. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/clang/plugins/CMakeLists.txt ('k') | tools/clang/plugins/tests/ipc.cpp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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");
« no previous file with comments | « tools/clang/plugins/CMakeLists.txt ('k') | tools/clang/plugins/tests/ipc.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698