summaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Checkers/WebKit
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/WebKit')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp93
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h84
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h36
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp155
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp172
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h59
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp167
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp195
8 files changed, 961 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
new file mode 100644
index 0000000000000..34c072ac22414
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
@@ -0,0 +1,93 @@
+//=======- ASTUtils.cpp ------------------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+
+using llvm::Optional;
+namespace clang {
+
+std::pair<const Expr *, bool>
+tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj) {
+ while (E) {
+ if (auto *cast = dyn_cast<CastExpr>(E)) {
+ if (StopAtFirstRefCountedObj) {
+ if (auto *ConversionFunc =
+ dyn_cast_or_null<FunctionDecl>(cast->getConversionFunction())) {
+ if (isCtorOfRefCounted(ConversionFunc))
+ return {E, true};
+ }
+ }
+ // FIXME: This can give false "origin" that would lead to false negatives
+ // in checkers. See https://reviews.llvm.org/D37023 for reference.
+ E = cast->getSubExpr();
+ continue;
+ }
+ if (auto *call = dyn_cast<CallExpr>(E)) {
+ if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(call)) {
+ if (isGetterOfRefCounted(memberCall->getMethodDecl())) {
+ E = memberCall->getImplicitObjectArgument();
+ if (StopAtFirstRefCountedObj) {
+ return {E, true};
+ }
+ continue;
+ }
+ }
+
+ if (auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(E)) {
+ if (operatorCall->getNumArgs() == 1) {
+ E = operatorCall->getArg(0);
+ continue;
+ }
+ }
+
+ if (auto *callee = call->getDirectCallee()) {
+ if (isCtorOfRefCounted(callee)) {
+ if (StopAtFirstRefCountedObj)
+ return {E, true};
+
+ E = call->getArg(0);
+ continue;
+ }
+
+ if (isPtrConversion(callee)) {
+ E = call->getArg(0);
+ continue;
+ }
+ }
+ }
+ if (auto *unaryOp = dyn_cast<UnaryOperator>(E)) {
+ // FIXME: Currently accepts ANY unary operator. Is it OK?
+ E = unaryOp->getSubExpr();
+ continue;
+ }
+
+ break;
+ }
+ // Some other expression.
+ return {E, false};
+}
+
+bool isASafeCallArg(const Expr *E) {
+ assert(E);
+ if (auto *Ref = dyn_cast<DeclRefExpr>(E)) {
+ if (auto *D = dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
+ if (isa<ParmVarDecl>(D) || D->isLocalVarDecl())
+ return true;
+ }
+ }
+
+ // TODO: checker for method calls on non-refcounted objects
+ return isa<CXXThisExpr>(E);
+}
+
+} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
new file mode 100644
index 0000000000000..ed45777554571
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -0,0 +1,84 @@
+//=======- ASTUtis.h ---------------------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_ASTUTILS_H
+
+#include "clang/AST/Decl.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/Support/Casting.h"
+
+#include <string>
+#include <utility>
+
+namespace clang {
+class CXXRecordDecl;
+class CXXBaseSpecifier;
+class FunctionDecl;
+class CXXMethodDecl;
+class Expr;
+
+/// This function de-facto defines a set of transformations that we consider
+/// safe (in heuristical sense). These transformation if passed a safe value as
+/// an input should provide a safe value (or an object that provides safe
+/// values).
+///
+/// For more context see Static Analyzer checkers documentation - specifically
+/// webkit.UncountedCallArgsChecker checker. Whitelist of transformations:
+/// - constructors of ref-counted types (including factory methods)
+/// - getters of ref-counted types
+/// - member overloaded operators
+/// - casts
+/// - unary operators like ``&`` or ``*``
+///
+/// If passed expression is of type uncounted pointer/reference we try to find
+/// the "origin" of the pointer value.
+/// Origin can be for example a local variable, nullptr, constant or
+/// this-pointer.
+///
+/// Certain subexpression nodes represent transformations that don't affect
+/// where the memory address originates from. We try to traverse such
+/// subexpressions to get to the relevant child nodes. Whenever we encounter a
+/// subexpression that either can't be ignored, we don't model its semantics or
+/// that has multiple children we stop.
+///
+/// \p E is an expression of uncounted pointer/reference type.
+/// If \p StopAtFirstRefCountedObj is true and we encounter a subexpression that
+/// represents ref-counted object during the traversal we return relevant
+/// sub-expression and true.
+///
+/// \returns subexpression that we traversed to and if \p
+/// StopAtFirstRefCountedObj is true we also return whether we stopped early.
+std::pair<const clang::Expr *, bool>
+tryToFindPtrOrigin(const clang::Expr *E, bool StopAtFirstRefCountedObj);
+
+/// For \p E referring to a ref-countable/-counted pointer/reference we return
+/// whether it's a safe call argument. Examples: function parameter or
+/// this-pointer. The logic relies on the set of recursive rules we enforce for
+/// WebKit codebase.
+///
+/// \returns Whether \p E is a safe call arugment.
+bool isASafeCallArg(const clang::Expr *E);
+
+/// \returns name of AST node or empty string.
+template <typename T> std::string safeGetName(const T *ASTNode) {
+ const auto *const ND = llvm::dyn_cast_or_null<clang::NamedDecl>(ASTNode);
+ if (!ND)
+ return "";
+
+ // In case F is for example "operator|" the getName() method below would
+ // assert.
+ if (!ND->getDeclName().isIdentifier())
+ return "";
+
+ return ND->getName().str();
+}
+
+} // namespace clang
+
+#endif
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
new file mode 100644
index 0000000000000..781a8d746001f
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
@@ -0,0 +1,36 @@
+//=======- DiagOutputUtils.h -------------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_DIAGPRINTUTILS_H
+
+#include "clang/AST/Decl.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace clang {
+
+template <typename NamedDeclDerivedT>
+void printQuotedQualifiedName(llvm::raw_ostream &Os,
+ const NamedDeclDerivedT &D) {
+ Os << "'";
+ D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/true);
+ Os << "'";
+}
+
+template <typename NamedDeclDerivedT>
+void printQuotedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D) {
+ Os << "'";
+ D->getNameForDiagnostic(Os, D->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/false);
+ Os << "'";
+}
+
+} // namespace clang
+
+#endif
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
new file mode 100644
index 0000000000000..3956db933b35b
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/NoUncountedMembersChecker.cpp
@@ -0,0 +1,155 @@
+//=======- NoUncountedMembersChecker.cpp -------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/Support/Casting.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class NoUncountedMemberChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug;
+ mutable BugReporter *BR;
+
+public:
+ NoUncountedMemberChecker()
+ : Bug(this,
+ "Member variable is a raw-poiner/reference to reference-countable "
+ "type",
+ "WebKit coding guidelines") {}
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const NoUncountedMemberChecker *Checker;
+ explicit LocalVisitor(const NoUncountedMemberChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitRecordDecl(const RecordDecl *RD) {
+ Checker->visitRecordDecl(RD);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitRecordDecl(const RecordDecl *RD) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ for (auto Member : RD->fields()) {
+ const Type *MemberType = Member->getType().getTypePtrOrNull();
+ if (!MemberType)
+ continue;
+
+ if (auto *MemberCXXRD = MemberType->getPointeeCXXRecordDecl()) {
+ // If we don't see the definition we just don't know.
+ if (MemberCXXRD->hasDefinition() && isRefCountable(MemberCXXRD))
+ reportBug(Member, MemberType, MemberCXXRD, RD);
+ }
+ }
+ }
+
+ bool shouldSkipDecl(const RecordDecl *RD) const {
+ if (!RD->isThisDeclarationADefinition())
+ return true;
+
+ if (RD->isImplicit())
+ return true;
+
+ if (RD->isLambda())
+ return true;
+
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ const auto RDLocation = RD->getLocation();
+ if (!RDLocation.isValid())
+ return true;
+
+ const auto Kind = RD->getTagKind();
+ // FIMXE: Should we check union members too?
+ if (Kind != TTK_Struct && Kind != TTK_Class)
+ return true;
+
+ // Ignore CXXRecords that come from system headers.
+ if (BR->getSourceManager().isInSystemHeader(RDLocation))
+ return true;
+
+ // Ref-counted smartpointers actually have raw-pointer to uncounted type as
+ // a member but we trust them to handle it correctly.
+ auto CXXRD = llvm::dyn_cast_or_null<CXXRecordDecl>(RD);
+ if (CXXRD)
+ return isRefCounted(CXXRD);
+
+ return false;
+ }
+
+ void reportBug(const FieldDecl *Member, const Type *MemberType,
+ const CXXRecordDecl *MemberCXXRD,
+ const RecordDecl *ClassCXXRD) const {
+ assert(Member);
+ assert(MemberType);
+ assert(MemberCXXRD);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << "Member variable ";
+ printQuotedName(Os, Member);
+ Os << " in ";
+ printQuotedQualifiedName(Os, ClassCXXRD);
+ Os << " is a "
+ << (isa<PointerType>(MemberType) ? "raw pointer" : "reference")
+ << " to ref-countable type ";
+ printQuotedQualifiedName(Os, MemberCXXRD);
+ Os << "; member variables must be ref-counted.";
+
+ PathDiagnosticLocation BSLoc(Member->getSourceRange().getBegin(),
+ BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(Member->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerNoUncountedMemberChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<NoUncountedMemberChecker>();
+}
+
+bool ento::shouldRegisterNoUncountedMemberChecker(
+ const CheckerManager &Mgr) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
new file mode 100644
index 0000000000000..168cfd511170c
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
@@ -0,0 +1,172 @@
+//=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "PtrTypesSemantics.h"
+#include "ASTUtils.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+
+using llvm::Optional;
+using namespace clang;
+
+namespace {
+
+bool hasPublicRefAndDeref(const CXXRecordDecl *R) {
+ assert(R);
+
+ bool hasRef = false;
+ bool hasDeref = false;
+ for (const CXXMethodDecl *MD : R->methods()) {
+ const auto MethodName = safeGetName(MD);
+
+ if (MethodName == "ref" && MD->getAccess() == AS_public) {
+ if (hasDeref)
+ return true;
+ hasRef = true;
+ } else if (MethodName == "deref" && MD->getAccess() == AS_public) {
+ if (hasRef)
+ return true;
+ hasDeref = true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+namespace clang {
+
+const CXXRecordDecl *isRefCountable(const CXXBaseSpecifier *Base) {
+ assert(Base);
+
+ const Type *T = Base->getType().getTypePtrOrNull();
+ if (!T)
+ return nullptr;
+
+ const CXXRecordDecl *R = T->getAsCXXRecordDecl();
+ if (!R)
+ return nullptr;
+
+ return hasPublicRefAndDeref(R) ? R : nullptr;
+}
+
+bool isRefCountable(const CXXRecordDecl *R) {
+ assert(R);
+
+ R = R->getDefinition();
+ assert(R);
+
+ if (hasPublicRefAndDeref(R))
+ return true;
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(const_cast<CXXRecordDecl *>(R));
+
+ const auto isRefCountableBase = [](const CXXBaseSpecifier *Base,
+ CXXBasePath &) {
+ return clang::isRefCountable(Base);
+ };
+
+ return R->lookupInBases(isRefCountableBase, Paths,
+ /*LookupInDependent =*/true);
+}
+
+bool isCtorOfRefCounted(const clang::FunctionDecl *F) {
+ assert(F);
+ const auto &FunctionName = safeGetName(F);
+
+ return FunctionName == "Ref" || FunctionName == "makeRef"
+
+ || FunctionName == "RefPtr" || FunctionName == "makeRefPtr"
+
+ || FunctionName == "UniqueRef" || FunctionName == "makeUniqueRef" ||
+ FunctionName == "makeUniqueRefWithoutFastMallocCheck"
+
+ || FunctionName == "String" || FunctionName == "AtomString" ||
+ FunctionName == "UniqueString"
+ // FIXME: Implement as attribute.
+ || FunctionName == "Identifier";
+}
+
+bool isUncounted(const CXXRecordDecl *Class) {
+ // Keep isRefCounted first as it's cheaper.
+ return !isRefCounted(Class) && isRefCountable(Class);
+}
+
+bool isUncountedPtr(const Type *T) {
+ assert(T);
+
+ if (T->isPointerType() || T->isReferenceType()) {
+ if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
+ return isUncounted(CXXRD);
+ }
+ }
+ return false;
+}
+
+bool isGetterOfRefCounted(const CXXMethodDecl *M) {
+ assert(M);
+
+ if (isa<CXXMethodDecl>(M)) {
+ const CXXRecordDecl *calleeMethodsClass = M->getParent();
+ auto className = safeGetName(calleeMethodsClass);
+ auto methodName = safeGetName(M);
+
+ if (((className == "Ref" || className == "RefPtr") &&
+ methodName == "get") ||
+ ((className == "String" || className == "AtomString" ||
+ className == "AtomStringImpl" || className == "UniqueString" ||
+ className == "UniqueStringImpl" || className == "Identifier") &&
+ methodName == "impl"))
+ return true;
+
+ // Ref<T> -> T conversion
+ // FIXME: Currently allowing any Ref<T> -> whatever cast.
+ if (className == "Ref" || className == "RefPtr") {
+ if (auto *maybeRefToRawOperator = dyn_cast<CXXConversionDecl>(M)) {
+ if (auto *targetConversionType =
+ maybeRefToRawOperator->getConversionType().getTypePtrOrNull()) {
+ if (isUncountedPtr(targetConversionType)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool isRefCounted(const CXXRecordDecl *R) {
+ assert(R);
+ if (auto *TmplR = R->getTemplateInstantiationPattern()) {
+ // FIXME: String/AtomString/UniqueString
+ const auto &ClassName = safeGetName(TmplR);
+ return ClassName == "RefPtr" || ClassName == "Ref";
+ }
+ return false;
+}
+
+bool isPtrConversion(const FunctionDecl *F) {
+ assert(F);
+ if (isCtorOfRefCounted(F))
+ return true;
+
+ // FIXME: check # of params == 1
+ const auto FunctionName = safeGetName(F);
+ if (FunctionName == "getPtr" || FunctionName == "WeakPtr" ||
+ FunctionName == "makeWeakPtr"
+
+ || FunctionName == "downcast" || FunctionName == "bitwise_cast")
+ return true;
+
+ return false;
+}
+
+} // namespace clang
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
new file mode 100644
index 0000000000000..83d9c0bcc13b3
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
@@ -0,0 +1,59 @@
+//=======- PtrTypesSemantics.cpp ---------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
+#define LLVM_CLANG_ANALYZER_WEBKIT_PTRTYPESEMANTICS_H
+
+namespace clang {
+class CXXBaseSpecifier;
+class CXXMethodDecl;
+class CXXRecordDecl;
+class Expr;
+class FunctionDecl;
+class Type;
+
+// Ref-countability of a type is implicitly defined by Ref<T> and RefPtr<T>
+// implementation. It can be modeled as: type T having public methods ref() and
+// deref()
+
+// In WebKit there are two ref-counted templated smart pointers: RefPtr<T> and
+// Ref<T>.
+
+/// \returns CXXRecordDecl of the base if the type is ref-countable, nullptr if
+/// not.
+const clang::CXXRecordDecl *isRefCountable(const clang::CXXBaseSpecifier *Base);
+
+/// \returns true if \p Class is ref-countable, false if not.
+/// Asserts that \p Class IS a definition.
+bool isRefCountable(const clang::CXXRecordDecl *Class);
+
+/// \returns true if \p Class is ref-counted, false if not.
+bool isRefCounted(const clang::CXXRecordDecl *Class);
+
+/// \returns true if \p Class is ref-countable AND not ref-counted, false if
+/// not. Asserts that \p Class IS a definition.
+bool isUncounted(const clang::CXXRecordDecl *Class);
+
+/// \returns true if \p T is either a raw pointer or reference to an uncounted
+/// class, false if not.
+bool isUncountedPtr(const clang::Type *T);
+
+/// \returns true if \p F creates ref-countable object from uncounted parameter,
+/// false if not.
+bool isCtorOfRefCounted(const clang::FunctionDecl *F);
+
+/// \returns true if \p M is getter of a ref-counted class, false if not.
+bool isGetterOfRefCounted(const clang::CXXMethodDecl *Method);
+
+/// \returns true if \p F is a conversion between ref-countable or ref-counted
+/// pointer types.
+bool isPtrConversion(const FunctionDecl *F);
+
+} // namespace clang
+
+#endif
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
new file mode 100644
index 0000000000000..81ce284c2dc7c
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
@@ -0,0 +1,167 @@
+//=======- RefCntblBaseVirtualDtor.cpp ---------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class RefCntblBaseVirtualDtorChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ BugType Bug;
+ mutable BugReporter *BR;
+
+public:
+ RefCntblBaseVirtualDtorChecker()
+ : Bug(this,
+ "Reference-countable base class doesn't have virtual destructor",
+ "WebKit coding guidelines") {}
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const RefCntblBaseVirtualDtorChecker *Checker;
+ explicit LocalVisitor(const RefCntblBaseVirtualDtorChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitCXXRecordDecl(const CXXRecordDecl *RD) {
+ Checker->visitCXXRecordDecl(RD);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitCXXRecordDecl(const CXXRecordDecl *RD) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ CXXBasePaths Paths;
+ Paths.setOrigin(RD);
+
+ const CXXBaseSpecifier *ProblematicBaseSpecifier = nullptr;
+ const CXXRecordDecl *ProblematicBaseClass = nullptr;
+
+ const auto IsPublicBaseRefCntblWOVirtualDtor =
+ [RD, &ProblematicBaseSpecifier,
+ &ProblematicBaseClass](const CXXBaseSpecifier *Base, CXXBasePath &) {
+ const auto AccSpec = Base->getAccessSpecifier();
+ if (AccSpec == AS_protected || AccSpec == AS_private ||
+ (AccSpec == AS_none && RD->isClass()))
+ return false;
+
+ llvm::Optional<const clang::CXXRecordDecl *> MaybeRefCntblBaseRD =
+ isRefCountable(Base);
+ if (!MaybeRefCntblBaseRD.hasValue())
+ return false;
+
+ const CXXRecordDecl *RefCntblBaseRD = MaybeRefCntblBaseRD.getValue();
+ if (!RefCntblBaseRD)
+ return false;
+
+ const auto *Dtor = RefCntblBaseRD->getDestructor();
+ if (!Dtor || !Dtor->isVirtual()) {
+ ProblematicBaseSpecifier = Base;
+ ProblematicBaseClass = RefCntblBaseRD;
+ return true;
+ }
+
+ return false;
+ };
+
+ if (RD->lookupInBases(IsPublicBaseRefCntblWOVirtualDtor, Paths,
+ /*LookupInDependent =*/true)) {
+ reportBug(RD, ProblematicBaseSpecifier, ProblematicBaseClass);
+ }
+ }
+
+ bool shouldSkipDecl(const CXXRecordDecl *RD) const {
+ if (!RD->isThisDeclarationADefinition())
+ return true;
+
+ if (RD->isImplicit())
+ return true;
+
+ if (RD->isLambda())
+ return true;
+
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ const auto RDLocation = RD->getLocation();
+ if (!RDLocation.isValid())
+ return true;
+
+ const auto Kind = RD->getTagKind();
+ if (Kind != TTK_Struct && Kind != TTK_Class)
+ return true;
+
+ // Ignore CXXRecords that come from system headers.
+ if (BR->getSourceManager().getFileCharacteristic(RDLocation) !=
+ SrcMgr::C_User)
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const CXXRecordDecl *DerivedClass,
+ const CXXBaseSpecifier *BaseSpec,
+ const CXXRecordDecl *ProblematicBaseClass) const {
+ assert(DerivedClass);
+ assert(BaseSpec);
+ assert(ProblematicBaseClass);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ Os << (ProblematicBaseClass->isClass() ? "Class" : "Struct") << " ";
+ printQuotedQualifiedName(Os, ProblematicBaseClass);
+
+ Os << " is used as a base of "
+ << (DerivedClass->isClass() ? "class" : "struct") << " ";
+ printQuotedQualifiedName(Os, DerivedClass);
+
+ Os << " but doesn't have virtual destructor";
+
+ PathDiagnosticLocation BSLoc(BaseSpec->getSourceRange().getBegin(),
+ BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(BaseSpec->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerRefCntblBaseVirtualDtorChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<RefCntblBaseVirtualDtorChecker>();
+}
+
+bool ento::shouldRegisterRefCntblBaseVirtualDtorChecker(
+ const CheckerManager &mgr) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
new file mode 100644
index 0000000000000..940a1f349831d
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -0,0 +1,195 @@
+//=======- UncountedCallArgsChecker.cpp --------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ASTUtils.h"
+#include "DiagOutputUtils.h"
+#include "PtrTypesSemantics.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseSet.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class UncountedCallArgsChecker
+ : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+ BugType Bug{this,
+ "Uncounted call argument for a raw pointer/reference parameter",
+ "WebKit coding guidelines"};
+ mutable BugReporter *BR;
+
+public:
+
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const UncountedCallArgsChecker *Checker;
+ explicit LocalVisitor(const UncountedCallArgsChecker *Checker)
+ : Checker(Checker) {
+ assert(Checker);
+ }
+
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return false; }
+
+ bool VisitCallExpr(const CallExpr *CE) {
+ Checker->visitCallExpr(CE);
+ return true;
+ }
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ void visitCallExpr(const CallExpr *CE) const {
+ if (shouldSkipCall(CE))
+ return;
+
+ if (auto *F = CE->getDirectCallee()) {
+ // Skip the first argument for overloaded member operators (e. g. lambda
+ // or std::function call operator).
+ unsigned ArgIdx =
+ isa<CXXOperatorCallExpr>(CE) && dyn_cast_or_null<CXXMethodDecl>(F);
+
+ for (auto P = F->param_begin();
+ // FIXME: Also check variadic function parameters.
+ // FIXME: Also check default function arguments. Probably a different
+ // checker. In case there are default arguments the call can have
+ // fewer arguments than the callee has parameters.
+ P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
+ // TODO: attributes.
+ // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>())
+ // continue;
+
+ const auto *ArgType = (*P)->getType().getTypePtrOrNull();
+ if (!ArgType)
+ continue; // FIXME? Should we bail?
+
+ // FIXME: more complex types (arrays, references to raw pointers, etc)
+ if (!isUncountedPtr(ArgType))
+ continue;
+
+ const auto *Arg = CE->getArg(ArgIdx);
+
+ std::pair<const clang::Expr *, bool> ArgOrigin =
+ tryToFindPtrOrigin(Arg, true);
+
+ // Temporary ref-counted object created as part of the call argument
+ // would outlive the call.
+ if (ArgOrigin.second)
+ continue;
+
+ if (isa<CXXNullPtrLiteralExpr>(ArgOrigin.first)) {
+ // foo(nullptr)
+ continue;
+ }
+ if (isa<IntegerLiteral>(ArgOrigin.first)) {
+ // FIXME: Check the value.
+ // foo(NULL)
+ continue;
+ }
+
+ if (isASafeCallArg(ArgOrigin.first))
+ continue;
+
+ reportBug(Arg, *P);
+ }
+ }
+ }
+
+ bool shouldSkipCall(const CallExpr *CE) const {
+ if (CE->getNumArgs() == 0)
+ return false;
+
+ // If an assignment is problematic we should warn about the sole existence
+ // of object on LHS.
+ if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ // Note: assignemnt to built-in type isn't derived from CallExpr.
+ if (MemberOp->isAssignmentOp())
+ return false;
+ }
+
+ const auto *Callee = CE->getDirectCallee();
+ if (!Callee)
+ return false;
+
+ auto overloadedOperatorType = Callee->getOverloadedOperator();
+ if (overloadedOperatorType == OO_EqualEqual ||
+ overloadedOperatorType == OO_ExclaimEqual ||
+ overloadedOperatorType == OO_LessEqual ||
+ overloadedOperatorType == OO_GreaterEqual ||
+ overloadedOperatorType == OO_Spaceship ||
+ overloadedOperatorType == OO_AmpAmp ||
+ overloadedOperatorType == OO_PipePipe)
+ return true;
+
+ if (isCtorOfRefCounted(Callee))
+ return true;
+
+ auto name = safeGetName(Callee);
+ if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" ||
+ name == "makeWeakPtr" || name == "downcast" || name == "bitwise_cast" ||
+ name == "is" || name == "equal" || name == "hash" ||
+ name == "isType"
+ // FIXME: Most/all of these should be implemented via attributes.
+ || name == "equalIgnoringASCIICase" ||
+ name == "equalIgnoringASCIICaseCommon" ||
+ name == "equalIgnoringNullity")
+ return true;
+
+ return false;
+ }
+
+ void reportBug(const Expr *CallArg, const ParmVarDecl *Param) const {
+ assert(CallArg);
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ const std::string paramName = safeGetName(Param);
+ Os << "Call argument";
+ if (!paramName.empty()) {
+ Os << " for parameter ";
+ printQuotedQualifiedName(Os, Param);
+ }
+ Os << " is uncounted and unsafe.";
+
+ const SourceLocation SrcLocToReport =
+ isa<CXXDefaultArgExpr>(CallArg) ? Param->getDefaultArg()->getExprLoc()
+ : CallArg->getSourceRange().getBegin();
+
+ PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
+ auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
+ Report->addRange(CallArg->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<UncountedCallArgsChecker>();
+}
+
+bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) {
+ return true;
+}