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

Unified Diff: tools/clang/plugins/CheckIPCVisitor.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: Blacklist types instead 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
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

Powered by Google App Engine
This is Rietveld 408576698