diff --git a/ipc/ipc_message_macros.h b/ipc/ipc_message_macros.h index ea28d0c199bcae..38f428e90aaa18 100644 --- a/ipc/ipc_message_macros.h +++ b/ipc/ipc_message_macros.h @@ -249,7 +249,7 @@ #define IPC_SYNC_MESSAGE_ROUTED(msg_class, in, out) \ IPC_MESSAGE_DECL(msg_class, ROUTED, IPC_TUPLE in, IPC_TUPLE out) -#define IPC_TUPLE(...) std::tuple<__VA_ARGS__> +#define IPC_TUPLE(...) IPC::CheckedTuple<__VA_ARGS__>::Tuple #define IPC_MESSAGE_DECL(msg_name, kind, in_tuple, out_tuple) \ struct IPC_MESSAGE_EXPORT msg_name##_Meta { \ diff --git a/ipc/ipc_message_utils.h b/ipc/ipc_message_utils.h index efd669c193498c..cf4fa8fb14c5a3 100644 --- a/ipc/ipc_message_utils.h +++ b/ipc/ipc_message_utils.h @@ -77,12 +77,26 @@ struct IPC_EXPORT LogData { struct NoParams { }; +// Specializations are checked by 'IPC checker' part of find-bad-constructs +// Clang plugin (see WriteParam() below for the details). +template +struct CheckedTuple { + typedef std::tuple Tuple; +}; + template static inline void GetParamSize(base::PickleSizer* sizer, const P& p) { typedef typename SimilarTypeTraits

::Type Type; ParamTraits::GetSize(sizer, static_cast(p)); } +// This function is checked by 'IPC checker' part of find-bad-constructs +// Clang plugin to make it's not called on the following types: +// 1. long / unsigned long (but not typedefs to) +// 2. 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 (including typedefs to) +// 3. Any template referencing types above (e.g. std::vector) template static inline void WriteParam(base::Pickle* m, const P& p) { typedef typename SimilarTypeTraits

::Type Type; diff --git a/tools/clang/plugins/CMakeLists.txt b/tools/clang/plugins/CMakeLists.txt index 04de853e16f217..6be8b2eb699d55 100644 --- a/tools/clang/plugins/CMakeLists.txt +++ b/tools/clang/plugins/CMakeLists.txt @@ -1,7 +1,8 @@ set(plugin_sources ChromeClassTester.cpp FindBadConstructsAction.cpp - FindBadConstructsConsumer.cpp) + FindBadConstructsConsumer.cpp + CheckIPCVisitor.cpp) if(WIN32) # Clang doesn't support loadable modules on Windows. Unfortunately, building diff --git a/tools/clang/plugins/CheckIPCVisitor.cpp b/tools/clang/plugins/CheckIPCVisitor.cpp new file mode 100644 index 00000000000000..b123b0130ed1c5 --- /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()) { + 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(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(); + ReportCheckError( + details, + parent_decl ? parent_decl->getLocStart() : SourceLocation(), + error_tuple_bad_type_); + } + + return valid; +} + +template +const T* CheckIPCVisitor::GetParentDecl() const { + for (auto i = decl_stack_.rbegin(); i != decl_stack_.rend(); ++i) { + if (auto* parent = dyn_cast_or_null(*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(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(type)) { + for (const TemplateArgument& arg: *spec) { + if (!CheckTemplateArgument(arg, details)) { + return false; + } + } + return true; + } + + if (auto* record = dyn_cast(type)) { + if (auto* spec = dyn_cast( + 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(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 diff --git a/tools/clang/plugins/CheckIPCVisitor.h b/tools/clang/plugins/CheckIPCVisitor.h new file mode 100644 index 00000000000000..2d88e6b931a934 --- /dev/null +++ b/tools/clang/plugins/CheckIPCVisitor.h @@ -0,0 +1,99 @@ +// 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. + +// This check ensures that 32/64-bit unstable types are not used in IPC. +// +// A type (or typedef) is unstable if it changes size between 32/ 64-bit +// platforms. However, it's impossible to accurately identify unstable +// typedefs, because their definitions rely on the preprocessor. For +// example uintptr_t is either unsigned int or unsigned long. +// +// So we're not trying to be accurate, and just blacklisting some types +// that are known to be unstable: +// 1. Types: long / unsigned long (but not typedefs to) +// 2. Typedefs: 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 (including typedefs to) +// +// Additionally, templates referencing blacklisted types (e.g. vector) +// are also blacklisted. +// +// Blacklisted types are checked in: +// 1. IPC::WriteParam() calls +// 2. IPC::CheckedTuple<> specializations +// + +#ifndef TOOLS_CLANG_PLUGINS_CHECKIPC_VISITOR_H_ +#define TOOLS_CLANG_PLUGINS_CHECKIPC_VISITOR_H_ + +#include + +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Frontend/CompilerInstance.h" +#include "llvm/ADT/StringSet.h" + +namespace chrome_checker { + +class CheckIPCVisitor { + public: + explicit CheckIPCVisitor(clang::CompilerInstance& compiler); + + void set_context(clang::ASTContext* context) { context_ = context; } + + void BeginDecl(clang::Decl* decl); + void EndDecl(); + void VisitTemplateSpecializationType( + clang::TemplateSpecializationType* spec); + void VisitCallExpr(clang::CallExpr* call_expr); + + private: + // ValidateXXX functions return false if validation failed and diagnostic + // was reported. They return true otherwise (not applicable / validation + // succeeded). + + bool ValidateWriteParam(const clang::CallExpr* call_expr); + bool ValidateWriteParamSignature(const clang::CallExpr* call_expr); + bool ValidateWriteParamArgument(const clang::Expr* arg_expr); + bool ValidateCheckedTuple( + const clang::TemplateSpecializationType* spec); + + template + const T* GetParentDecl() const; + + bool IsBlacklistedType(clang::QualType type) const; + bool IsBlacklistedTypedef(const clang::TypedefNameDecl* tdef) const; + + struct CheckDetails { + clang::QualType entry_type; + clang::QualType exit_type; + llvm::SmallVector typedefs; + }; + + bool CheckType(clang::QualType type, CheckDetails* details) const; + bool CheckIntegerType(clang::QualType type, CheckDetails* details) const; + bool CheckTemplateArgument(const clang::TemplateArgument& arg, + CheckDetails* details) const; + + void ReportCheckError(const CheckDetails& details, + clang::SourceLocation loc, + unsigned error); + + clang::CompilerInstance& compiler_; + clang::ASTContext* context_; + + unsigned error_write_param_bad_type_; + unsigned error_tuple_bad_type_; + unsigned error_write_param_bad_signature_; + unsigned note_see_here_; + + std::vector decl_stack_; + + llvm::StringSet<> blacklisted_typedefs_; +}; + +} // namespace chrome_checker + +#endif // TOOLS_CLANG_PLUGINS_CHECKIPC_VISITOR_H_ diff --git a/tools/clang/plugins/FindBadConstructsAction.cpp b/tools/clang/plugins/FindBadConstructsAction.cpp index ad9fc5582638b9..f857ac2bbe8881 100644 --- a/tools/clang/plugins/FindBadConstructsAction.cpp +++ b/tools/clang/plugins/FindBadConstructsAction.cpp @@ -21,7 +21,7 @@ class PluginConsumer : public ASTConsumer { : visitor_(*instance, options) {} void HandleTranslationUnit(clang::ASTContext& context) override { - visitor_.TraverseDecl(context.getTranslationUnitDecl()); + visitor_.Traverse(context); } private: @@ -63,6 +63,8 @@ bool FindBadConstructsAction::ParseArgs(const CompilerInstance& instance, options_.check_implicit_copy_ctors = true; } else if (args[i] == "no-realpath") { options_.no_realpath = true; + } else if (args[i] == "check-ipc") { + options_.check_ipc = true; } else { parsed = false; llvm::errs() << "Unknown clang plugin argument: " << args[i] << "\n"; diff --git a/tools/clang/plugins/FindBadConstructsConsumer.cpp b/tools/clang/plugins/FindBadConstructsConsumer.cpp index 649a7a5882ce9d..6c33f75d41b374 100644 --- a/tools/clang/plugins/FindBadConstructsConsumer.cpp +++ b/tools/clang/plugins/FindBadConstructsConsumer.cpp @@ -7,6 +7,7 @@ #include "clang/Frontend/CompilerInstance.h" #include "clang/AST/Attr.h" #include "clang/Lex/Lexer.h" +#include "clang/Sema/Sema.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -91,11 +92,32 @@ bool IsPodOrTemplateType(const CXXRecordDecl& record) { record.isDependentType(); } +// Use a local RAV implementation to simply collect all FunctionDecls marked for +// late template parsing. This happens with the flag -fdelayed-template-parsing, +// which is on by default in MSVC-compatible mode. +std::set GetLateParsedFunctionDecls(TranslationUnitDecl* decl) { + struct Visitor : public RecursiveASTVisitor { + bool VisitFunctionDecl(FunctionDecl* function_decl) { + if (function_decl->isLateTemplateParsed()) + late_parsed_decls.insert(function_decl); + return true; + } + + std::set late_parsed_decls; + } v; + v.TraverseDecl(decl); + return v.late_parsed_decls; +} + } // namespace FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, const Options& options) : ChromeClassTester(instance, options) { + if (options.check_ipc) { + ipc_visitor_.reset(new CheckIPCVisitor(instance)); + } + // Messages for virtual method specifiers. diag_method_requires_override_ = diagnostic().getCustomDiagID(getErrorLevel(), kMethodRequiresOverride); @@ -129,6 +151,22 @@ FindBadConstructsConsumer::FindBadConstructsConsumer(CompilerInstance& instance, DiagnosticsEngine::Note, kNoteProtectedNonVirtualDtor); } +void FindBadConstructsConsumer::Traverse(ASTContext& context) { + if (ipc_visitor_) { + ipc_visitor_->set_context(&context); + ParseFunctionTemplates(context.getTranslationUnitDecl()); + } + RecursiveASTVisitor::TraverseDecl(context.getTranslationUnitDecl()); + if (ipc_visitor_) ipc_visitor_->set_context(nullptr); +} + +bool FindBadConstructsConsumer::TraverseDecl(Decl* decl) { + if (ipc_visitor_) ipc_visitor_->BeginDecl(decl); + bool result = RecursiveASTVisitor::TraverseDecl(decl); + if (ipc_visitor_) ipc_visitor_->EndDecl(); + return result; +} + bool FindBadConstructsConsumer::VisitDecl(clang::Decl* decl) { clang::TagDecl* tag_decl = dyn_cast(decl); if (tag_decl && tag_decl->isCompleteDefinition()) @@ -136,6 +174,17 @@ bool FindBadConstructsConsumer::VisitDecl(clang::Decl* decl) { return true; } +bool FindBadConstructsConsumer::VisitTemplateSpecializationType( + TemplateSpecializationType* spec) { + if (ipc_visitor_) ipc_visitor_->VisitTemplateSpecializationType(spec); + return true; +} + +bool FindBadConstructsConsumer::VisitCallExpr(CallExpr* call_expr) { + if (ipc_visitor_) ipc_visitor_->VisitCallExpr(call_expr); + return true; +} + void FindBadConstructsConsumer::CheckChromeClass(SourceLocation record_location, CXXRecordDecl* record) { // By default, the clang checker doesn't check some types (templates, etc). @@ -878,4 +927,26 @@ void FindBadConstructsConsumer::CheckWeakPtrFactoryMembers( } } +// Copied from BlinkGCPlugin, see crrev.com/1135333007 +void FindBadConstructsConsumer::ParseFunctionTemplates( + TranslationUnitDecl* decl) { + if (!instance().getLangOpts().DelayedTemplateParsing) + return; // Nothing to do. + + std::set late_parsed_decls = GetLateParsedFunctionDecls(decl); + clang::Sema& sema = instance().getSema(); + + for (const FunctionDecl* fd : late_parsed_decls) { + assert(fd->isLateTemplateParsed()); + + if (instance().getSourceManager().isInSystemHeader( + instance().getSourceManager().getSpellingLoc(fd->getLocation()))) + continue; + + // Parse and build AST for yet-uninstantiated template functions. + clang::LateParsedTemplate* lpt = sema.LateParsedTemplateMap[fd]; + sema.LateTemplateParser(sema.OpaqueParser, *lpt); + } +} + } // namespace chrome_checker diff --git a/tools/clang/plugins/FindBadConstructsConsumer.h b/tools/clang/plugins/FindBadConstructsConsumer.h index 8f8fc870466309..62bf9cf0e6846e 100644 --- a/tools/clang/plugins/FindBadConstructsConsumer.h +++ b/tools/clang/plugins/FindBadConstructsConsumer.h @@ -20,6 +20,8 @@ #ifndef TOOLS_CLANG_PLUGINS_FINDBADCONSTRUCTSCONSUMER_H_ #define TOOLS_CLANG_PLUGINS_FINDBADCONSTRUCTSCONSUMER_H_ +#include + #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/Attr.h" @@ -29,6 +31,7 @@ #include "clang/Basic/SourceManager.h" #include "clang/Basic/SourceLocation.h" +#include "CheckIPCVisitor.h" #include "ChromeClassTester.h" #include "Options.h" #include "SuppressibleDiagnosticBuilder.h" @@ -43,8 +46,13 @@ class FindBadConstructsConsumer FindBadConstructsConsumer(clang::CompilerInstance& instance, const Options& options); + void Traverse(clang::ASTContext& context); + // RecursiveASTVisitor: + bool TraverseDecl(clang::Decl* decl); bool VisitDecl(clang::Decl* decl); + bool VisitTemplateSpecializationType(clang::TemplateSpecializationType* spec); + bool VisitCallExpr(clang::CallExpr* call_expr); // ChromeClassTester overrides: void CheckChromeClass(clang::SourceLocation record_location, @@ -98,6 +106,8 @@ class FindBadConstructsConsumer void CheckWeakPtrFactoryMembers(clang::SourceLocation record_location, clang::CXXRecordDecl* record); + void ParseFunctionTemplates(clang::TranslationUnitDecl* decl); + unsigned diag_method_requires_override_; unsigned diag_redundant_virtual_specifier_; unsigned diag_base_method_virtual_and_final_; @@ -110,6 +120,8 @@ class FindBadConstructsConsumer unsigned diag_note_implicit_dtor_; unsigned diag_note_public_dtor_; unsigned diag_note_protected_non_virtual_dtor_; + + std::unique_ptr ipc_visitor_; }; } // namespace chrome_checker diff --git a/tools/clang/plugins/Options.h b/tools/clang/plugins/Options.h index e56084e428d508..684dab52bbb604 100644 --- a/tools/clang/plugins/Options.h +++ b/tools/clang/plugins/Options.h @@ -17,10 +17,10 @@ struct Options { // This is needed during the migration from ASTConsumer approach to the // RecursiveASTVisitor approach. See https://crbug.com/436357 for details. bool check_implicit_copy_ctors = false; - // This is needed for some distributed build-sytems to respect banned // paths. See https://crbug.com/583454 for details. bool no_realpath = false; + bool check_ipc = false; }; } // namespace chrome_checker diff --git a/tools/clang/plugins/tests/ipc.cpp b/tools/clang/plugins/tests/ipc.cpp new file mode 100644 index 00000000000000..d2bcef1d58d85e --- /dev/null +++ b/tools/clang/plugins/tests/ipc.cpp @@ -0,0 +1,353 @@ +// 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. + +// Blacklisted typedefs +typedef __INTMAX_TYPE__ intmax_t; +typedef __UINTMAX_TYPE__ uintmax_t; +typedef int intptr_t; +typedef unsigned int uintptr_t; +typedef __WINT_TYPE__ wint_t; +typedef __SIZE_TYPE__ size_t; +typedef __SIZE_TYPE__ rsize_t; +typedef long ssize_t; +typedef __PTRDIFF_TYPE__ ptrdiff_t; +typedef unsigned int dev_t; +typedef int off_t; +typedef long clock_t; +typedef int time_t; +typedef long suseconds_t; + +// Other typedefs +typedef int int32_t; +typedef unsigned int uint32_t; +typedef long int64_t; +typedef unsigned long uint64_t; + +namespace std { + +template +struct allocator {}; + +template > +struct vector {}; + +template +struct pair {}; + +} // namespace std + +namespace base { + +class Pickle {}; + +template +struct Tuple { + T value; +}; + +} // namespace base + +namespace IPC { + +template +struct CheckedTuple { + typedef base::Tuple Tuple; +}; + +template +struct ParamTraits { + static void Write(base::Pickle*, const T&) {} +}; + +template +void WriteParam(base::Pickle* pickle, const T& value) { + ParamTraits::Write(pickle, value); +} + +} // namespace IPC + + +/* Test IPC::WriteParam() usage in templates. ERRORS: 6 */ + +struct Data { + uint32_t value; + size_t size; +}; + +template <> +struct IPC::ParamTraits { + static void Write(base::Pickle* pickle, const Data& p) { + // OK: WriteParam() called in explicit specialization + WriteParam(pickle, p.value); // OK + WriteParam(pickle, p.size); // ERROR + } +}; + +template +struct Container { + T value; +}; + +template +struct IPC::ParamTraits> { + static void Write(base::Pickle* pickle, const Container& container) { + // NOT CHECKED: T is not explicitly referenced + IPC::WriteParam(pickle, container.value); // NOT CHECKED + WriteParam(pickle, container.value); // NOT CHECKED + + // NOT CHECKED: T explicitly referenced + IPC::WriteParam(pickle, container.value); // NOT CHECKED + WriteParam(pickle, container.value); // NOT CHECKED + + // OK: explicit cast to non-dependent allowed type + WriteParam(pickle, static_cast(container.value)); // OK + + // ERROR: explicit cast to non-dependent banned type + WriteParam(pickle, static_cast(container.value)); // ERROR + } +}; + +template +struct MultiContainer { + T value; +}; + +template +struct IPC::ParamTraits> { + static void Write(base::Pickle* pickle, + const MultiContainer& container) { + // NOT CHECKED: template argument explicitly referenced + bool helper[] = { + (WriteParam(pickle, container.value), true)... // NOT CHECKED + }; + (void)helper; + } +}; + +template +struct SomeClass { + static void Write(base::Pickle* pickle) { + // NOT CHECKED: WriteParam() calls on dependent types + IPC::WriteParam(pickle, T(0)); // NOT CHECKED + + // Non-dependent types are checked + IPC::WriteParam(pickle, size_t(0)); // ERROR + IPC::WriteParam(pickle, uint64_t(0)); // OK + } + + template + static void WriteEx(base::Pickle* pickle) { + // NOT CHECKED: WriteParam() calls on dependent types + IPC::WriteParam(pickle, U(0)); // NOT CHECKED + + // Non-dependent types are checked + IPC::WriteParam(pickle, time_t(0)); // ERROR + IPC::WriteParam(pickle, uint32_t(0)); // OK + } +}; + +template +void SomeWriteFunction(base::Pickle* pickle) { + // NOT CHECKED: WriteParam() calls on dependent types + IPC::WriteParam(pickle, T(0)); // NOT CHECKED + + // Non-dependent types are checked + IPC::WriteParam(pickle, long(0)); // ERROR + IPC::WriteParam(pickle, char(0)); // OK + + [&](){ + IPC::WriteParam(pickle, T(0)); // NOT CHECKED + + IPC::WriteParam(pickle, clock_t(0)); // ERROR + IPC::WriteParam(pickle, int64_t(0)); // OK + }(); +} + +void TestWriteParamInTemplates() { + // These specializations call WriteParam() on various banned types, either + // because they were specified directly (long) or because non-blacklisted + // typedef (uint64_t) was stripped down to its underlying type, which is + // blacklisted when used as is (unsigned long). + // However, since it's hard (if not impossible) to check specializations + // properly, we're simply not checking them. + SomeClass::Write(nullptr); + SomeClass::WriteEx(nullptr); + SomeWriteFunction(nullptr); +} + + +/* Test IPC::CheckedTuple. ERRORS: 5 */ + +#define IPC_TUPLE(...) IPC::CheckedTuple<__VA_ARGS__>::Tuple + +#define IPC_MESSAGE_DECL(name, id, in_tuple) \ + struct name ## Meta_ ## id { \ + using InTuple = in_tuple; \ + }; + +#define IPC_TEST_MESSAGE(id, in) \ + IPC_MESSAGE_DECL(TestMessage, id, IPC_TUPLE in) + +struct Empty {}; + +IPC_TEST_MESSAGE(__COUNTER__, (bool, size_t, Empty, long)) // 2 ERRORs + +typedef std::vector long1D; +typedef std::vector long2D; +IPC_TEST_MESSAGE(__COUNTER__, (bool, long2D)) // ERROR + +IPC_TEST_MESSAGE(__COUNTER__, (char, short, std::pair)) // ERROR + +IPC_TEST_MESSAGE(__COUNTER__, (std::vector&>&)) // ERROR + + +/* Check IPC::WriteParam() arguments. ERRORS: 30 */ + +// ERRORS: 21 +void TestWriteParamArgument() { + #define CALL_WRITEPARAM(Type) \ + { \ + Type p; \ + IPC::WriteParam(nullptr, p); \ + } + + // ERROR: blacklisted types / typedefs + CALL_WRITEPARAM(long) // ERROR + CALL_WRITEPARAM(unsigned long) // ERROR + CALL_WRITEPARAM(intmax_t) // ERROR + CALL_WRITEPARAM(uintmax_t) // ERROR + CALL_WRITEPARAM(intptr_t) // ERROR + CALL_WRITEPARAM(uintptr_t) // ERROR + CALL_WRITEPARAM(wint_t) // ERROR + CALL_WRITEPARAM(size_t) // ERROR + CALL_WRITEPARAM(rsize_t) // ERROR + CALL_WRITEPARAM(ssize_t) // ERROR + CALL_WRITEPARAM(ptrdiff_t) // ERROR + CALL_WRITEPARAM(dev_t) // ERROR + CALL_WRITEPARAM(off_t) // ERROR + CALL_WRITEPARAM(clock_t) // ERROR + CALL_WRITEPARAM(time_t) // ERROR + CALL_WRITEPARAM(suseconds_t) // ERROR + + // ERROR: typedef to blacklisted typedef + typedef size_t my_size; + CALL_WRITEPARAM(my_size) // ERROR + + // ERROR: expression ends up with type "unsigned long" + { + uint64_t p = 0; + IPC::WriteParam(nullptr, p + 1); // ERROR + } + + // ERROR: long chain of typedefs, ends up with blacklisted typedef + { + typedef size_t my_size_base; + typedef const my_size_base my_size; + typedef my_size& my_size_ref; + my_size_ref p = 0; + IPC::WriteParam(nullptr, p); // ERROR + } + + // ERROR: template specialization references blacklisted type + CALL_WRITEPARAM(std::vector) // ERROR + CALL_WRITEPARAM(std::vector) // ERROR + + // OK: typedef to blacklisted type + typedef long my_long; + CALL_WRITEPARAM(my_long) // OK + + // OK: other types / typedefs + CALL_WRITEPARAM(char) // OK + CALL_WRITEPARAM(int) // OK + CALL_WRITEPARAM(uint32_t) // OK + CALL_WRITEPARAM(int64_t) // OK + + // OK: long chain of typedefs, ends up with non-blacklisted typedef + { + typedef uint32_t my_int_base; + typedef const my_int_base my_int; + typedef my_int& my_int_ref; + my_int_ref p = 0; + IPC::WriteParam(nullptr, p); // OK + } + + // OK: template specialization references non-blacklisted type + CALL_WRITEPARAM(std::vector) // OK + CALL_WRITEPARAM(std::vector) // OK + + #undef CALL_WRITEPARAM +} + +struct Provider { + typedef unsigned int flags; + + short get_short() const { return 0; } + uint64_t get_uint64() const { return 0; } + long get_long() const { return 0; } + unsigned int get_uint() const { return 0; } + flags get_flags() const { return 0; } + size_t get_size() const { return 0; } + + const std::vector& get_sizes() const { return sizes_data; } + const std::vector& get_uint64s() const { return uint64s_data; } + + template + T get() const { return T(); } + + short short_data; + unsigned int uint_data; + flags flags_data; + long long_data; + size_t size_data; + uint64_t uint64_data; + std::vector sizes_data; + std::vector uint64s_data; +}; + +// ERRORS: 9 +void TestWriteParamMemberArgument() { + Provider p; + + IPC::WriteParam(nullptr, p.get()); // OK + IPC::WriteParam(nullptr, p.get_short()); // OK + IPC::WriteParam(nullptr, p.short_data); // OK + + IPC::WriteParam(nullptr, p.get()); // OK + IPC::WriteParam(nullptr, p.get_uint()); // OK + IPC::WriteParam(nullptr, p.uint_data); // OK + + IPC::WriteParam(nullptr, p.get()); // OK + IPC::WriteParam(nullptr, p.get_flags()); // OK + IPC::WriteParam(nullptr, p.flags_data); // OK + + IPC::WriteParam(nullptr, p.get()); // ERROR + IPC::WriteParam(nullptr, p.get_long()); // ERROR + IPC::WriteParam(nullptr, p.long_data); // ERROR + + // This one is flaky and depends on whether size_t is typedefed to a + // blacklisted type (unsigned long). + //IPC::WriteParam(nullptr, p.get()); // ERROR + IPC::WriteParam(nullptr, p.get_size()); // ERROR + IPC::WriteParam(nullptr, p.size_data); // ERROR + + // Information about uint64_t gets lost, and plugin sees WriteParam() + // call on unsigned long, which is blacklisted. + IPC::WriteParam(nullptr, p.get()); // ERROR + IPC::WriteParam(nullptr, p.get_uint64()); // OK + IPC::WriteParam(nullptr, p.uint64_data); // OK + + // Same thing here, WriteParam() sees vector, and denies it. + IPC::WriteParam(nullptr, p.get>()); // ERROR + IPC::WriteParam(nullptr, p.get_uint64s()); // OK + IPC::WriteParam(nullptr, p.uint64s_data); // OK + + // This one is flaky and depends on whether size_t is typedefed to a + // blacklisted type (unsigned long). + //IPC::WriteParam(nullptr, p.get>()); + IPC::WriteParam(nullptr, p.get_sizes()); // ERROR + IPC::WriteParam(nullptr, p.sizes_data); // ERROR +} + + +/* ERRORS: 41 */ diff --git a/tools/clang/plugins/tests/ipc.flags b/tools/clang/plugins/tests/ipc.flags new file mode 100644 index 00000000000000..49716d669a3f65 --- /dev/null +++ b/tools/clang/plugins/tests/ipc.flags @@ -0,0 +1 @@ +-ferror-limit=0 -Xclang -plugin-arg-find-bad-constructs -Xclang check-ipc \ No newline at end of file diff --git a/tools/clang/plugins/tests/ipc.txt b/tools/clang/plugins/tests/ipc.txt new file mode 100644 index 00000000000000..fbca40b865e413 --- /dev/null +++ b/tools/clang/plugins/tests/ipc.txt @@ -0,0 +1,224 @@ +ipc.cpp:83:26: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t'. + WriteParam(pickle, p.size); // ERROR + ^ +ipc.cpp:107:24: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'long'. + WriteParam(pickle, static_cast(container.value)); // ERROR + ^ +ipc.cpp:135:29: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t'. + IPC::WriteParam(pickle, size_t(0)); // ERROR + ^ +ipc.cpp:145:29: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'time_t'. + IPC::WriteParam(pickle, time_t(0)); // ERROR + ^ +ipc.cpp:156:27: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'long'. + IPC::WriteParam(pickle, long(0)); // ERROR + ^ +ipc.cpp:162:29: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'clock_t'. + IPC::WriteParam(pickle, clock_t(0)); // ERROR + ^ +ipc.cpp:194:1: error: [chromium-ipc] IPC tuple references banned type 'size_t'. +IPC_TEST_MESSAGE(__COUNTER__, (bool, size_t, Empty, long)) // 2 ERRORs +^ +ipc.cpp:190:3: note: expanded from macro 'IPC_TEST_MESSAGE' + IPC_MESSAGE_DECL(TestMessage, id, IPC_TUPLE in) + ^ +ipc.cpp:186:5: note: expanded from macro 'IPC_MESSAGE_DECL' + using InTuple = in_tuple; \ + ^ +ipc.cpp:194:1: error: [chromium-ipc] IPC tuple references banned type 'long'. +ipc.cpp:190:3: note: expanded from macro 'IPC_TEST_MESSAGE' + IPC_MESSAGE_DECL(TestMessage, id, IPC_TUPLE in) + ^ +ipc.cpp:186:5: note: expanded from macro 'IPC_MESSAGE_DECL' + using InTuple = in_tuple; \ + ^ +ipc.cpp:198:1: error: [chromium-ipc] IPC tuple references banned type 'long' via 'long2D'. +IPC_TEST_MESSAGE(__COUNTER__, (bool, long2D)) // ERROR +^ +ipc.cpp:190:3: note: expanded from macro 'IPC_TEST_MESSAGE' + IPC_MESSAGE_DECL(TestMessage, id, IPC_TUPLE in) + ^ +ipc.cpp:186:5: note: expanded from macro 'IPC_MESSAGE_DECL' + using InTuple = in_tuple; \ + ^ +ipc.cpp:197:29: note: see here +typedef std::vector long2D; + ^ +ipc.cpp:196:27: note: see here +typedef std::vector long1D; + ^ +ipc.cpp:200:1: error: [chromium-ipc] IPC tuple references banned type 'size_t' via 'std::pair'. +IPC_TEST_MESSAGE(__COUNTER__, (char, short, std::pair)) // ERROR +^ +ipc.cpp:190:3: note: expanded from macro 'IPC_TEST_MESSAGE' + IPC_MESSAGE_DECL(TestMessage, id, IPC_TUPLE in) + ^ +ipc.cpp:186:5: note: expanded from macro 'IPC_MESSAGE_DECL' + using InTuple = in_tuple; \ + ^ +ipc.cpp:202:1: error: [chromium-ipc] IPC tuple references banned type 'long' via 'std::vector &>'. +IPC_TEST_MESSAGE(__COUNTER__, (std::vector&>&)) // ERROR +^ +ipc.cpp:190:3: note: expanded from macro 'IPC_TEST_MESSAGE' + IPC_MESSAGE_DECL(TestMessage, id, IPC_TUPLE in) + ^ +ipc.cpp:186:5: note: expanded from macro 'IPC_MESSAGE_DECL' + using InTuple = in_tuple; \ + ^ +ipc.cpp:216:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'long'. + CALL_WRITEPARAM(long) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:217:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'unsigned long'. + CALL_WRITEPARAM(unsigned long) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:218:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'intmax_t'. + CALL_WRITEPARAM(intmax_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:219:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'uintmax_t'. + CALL_WRITEPARAM(uintmax_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:220:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'intptr_t'. + CALL_WRITEPARAM(intptr_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:221:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'uintptr_t'. + CALL_WRITEPARAM(uintptr_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:222:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'wint_t'. + CALL_WRITEPARAM(wint_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:223:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t'. + CALL_WRITEPARAM(size_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:224:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'rsize_t'. + CALL_WRITEPARAM(rsize_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:225:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'ssize_t'. + CALL_WRITEPARAM(ssize_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:226:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'ptrdiff_t'. + CALL_WRITEPARAM(ptrdiff_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:227:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'dev_t'. + CALL_WRITEPARAM(dev_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:228:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'off_t'. + CALL_WRITEPARAM(off_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:229:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'clock_t'. + CALL_WRITEPARAM(clock_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:230:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'time_t'. + CALL_WRITEPARAM(time_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:231:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'suseconds_t'. + CALL_WRITEPARAM(suseconds_t) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:235:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t' via 'my_size'. + CALL_WRITEPARAM(my_size) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:234:18: note: see here + typedef size_t my_size; + ^ +ipc.cpp:240:32: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'unsigned long'. + IPC::WriteParam(nullptr, p + 1); // ERROR + ^ +ipc.cpp:249:30: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t' via 'my_size'. + IPC::WriteParam(nullptr, p); // ERROR + ^ +ipc.cpp:246:32: note: see here + typedef const my_size_base my_size; + ^ +ipc.cpp:245:20: note: see here + typedef size_t my_size_base; + ^ +ipc.cpp:253:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'long' via 'std::vector'. + CALL_WRITEPARAM(std::vector) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:254:3: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t' via 'std::vector'. + CALL_WRITEPARAM(std::vector) // ERROR + ^ +ipc.cpp:212:32: note: expanded from macro 'CALL_WRITEPARAM' + IPC::WriteParam(nullptr, p); \ + ^ +ipc.cpp:324:28: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'long'. + IPC::WriteParam(nullptr, p.get()); // ERROR + ^ +ipc.cpp:325:28: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'long'. + IPC::WriteParam(nullptr, p.get_long()); // ERROR + ^ +ipc.cpp:326:30: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'long'. + IPC::WriteParam(nullptr, p.long_data); // ERROR + ^ +ipc.cpp:331:28: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t'. + IPC::WriteParam(nullptr, p.get_size()); // ERROR + ^ +ipc.cpp:332:30: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t'. + IPC::WriteParam(nullptr, p.size_data); // ERROR + ^ +ipc.cpp:336:28: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'unsigned long'. + IPC::WriteParam(nullptr, p.get()); // ERROR + ^ +ipc.cpp:341:28: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'unsigned long' via 'struct std::vector >'. + IPC::WriteParam(nullptr, p.get>()); // ERROR + ^ +ipc.cpp:348:28: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t' via 'std::vector'. + IPC::WriteParam(nullptr, p.get_sizes()); // ERROR + ^ +ipc.cpp:349:30: error: [chromium-ipc] IPC::WriteParam() is called on blacklisted type 'size_t' via 'std::vector'. + IPC::WriteParam(nullptr, p.sizes_data); // ERROR + ^ +41 errors generated.