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 |