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..b123b0130ed1c5dd3d5690476a149a84187271ab |
--- /dev/null |
+++ b/tools/clang/plugins/CheckIPCVisitor.cpp |
@@ -0,0 +1,288 @@ |
+// 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" |
+ |
+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 kWriteParamBadSignature[] = |
+ "[chromium-ipc] IPC::WriteParam() is expected to have two arguments."; |
+ |
+const char kNoteSeeHere[] = |
+ "see here"; |
+ |
+} // namespace |
+ |
+CheckIPCVisitor::CheckIPCVisitor(CompilerInstance& compiler) |
+ : compiler_(compiler), context_(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_bad_signature_ = diagnostics.getCustomDiagID( |
+ DiagnosticsEngine::Error, kWriteParamBadSignature); |
+ note_see_here_ = diagnostics.getCustomDiagID( |
+ DiagnosticsEngine::Note, kNoteSeeHere); |
+ |
+ blacklisted_typedefs_ = llvm::StringSet<>({ |
+ "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" |
+ }); |
+} |
+ |
+void CheckIPCVisitor::BeginDecl(Decl* decl) { |
+ decl_stack_.push_back(decl); |
+} |
+ |
+void CheckIPCVisitor::EndDecl() { |
+ decl_stack_.pop_back(); |
+} |
+ |
+void CheckIPCVisitor::VisitTemplateSpecializationType( |
+ TemplateSpecializationType* spec) { |
+ ValidateCheckedTuple(spec); |
+} |
+ |
+void CheckIPCVisitor::VisitCallExpr(CallExpr* call_expr) { |
+ ValidateWriteParam(call_expr); |
+} |
+ |
+bool CheckIPCVisitor::ValidateWriteParam(const CallExpr* call_expr) { |
+ const FunctionDecl* callee_decl = call_expr->getDirectCallee(); |
+ if (!callee_decl || |
+ callee_decl->getQualifiedNameAsString() != "IPC::WriteParam") { |
+ return true; |
+ } |
+ |
+ return ValidateWriteParamSignature(call_expr) && |
+ ValidateWriteParamArgument(call_expr->getArg(1)); |
+} |
+ |
+// Checks that IPC::WriteParam() has expected signature. |
+bool CheckIPCVisitor::ValidateWriteParamSignature( |
+ const CallExpr* call_expr) { |
+ if (call_expr->getNumArgs() != 2) { |
+ compiler_.getDiagnostics().Report( |
+ call_expr->getExprLoc(), error_write_param_bad_signature_); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+// Checks that IPC::WriteParam() argument type is allowed. |
+// See CheckType() for specifics. |
+bool CheckIPCVisitor::ValidateWriteParamArgument(const Expr* arg_expr) { |
+ if (auto* parent_fn_decl = GetParentDecl<FunctionDecl>()) { |
+ auto template_kind = parent_fn_decl->getTemplatedKind(); |
+ if (template_kind != FunctionDecl::TK_NonTemplate && |
+ template_kind != FunctionDecl::TK_FunctionTemplate) { |
+ // Skip all specializations - we don't check WriteParam() on dependent |
+ // types (typedef info gets lost), and we checked all non-dependent uses |
+ // earlier (when we checked the template itself). |
+ return true; |
+ } |
+ } |
+ |
+ QualType arg_type; |
+ |
+ arg_expr = arg_expr->IgnoreImplicit(); |
+ if (auto* cast_expr = dyn_cast<ExplicitCastExpr>(arg_expr)) { |
+ arg_type = cast_expr->getTypeAsWritten(); |
+ } else { |
+ arg_type = arg_expr->getType(); |
+ } |
+ |
+ CheckDetails details; |
+ if (CheckType(arg_type, &details)) { |
+ return true; |
+ } |
+ |
+ ReportCheckError(details, |
+ arg_expr->getExprLoc(), |
+ error_write_param_bad_type_); |
+ |
+ return false; |
+} |
+ |
+// Checks that IPC::CheckedTuple<> is specialized with allowed types. |
+// See CheckType() above for specifics. |
+bool CheckIPCVisitor::ValidateCheckedTuple( |
+ const TemplateSpecializationType* spec) { |
+ TemplateDecl* decl = spec->getTemplateName().getAsTemplateDecl(); |
+ if (!decl || decl->getQualifiedNameAsString() != "IPC::CheckedTuple") { |
+ return true; |
+ } |
+ |
+ bool valid = true; |
+ for (unsigned i = 0; i != spec->getNumArgs(); ++i) { |
+ const TemplateArgument& arg = spec->getArg(i); |
+ CheckDetails details; |
+ if (CheckTemplateArgument(arg, &details)) { |
+ continue; |
+ } |
+ |
+ valid = false; |
+ |
+ auto* parent_decl = GetParentDecl<Decl>(); |
+ ReportCheckError( |
+ details, |
+ parent_decl ? parent_decl->getLocStart() : SourceLocation(), |
+ error_tuple_bad_type_); |
+ } |
+ |
+ return valid; |
+} |
+ |
+template <typename T> |
+const T* CheckIPCVisitor::GetParentDecl() const { |
+ for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) { |
+ if (auto* parent = dyn_cast_or_null<T>(*i)) { |
+ return parent; |
+ } |
+ } |
+ return nullptr; |
+} |
+ |
+ |
+bool CheckIPCVisitor::IsBlacklistedType(QualType type) const { |
+ return context_->hasSameUnqualifiedType(type, context_->LongTy) || |
+ context_->hasSameUnqualifiedType(type, context_->UnsignedLongTy); |
+} |
+ |
+bool CheckIPCVisitor::IsBlacklistedTypedef(const TypedefNameDecl* tdef) const { |
+ return blacklisted_typedefs_.find(tdef->getName()) != |
+ blacklisted_typedefs_.end(); |
+} |
+ |
+// Checks that integer type is allowed (not blacklisted). |
+bool CheckIPCVisitor::CheckIntegerType(QualType type, |
+ CheckDetails* details) const { |
+ bool seen_typedef = false; |
+ while (true) { |
+ details->exit_type = type; |
+ |
+ if (auto* tdef = dyn_cast<TypedefType>(type)) { |
+ if (IsBlacklistedTypedef(tdef->getDecl())) { |
+ return false; |
+ } |
+ 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(type); |
+} |
+ |
+// Checks that |type| is allowed (not blacklisted), recursively visiting |
+// template specializations. |
+bool CheckIPCVisitor::CheckType(QualType type, CheckDetails* details) const { |
+ if (type->isReferenceType()) { |
+ type = type->getPointeeType(); |
+ } |
+ type = type.getLocalUnqualifiedType(); |
+ |
+ if (details->entry_type.isNull()) { |
+ details->entry_type = type; |
+ } |
+ |
+ if (type->isIntegerType()) { |
+ return CheckIntegerType(type, details); |
+ } |
+ |
+ while (true) { |
+ if (auto* spec = dyn_cast<TemplateSpecializationType>(type)) { |
+ for (const TemplateArgument& arg: *spec) { |
+ if (!CheckTemplateArgument(arg, details)) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ if (auto* record = dyn_cast<RecordType>(type)) { |
+ if (auto* spec = dyn_cast<ClassTemplateSpecializationDecl>( |
+ record->getDecl())) { |
+ const TemplateArgumentList& args = spec->getTemplateArgs(); |
+ for (unsigned i = 0; i != args.size(); ++i) { |
+ if (!CheckTemplateArgument(args[i], details)) { |
+ return false; |
+ } |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ if (auto* tdef = dyn_cast<TypedefType>(type)) { |
+ details->typedefs.push_back(tdef); |
+ } |
+ |
+ QualType desugared_type = |
+ type->getLocallyUnqualifiedSingleStepDesugaredType(); |
+ if (desugared_type == type) { |
+ break; |
+ } |
+ |
+ type = desugared_type; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool CheckIPCVisitor::CheckTemplateArgument(const TemplateArgument& arg, |
+ CheckDetails* details) const { |
+ return arg.getKind() != TemplateArgument::Type || |
+ CheckType(arg.getAsType(), details); |
+} |
+ |
+void CheckIPCVisitor::ReportCheckError(const CheckDetails& details, |
+ SourceLocation loc, |
+ unsigned error) { |
+ DiagnosticsEngine& diagnostics = compiler_.getDiagnostics(); |
+ |
+ 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(), note_see_here_); |
+ } |
+} |
+ |
+} // namespace chrome_checker |