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..8024bfd2f2423d098a2a1c1bb74f2a89e94275e4 |
--- /dev/null |
+++ b/tools/clang/plugins/CheckIPCVisitor.cpp |
@@ -0,0 +1,384 @@ |
+// 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 "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[] = |
+ ""; |
dcheng
2016/02/23 19:46:46
Add some content to this string?
Dmitry Skiba
2016/02/24 14:39:04
Done.
|
+ |
+struct CheckDetails { |
+ QualType entry_type; |
+ QualType exit_type; |
+ std::vector<const TypedefType*> typedefs; |
+}; |
+ |
+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({ |
dcheng
2016/02/23 19:46:46
No static initializers. Just make this an instance
Dmitry Skiba
2016/02/24 14:39:04
TBD
|
+ "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 UnqualifyType(QualType type) { |
+ if (type->isReferenceType()) { |
+ type = type->getPointeeType(); |
+ } |
+ type = type.getLocalUnqualifiedType(); |
+ return type; |
+} |
+ |
+// Checks that integer type is not blacklisted. |
+bool CheckIntegerType(ASTContext& context, |
+ QualType type, |
+ CheckDetails* details = nullptr) { |
+ bool seen_typedef = false; |
+ while (true) { |
+ if (details) { |
+ details->exit_type = type; |
+ } |
+ |
+ if (auto tdef = dyn_cast<TypedefType>(type)) { |
dcheng
2016/02/23 19:46:46
Please qualify auto with * or & as appropriate (it
Dmitry Skiba
2016/02/24 14:39:04
Done.
|
+ if (IsBlacklistedTypedef(tdef->getDecl())) { |
+ return false; |
+ } |
+ if (details) { |
+ details->typedefs.push_back(tdef); |
+ } |
+ seen_typedef = true; |
+ } |
+ |
+ QualType desugared_type = |
+ type->getLocallyUnqualifiedSingleStepDesugaredType(); |
dcheng
2016/02/23 19:46:46
The documentation for this says to prefer getSingl
Dmitry Skiba
2016/02/24 14:39:04
Previously I was calling that function / stripping
|
+ if (desugared_type == type) { |
+ break; |
+ } |
+ |
+ type = desugared_type; |
+ } |
+ |
+ return seen_typedef || !IsBlacklistedType(context, type); |
+} |
+ |
+bool CheckTemplateArgument(ASTContext& context, |
+ const TemplateArgument& arg, |
+ CheckDetails* details = nullptr); |
+ |
+bool CheckType(ASTContext& context, |
+ QualType type, |
+ CheckDetails* details = nullptr) { |
+ type = UnqualifyType(type); |
+ if (details && details->entry_type == QualType()) { |
+ details->entry_type = type; |
+ } |
+ |
+ if (type->isIntegerType()) { |
+ return CheckIntegerType(context, type, details); |
+ } |
+ |
+ if (auto tdef = type->getAs<TypedefType>()) { |
+ if (details) { |
+ details->typedefs.push_back(tdef); |
+ } |
+ return CheckType(context, tdef->desugar(), details); |
+ } |
+ |
+ if (auto spec = type->getAs<TemplateSpecializationType>()) { |
+ for (unsigned i = 0; i != spec->getNumArgs(); ++i) { |
dcheng
2016/02/23 19:46:46
I think you can just do for (TemplateArgument* arg
Dmitry Skiba
2016/02/24 14:39:04
Done.
|
+ if (!CheckTemplateArgument(context, spec->getArg(i), details)) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ if (auto record = type->getAs<RecordType>()) { |
+ auto spec = dyn_cast<ClassTemplateSpecializationDecl>(record->getDecl()); |
dcheng
2016/02/23 19:46:46
Fold into 140 for consistency.
Dmitry Skiba
2016/02/24 14:39:04
Done.
|
+ if (spec) { |
+ const TemplateArgumentList& args = spec->getTemplateArgs(); |
+ for (unsigned i = 0; i != args.size(); ++i) { |
+ if (!CheckTemplateArgument(context, args[i], details)) { |
+ return false; |
+ } |
+ } |
+ } |
+ return true; |
+ } |
+ |
+ return true; |
+} |
+ |
+bool CheckTemplateArgument(ASTContext& context, |
+ const TemplateArgument& arg, |
+ CheckDetails* details) { |
+ return arg.getKind() != TemplateArgument::Type || |
+ CheckType(context, arg.getAsType(), details); |
+} |
+ |
+void ReportCheckError(clang::DiagnosticsEngine& diagnostics, |
+ const CheckDetails& details, |
+ SourceLocation loc, |
+ unsigned error, unsigned typedef_note) { |
dcheng
2016/02/23 19:46:46
What's the point of typedef_note: aren't we always
Dmitry Skiba
2016/02/24 14:39:04
Yes, but it's not known here, since ReportCheckErr
|
+ 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) { |
+ 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::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 { |
+ 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 { |
+ auto parent_fn_decl = GetParentDecl<FunctionDecl>(); |
+ if (parent_fn_decl && |
+ parent_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); |
dcheng
2016/02/23 19:46:46
Why not just pass details in to begin with?
Dmitry Skiba
2016/02/24 14:39:04
It's just an optimization. I expect CheckType() to
dcheng
2016/02/24 22:57:42
I'd just use llvm::SmallVector<> and always pass i
|
+ |
+ 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 { |
+ auto parent_fn_decl = GetParentDecl<FunctionDecl>(); |
+ if (!parent_fn_decl || |
+ parent_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) { |
+ const TemplateArgument& arg = spec->getArg(i); |
+ if (CheckTemplateArgument(*context_, arg)) { |
+ continue; |
+ } |
+ |
+ valid = false; |
+ |
+ CheckDetails details; |
+ CheckTemplateArgument(*context_, arg, &details); |
+ |
+ auto parent_decl = GetParentDecl<Decl>(); |
+ SourceLocation error_loc = parent_decl ? |
+ parent_decl->getLocStart() : SourceLocation(); |
+ ReportCheckError(compiler_.getDiagnostics(), |
+ details, |
+ error_loc, |
+ error_tuple_bad_type_, note_see_here_); |
+ } |
+ |
+ return valid; |
+} |
+ |
+bool CheckIPCVisitor::IsInsideParamTraits() const { |
+ auto parent_spec_decl = GetParentDecl<ClassTemplateSpecializationDecl>(); |
+ return parent_spec_decl && |
+ parent_spec_decl->getQualifiedNameAsString() == "IPC::ParamTraits"; |
+} |
+ |
+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; |
+} |
+ |
+} // namespace chrome_checker |