Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "clang/Frontend/FrontendPluginRegistry.h" | |
| 6 #include "clang/AST/AST.h" | |
| 7 #include "clang/AST/ASTConsumer.h" | |
| 8 #include "clang/AST/RecursiveASTVisitor.h" | |
| 9 #include "clang/Frontend/CompilerInstance.h" | |
| 10 #include "llvm/Support/raw_ostream.h" | |
| 11 | |
| 12 #include <stack> | |
| 13 | |
| 14 using namespace clang; | |
| 15 | |
| 16 namespace { | |
| 17 | |
| 18 const char kWriteParamSizeT[] = | |
| 19 "[chromium-ipc] IPC::WriteParam() is called on size_t."; | |
| 20 | |
| 21 const char kWriteParamTemplate[] = | |
| 22 "[chromium-ipc] IPC::WriteParam() is called in a template instantiation " | |
| 23 "on a type equivalent to size_t."; | |
| 24 | |
| 25 const char kNoteInstantiation[] = | |
| 26 "instantiation: %0"; | |
| 27 | |
| 28 const char kTupleSizeT[] = | |
| 29 "[chromium-ipc] IPC::ParamTuple specialized with size_t."; | |
| 30 | |
| 31 | |
| 32 bool IsSizeT(ASTContext& context, QualType type) { | |
| 33 type = type.getLocalUnqualifiedType(); | |
| 34 if (!context.hasSameUnqualifiedType(type, context.getSizeType())) { | |
| 35 return false; | |
| 36 } | |
| 37 while (true) { | |
| 38 if (type.getAsString() == "size_t") { | |
| 39 return true; | |
| 40 } | |
| 41 QualType desugaredType = type.getSingleStepDesugaredType(context); | |
|
dcheng
2016/02/05 19:42:27
Nit: desugared_type
| |
| 42 if (desugaredType == type) { | |
| 43 return false; | |
| 44 } | |
| 45 type = desugaredType.getLocalUnqualifiedType(); | |
| 46 } | |
| 47 } | |
| 48 | |
| 49 class SizeTExprFinder: RecursiveASTVisitor<SizeTExprFinder> { | |
| 50 public: | |
| 51 static Expr* Find(ASTContext& context, Expr* expr) { | |
| 52 SizeTExprFinder finder(context); | |
| 53 finder.TraverseStmt(expr); | |
| 54 return finder.found_; | |
| 55 } | |
| 56 | |
| 57 private: | |
| 58 typedef RecursiveASTVisitor<SizeTExprFinder> Base; | |
| 59 friend Base; | |
| 60 | |
| 61 SizeTExprFinder(ASTContext& context): context_(context), found_(nullptr) {} | |
|
dcheng
2016/02/05 19:42:27
explicit
| |
| 62 | |
| 63 bool shouldVisitTemplateInstantiations() const { return true; } | |
| 64 | |
| 65 bool TraverseStmt(Stmt* stmt) { | |
| 66 if (found_) { | |
| 67 return false; | |
| 68 } | |
| 69 Expr* expr = dyn_cast_or_null<Expr>(stmt); | |
| 70 if (!expr) { | |
| 71 return false; | |
| 72 } | |
| 73 QualType type = expr->getType(); | |
| 74 if (!context_.hasSameUnqualifiedType(type, context_.getSizeType())) { | |
| 75 return false; | |
| 76 } | |
| 77 if (IsSizeT(context_, type)) { | |
| 78 found_ = expr; | |
| 79 return false; | |
| 80 } | |
| 81 return Base::TraverseStmt(expr); | |
| 82 } | |
| 83 | |
| 84 ASTContext& context_; | |
| 85 Expr* found_; | |
| 86 }; | |
| 87 | |
| 88 class Visitor: public RecursiveASTVisitor<Visitor> { | |
| 89 typedef RecursiveASTVisitor<Visitor> Base; | |
| 90 public: | |
| 91 Visitor(CompilerInstance& compiler, ASTContext& context) | |
| 92 : compiler_(compiler), context_(context) { | |
| 93 auto& diagnostics = compiler_.getDiagnostics(); | |
| 94 error_write_param_size_t_ = diagnostics.getCustomDiagID( | |
| 95 DiagnosticsEngine::Error, kWriteParamSizeT); | |
| 96 error_write_param_template_ = diagnostics.getCustomDiagID( | |
| 97 DiagnosticsEngine::Error, kWriteParamTemplate); | |
| 98 note_instantiation_ = diagnostics.getCustomDiagID( | |
| 99 DiagnosticsEngine::Note, kNoteInstantiation); | |
| 100 error_tuple_size_t_ = diagnostics.getCustomDiagID( | |
| 101 DiagnosticsEngine::Error, kTupleSizeT); | |
| 102 } | |
| 103 | |
| 104 bool shouldVisitTemplateInstantiations() const { return true; } | |
| 105 | |
| 106 bool VisitTemplateSpecializationType(TemplateSpecializationType* spec) { | |
| 107 TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl(); | |
| 108 if (decl && decl->getQualifiedNameAsString() == "IPC::ParamTuple") { | |
| 109 for (unsigned i = 0; i != spec->getNumArgs(); ++i) { | |
| 110 QualType arg_type = spec->getArg(i).getAsType(); | |
| 111 if (IsSizeT(context_, arg_type)) { | |
| 112 compiler_.getDiagnostics().Report( | |
| 113 getParentDecl()->getLocStart(), error_tuple_size_t_); | |
| 114 break; | |
| 115 } | |
| 116 } | |
| 117 } | |
| 118 return true; | |
| 119 } | |
| 120 | |
| 121 bool VisitCallExpr(CallExpr* call) { | |
| 122 const FunctionDecl* callee = call->getDirectCallee(); | |
| 123 if (!callee || callee->getQualifiedNameAsString() != "IPC::WriteParam") { | |
| 124 return true; | |
| 125 } | |
| 126 | |
| 127 // TODO: what about WriteParam<size_t>((__SIZE_TYPE__)0)? | |
| 128 // It will be caught by missing unsigned long ParamTraits | |
| 129 // specialization, but we can also catch it here. | |
| 130 | |
| 131 // Don't bother if we're not called with __SIZE_TYPE__ | |
| 132 bool called_with_size_type = false; | |
| 133 for (const Expr* argument: call->arguments()) { | |
| 134 if (context_.hasSameUnqualifiedType( | |
| 135 argument->getType(), context_.getSizeType())) { | |
| 136 called_with_size_type = true; | |
| 137 break; | |
| 138 } | |
| 139 } | |
| 140 if (!called_with_size_type) { | |
| 141 return true; | |
| 142 } | |
| 143 | |
| 144 // TODO: what about lambdas? Do we need to search more? | |
| 145 if (auto parent = dyn_cast_or_null<FunctionDecl>(getParentDecl())) { | |
| 146 if (CheckWriteParamInstantiation(call, parent)) { | |
| 147 return true; | |
| 148 } | |
| 149 } | |
| 150 | |
| 151 for (Expr* argument: call->arguments()) { | |
| 152 Expr* sizet_expr = SizeTExprFinder::Find(context_, argument); | |
| 153 if (sizet_expr) { | |
| 154 compiler_.getDiagnostics().Report( | |
| 155 sizet_expr->getExprLoc(), error_write_param_size_t_); | |
| 156 } | |
| 157 } | |
| 158 | |
| 159 return true; | |
| 160 } | |
| 161 | |
| 162 bool TraverseDecl(Decl* decl) { | |
| 163 parent_decls_.push(decl); | |
| 164 bool result = Base::TraverseDecl(decl); | |
| 165 parent_decls_.pop(); | |
| 166 return result; | |
| 167 } | |
| 168 | |
| 169 private: | |
| 170 Decl* getParentDecl() const { | |
|
dcheng
2016/02/05 19:42:27
GetParentDecl()?
| |
| 171 return parent_decls_.empty() ? nullptr : parent_decls_.top(); | |
| 172 } | |
| 173 | |
| 174 bool CheckWriteParamInstantiation(CallExpr* call, FunctionDecl* parent) { | |
| 175 // Only check implicit instantiations | |
| 176 if (parent->getTemplatedKind() == FunctionDecl::TK_NonTemplate || | |
| 177 parent->getTemplateSpecializationKind() != TSK_ImplicitInstantiation) { | |
| 178 return false; | |
| 179 } | |
| 180 | |
| 181 // Check that we are inside IPC::ParamTraits<IPC::ParamTuple<..>> | |
| 182 bool param_tuple_specialization = false; | |
| 183 if (auto cxx_method = dyn_cast<CXXMethodDecl>(parent)) { | |
| 184 auto spec = dyn_cast<ClassTemplateSpecializationDecl>( | |
| 185 cxx_method->getParent()); | |
| 186 if (spec && spec->getQualifiedNameAsString() == "IPC::ParamTraits") { | |
| 187 const TemplateArgument& arg = spec->getTemplateArgs().get(0); | |
| 188 auto arg_type = dyn_cast<TagType>(arg.getAsType().getTypePtr()); | |
| 189 param_tuple_specialization = | |
| 190 arg_type && | |
| 191 arg_type->getDecl()->getQualifiedNameAsString() == | |
| 192 "IPC::ParamTuple"; | |
| 193 } | |
| 194 } | |
| 195 if (param_tuple_specialization) { | |
| 196 return false; | |
| 197 } | |
| 198 | |
| 199 compiler_.getDiagnostics().Report( | |
| 200 call->getExprLoc(), error_write_param_template_); | |
| 201 | |
| 202 // TODO: use TemplateSpecializationType::PrintTemplateArgumentList to | |
| 203 // include function's template arguments | |
| 204 compiler_.getDiagnostics().Report(call->getExprLoc(), note_instantiation_) | |
| 205 << parent->getQualifiedNameAsString(); | |
| 206 return true; | |
| 207 } | |
| 208 | |
| 209 CompilerInstance& compiler_; | |
| 210 ASTContext& context_; | |
| 211 | |
| 212 unsigned error_write_param_size_t_; | |
| 213 unsigned error_write_param_template_; | |
| 214 unsigned error_tuple_size_t_; | |
| 215 unsigned note_instantiation_; | |
| 216 | |
| 217 std::stack<Decl*> parent_decls_; | |
| 218 }; | |
| 219 | |
| 220 class Consumer: public ASTConsumer { | |
| 221 public: | |
| 222 Consumer(CompilerInstance& compiler): compiler_(compiler) {} | |
| 223 | |
| 224 void HandleTranslationUnit(ASTContext& context) override { | |
| 225 Visitor(compiler_, context).TraverseDecl(context.getTranslationUnitDecl()); | |
| 226 } | |
| 227 | |
| 228 private: | |
| 229 CompilerInstance& compiler_; | |
| 230 }; | |
| 231 | |
| 232 class Plugin: public PluginASTAction { | |
| 233 protected: | |
| 234 std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance& compiler, | |
| 235 llvm::StringRef) override { | |
| 236 return llvm::make_unique<Consumer>(compiler); | |
| 237 } | |
| 238 | |
| 239 bool ParseArgs(const CompilerInstance&, | |
| 240 const std::vector<std::string>&) override { | |
| 241 return true; | |
| 242 } | |
| 243 }; | |
| 244 | |
| 245 } // namespace | |
| 246 | |
| 247 static FrontendPluginRegistry::Add<Plugin> X( | |
| 248 "check-ipc", | |
| 249 "Checks for size_t in IPC messages"); | |
| OLD | NEW |