[clang] 51a5b77 - [Webkit Checkers] Introduce a Webkit checker for memory unsafe casts (#114606)

via cfe-commits cfe-commits at lists.llvm.org
Thu Dec 5 11:01:31 PST 2024


Author: Rashmi Mudduluru
Date: 2024-12-05T11:01:27-08:00
New Revision: 51a5b77b57ab3061f4a7fbcf858d7304cd9ccdce

URL: https://github.com/llvm/llvm-project/commit/51a5b77b57ab3061f4a7fbcf858d7304cd9ccdce
DIFF: https://github.com/llvm/llvm-project/commit/51a5b77b57ab3061f4a7fbcf858d7304cd9ccdce.diff

LOG: [Webkit Checkers] Introduce a Webkit checker for memory unsafe casts (#114606)

This PR introduces a new checker
`[alpha.webkit.MemoryUnsafeCastChecker]` that warns all downcasts from a base type to a derived type.

rdar://137766829

Added: 
    clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp
    clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp
    clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm

Modified: 
    clang/docs/analyzer/checkers.rst
    clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
    clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index d99d4035a57acf..29d5e1f92a69c2 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3438,6 +3438,31 @@ alpha.WebKit
 
 .. _alpha-webkit-NoUncheckedPtrMemberChecker:
 
+alpha.webkit.MemoryUnsafeCastChecker
+""""""""""""""""""""""""""""""""""""""
+Check for all casts from a base type to its derived type as these might be memory-unsafe.
+
+Example:
+
+.. code-block:: cpp
+
+    class Base { };
+    class Derived : public Base { };
+
+    void f(Base* base) {
+        Derived* derived = static_cast<Derived*>(base); // ERROR
+    }
+
+For all cast operations (C-style casts, static_cast, reinterpret_cast, dynamic_cast), if the source type a `Base*` and the destination type is `Derived*`, where `Derived` inherits from `Base`, the static analyzer should signal an error.
+
+This applies to:
+
+- C structs, C++ structs and classes, and Objective-C classes and protocols.
+- Pointers and references.
+- Inside template instantiations and macro expansions that are visible to the compiler.
+
+For types like this, instead of using built in casts, the programmer will use helper functions that internally perform the appropriate type check and disable static analysis.
+
 alpha.webkit.NoUncheckedPtrMemberChecker
 """"""""""""""""""""""""""""""""""""""""
 Raw pointers and references to an object which supports CheckedPtr or CheckedRef can't be used as class members. Only CheckedPtr, CheckedRef, RefPtr, or Ref are allowed.

diff  --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 9be82622f264ca..b34e940682fc5e 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1749,6 +1749,10 @@ def UncountedLambdaCapturesChecker : Checker<"UncountedLambdaCapturesChecker">,
 
 let ParentPackage = WebKitAlpha in {
 
+def MemoryUnsafeCastChecker : Checker<"MemoryUnsafeCastChecker">,
+  HelpText<"Check for memory unsafe casts from base type to derived type.">,
+  Documentation<HasDocumentation>;
+
 def NoUncheckedPtrMemberChecker : Checker<"NoUncheckedPtrMemberChecker">,
   HelpText<"Check for no unchecked member variables.">,
   Documentation<HasDocumentation>;

diff  --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
index 2b15d31053cf40..fcbe8b864b6e41 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -131,6 +131,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   VirtualCallChecker.cpp
   WebKit/RawPtrRefMemberChecker.cpp
   WebKit/ASTUtils.cpp
+  WebKit/MemoryUnsafeCastChecker.cpp
   WebKit/PtrTypesSemantics.cpp
   WebKit/RefCntblBaseVirtualDtorChecker.cpp
   WebKit/RawPtrRefCallArgsChecker.cpp

diff  --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp
new file mode 100644
index 00000000000000..eeaccf9b70524a
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp
@@ -0,0 +1,188 @@
+//=======- MemoryUnsafeCastChecker.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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines MemoryUnsafeCast checker, which checks for casts from a
+// base type to a derived type.
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.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 "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+static constexpr const char *const BaseNode = "BaseNode";
+static constexpr const char *const DerivedNode = "DerivedNode";
+static constexpr const char *const FromCastNode = "FromCast";
+static constexpr const char *const ToCastNode = "ToCast";
+static constexpr const char *const WarnRecordDecl = "WarnRecordDecl";
+
+class MemoryUnsafeCastChecker : public Checker<check::ASTCodeBody> {
+  BugType BT{this, "Unsafe cast", "WebKit coding guidelines"};
+
+public:
+  void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+                        BugReporter &BR) const;
+};
+} // end namespace
+
+static void emitDiagnostics(const BoundNodes &Nodes, BugReporter &BR,
+                            AnalysisDeclContext *ADC,
+                            const MemoryUnsafeCastChecker *Checker,
+                            const BugType &BT) {
+  const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
+  const NamedDecl *Base = Nodes.getNodeAs<NamedDecl>(BaseNode);
+  const NamedDecl *Derived = Nodes.getNodeAs<NamedDecl>(DerivedNode);
+  assert(CE && Base && Derived);
+
+  std::string Diagnostics;
+  llvm::raw_string_ostream OS(Diagnostics);
+  OS << "Unsafe cast from base type '" << Base->getNameAsString()
+     << "' to derived type '" << Derived->getNameAsString() << "'";
+  PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
+                               BR.getSourceManager());
+  auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
+  Report->addRange(CE->getSourceRange());
+  BR.emitReport(std::move(Report));
+}
+
+static void emitDiagnosticsUnrelated(const BoundNodes &Nodes, BugReporter &BR,
+                                     AnalysisDeclContext *ADC,
+                                     const MemoryUnsafeCastChecker *Checker,
+                                     const BugType &BT) {
+  const auto *CE = Nodes.getNodeAs<CastExpr>(WarnRecordDecl);
+  const NamedDecl *FromCast = Nodes.getNodeAs<NamedDecl>(FromCastNode);
+  const NamedDecl *ToCast = Nodes.getNodeAs<NamedDecl>(ToCastNode);
+  assert(CE && FromCast && ToCast);
+
+  std::string Diagnostics;
+  llvm::raw_string_ostream OS(Diagnostics);
+  OS << "Unsafe cast from type '" << FromCast->getNameAsString()
+     << "' to an unrelated type '" << ToCast->getNameAsString() << "'";
+  PathDiagnosticLocation BSLoc(CE->getSourceRange().getBegin(),
+                               BR.getSourceManager());
+  auto Report = std::make_unique<BasicBugReport>(BT, OS.str(), BSLoc);
+  Report->addRange(CE->getSourceRange());
+  BR.emitReport(std::move(Report));
+}
+
+namespace clang {
+namespace ast_matchers {
+AST_MATCHER_P(StringLiteral, mentionsBoundType, std::string, BindingID) {
+  return Builder->removeBindings([this, &Node](const BoundNodesMap &Nodes) {
+    const auto &BN = Nodes.getNode(this->BindingID);
+    if (const auto *ND = BN.get<NamedDecl>()) {
+      return ND->getName() != Node.getString();
+    }
+    return true;
+  });
+}
+} // end namespace ast_matchers
+} // end namespace clang
+
+static decltype(auto) hasTypePointingTo(DeclarationMatcher DeclM) {
+  return hasType(pointerType(pointee(hasDeclaration(DeclM))));
+}
+
+void MemoryUnsafeCastChecker::checkASTCodeBody(const Decl *D,
+                                               AnalysisManager &AM,
+                                               BugReporter &BR) const {
+
+  AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+  // Match downcasts from base type to derived type and warn
+  auto MatchExprPtr = allOf(
+      hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
+      hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
+                            .bind(DerivedNode)),
+      unless(anyOf(hasSourceExpression(cxxThisExpr()),
+                   hasTypePointingTo(templateTypeParmDecl()))));
+  auto MatchExprPtrObjC = allOf(
+      hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
+          pointee(hasDeclaration(objcInterfaceDecl().bind(BaseNode))))))),
+      ignoringImpCasts(hasType(objcObjectPointerType(pointee(hasDeclaration(
+          objcInterfaceDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
+              .bind(DerivedNode)))))));
+  auto MatchExprRefTypeDef =
+      allOf(hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
+                hasDeclaration(decl(cxxRecordDecl().bind(BaseNode))))))),
+            hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+                decl(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
+                         .bind(DerivedNode)))))),
+            unless(anyOf(hasSourceExpression(hasDescendant(cxxThisExpr())),
+                         hasType(templateTypeParmDecl()))));
+
+  auto ExplicitCast = explicitCastExpr(anyOf(MatchExprPtr, MatchExprRefTypeDef,
+                                             MatchExprPtrObjC))
+                          .bind(WarnRecordDecl);
+  auto Cast = stmt(ExplicitCast);
+
+  auto Matches =
+      match(stmt(forEachDescendant(Cast)), *D->getBody(), AM.getASTContext());
+  for (BoundNodes Match : Matches)
+    emitDiagnostics(Match, BR, ADC, this, BT);
+
+  // Match casts between unrelated types and warn
+  auto MatchExprPtrUnrelatedTypes = allOf(
+      hasSourceExpression(
+          hasTypePointingTo(cxxRecordDecl().bind(FromCastNode))),
+      hasTypePointingTo(cxxRecordDecl().bind(ToCastNode)),
+      unless(anyOf(hasTypePointingTo(cxxRecordDecl(
+                       isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))),
+                   hasSourceExpression(hasTypePointingTo(cxxRecordDecl(
+                       isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))));
+  auto MatchExprPtrObjCUnrelatedTypes = allOf(
+      hasSourceExpression(ignoringImpCasts(hasType(objcObjectPointerType(
+          pointee(hasDeclaration(objcInterfaceDecl().bind(FromCastNode))))))),
+      ignoringImpCasts(hasType(objcObjectPointerType(
+          pointee(hasDeclaration(objcInterfaceDecl().bind(ToCastNode)))))),
+      unless(anyOf(
+          ignoringImpCasts(hasType(
+              objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
+                  isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
+          hasSourceExpression(ignoringImpCasts(hasType(
+              objcObjectPointerType(pointee(hasDeclaration(objcInterfaceDecl(
+                  isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
+  auto MatchExprRefTypeDefUnrelated = allOf(
+      hasSourceExpression(hasType(hasUnqualifiedDesugaredType(recordType(
+          hasDeclaration(decl(cxxRecordDecl().bind(FromCastNode))))))),
+      hasType(hasUnqualifiedDesugaredType(
+          recordType(hasDeclaration(decl(cxxRecordDecl().bind(ToCastNode)))))),
+      unless(anyOf(
+          hasType(hasUnqualifiedDesugaredType(
+              recordType(hasDeclaration(decl(cxxRecordDecl(
+                  isSameOrDerivedFrom(equalsBoundNode(FromCastNode)))))))),
+          hasSourceExpression(hasType(hasUnqualifiedDesugaredType(
+              recordType(hasDeclaration(decl(cxxRecordDecl(
+                  isSameOrDerivedFrom(equalsBoundNode(ToCastNode))))))))))));
+
+  auto ExplicitCastUnrelated =
+      explicitCastExpr(anyOf(MatchExprPtrUnrelatedTypes,
+                             MatchExprPtrObjCUnrelatedTypes,
+                             MatchExprRefTypeDefUnrelated))
+          .bind(WarnRecordDecl);
+  auto CastUnrelated = stmt(ExplicitCastUnrelated);
+  auto MatchesUnrelatedTypes = match(stmt(forEachDescendant(CastUnrelated)),
+                                     *D->getBody(), AM.getASTContext());
+  for (BoundNodes Match : MatchesUnrelatedTypes)
+    emitDiagnosticsUnrelated(Match, BR, ADC, this, BT);
+}
+
+void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<MemoryUnsafeCastChecker>();
+}
+
+bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &mgr) {
+  return true;
+}

diff  --git a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp
new file mode 100644
index 00000000000000..62c945c7a2c242
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp
@@ -0,0 +1,266 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.MemoryUnsafeCastChecker -verify %s
+
+class Base { };
+class Derived : public Base { };
+
+template<typename Target, typename Source>
+Target& downcast_ref(Source& source){
+  [[clang::suppress]]
+  return static_cast<Target&>(source);
+}
+
+template<typename Target, typename Source>
+Target* downcast_ptr(Source* source){
+  [[clang::suppress]]
+  return static_cast<Target*>(source);
+}
+
+void test_pointers(Base *base) {
+  Derived *derived_static = static_cast<Derived*>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived *derived_reinterpret = reinterpret_cast<Derived*>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived *derived_c = (Derived*)base;
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived *derived_d = downcast_ptr<Derived, Base>(base);  // no warning
+}
+
+void test_non_pointers(Derived derived) {
+  Base base_static = static_cast<Base>(derived);  // no warning
+}
+
+void test_refs(Base &base) {
+  Derived &derived_static = static_cast<Derived&>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived &derived_reinterpret = reinterpret_cast<Derived&>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived &derived_c = (Derived&)base;
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived &derived_d = downcast_ref<Derived, Base>(base);  // no warning
+}
+
+class BaseVirtual {
+  virtual void virtual_base_function();
+};
+
+class DerivedVirtual : public BaseVirtual {
+  void virtual_base_function() override { }
+};
+
+void test_dynamic_casts(BaseVirtual *base_ptr, BaseVirtual &base_ref) {
+  DerivedVirtual *derived_dynamic_ptr = dynamic_cast<DerivedVirtual*>(base_ptr);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseVirtual' to derived type 'DerivedVirtual'}}
+  DerivedVirtual &derived_dynamic_ref = dynamic_cast<DerivedVirtual&>(base_ref);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseVirtual' to derived type 'DerivedVirtual'}}
+}
+
+struct BaseStruct { };
+struct DerivedStruct : BaseStruct { };
+
+void test_struct_pointers(struct BaseStruct *base_struct) {
+  struct DerivedStruct *derived_static = static_cast<struct DerivedStruct*>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}}
+  struct DerivedStruct *derived_reinterpret = reinterpret_cast<struct DerivedStruct*>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}}
+  struct DerivedStruct *derived_c = (struct DerivedStruct*)base_struct;
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}}
+}
+
+typedef struct BaseStruct BStruct;
+typedef struct DerivedStruct DStruct;
+
+void test_struct_refs(BStruct &base_struct) {
+  DStruct &derived_static = static_cast<DStruct&>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}}
+  DStruct &derived_reinterpret = reinterpret_cast<DStruct&>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}}
+  DStruct &derived_c = (DStruct&)base_struct;
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}}
+}
+
+int counter = 0;
+void test_recursive(BStruct &base_struct) {
+  if (counter == 5)
+    return;
+  counter++;
+  DStruct &derived_static = static_cast<DStruct&>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}}
+}
+
+template<typename T>
+class BaseTemplate { };
+
+template<typename T>
+class DerivedTemplate : public BaseTemplate<T> { };
+
+void test_templates(BaseTemplate<int> *base, BaseTemplate<int> &base_ref) {
+  DerivedTemplate<int> *derived_static = static_cast<DerivedTemplate<int>*>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}}
+  DerivedTemplate<int> *derived_reinterpret = reinterpret_cast<DerivedTemplate<int>*>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}}
+  DerivedTemplate<int> *derived_c = (DerivedTemplate<int>*)base;
+  // expected-warning at -1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}}
+  DerivedTemplate<int> &derived_static_ref = static_cast<DerivedTemplate<int>&>(base_ref);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}}
+  DerivedTemplate<int> &derived_reinterpret_ref = reinterpret_cast<DerivedTemplate<int>&>(base_ref);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}}
+  DerivedTemplate<int> &derived_c_ref = (DerivedTemplate<int>&)base_ref;
+  // expected-warning at -1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}}
+}
+
+#define CAST_MACRO_STATIC(X,Y) (static_cast<Y>(X))
+#define CAST_MACRO_REINTERPRET(X,Y) (reinterpret_cast<Y>(X))
+#define CAST_MACRO_C(X,Y) ((Y)X)
+
+void test_macro_static(Base *base, Derived *derived, Base &base_ref) {
+  Derived *derived_static = CAST_MACRO_STATIC(base, Derived*);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived &derived_static_ref = CAST_MACRO_STATIC(base_ref, Derived&);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Base *base_static_same = CAST_MACRO_STATIC(base, Base*);  // no warning
+  Base *base_static_upcast = CAST_MACRO_STATIC(derived, Base*);  // no warning
+}
+
+void test_macro_reinterpret(Base *base, Derived *derived, Base &base_ref) {
+  Derived *derived_reinterpret = CAST_MACRO_REINTERPRET(base, Derived*);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived &derived_reinterpret_ref = CAST_MACRO_REINTERPRET(base_ref, Derived&);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Base *base_reinterpret_same = CAST_MACRO_REINTERPRET(base, Base*);  // no warning
+  Base *base_reinterpret_upcast = CAST_MACRO_REINTERPRET(derived, Base*);  // no warning
+}
+
+void test_macro_c(Base *base, Derived *derived, Base &base_ref) {
+  Derived *derived_c = CAST_MACRO_C(base, Derived*);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Derived &derived_c_ref = CAST_MACRO_C(base_ref, Derived&);
+  // expected-warning at -1{{Unsafe cast from base type 'Base' to derived type 'Derived'}}
+  Base *base_c_same = CAST_MACRO_C(base, Base*);  // no warning
+  Base *base_c_upcast = CAST_MACRO_C(derived, Base*);  // no warning
+}
+
+struct BaseStructCpp {
+  int t;
+  void increment() { t++; }
+};
+struct DerivedStructCpp : BaseStructCpp {
+  void increment_t() {increment();}
+};
+
+void test_struct_cpp_pointers(struct BaseStructCpp *base_struct) {
+  struct DerivedStructCpp *derived_static = static_cast<struct DerivedStructCpp*>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}}
+  struct DerivedStructCpp *derived_reinterpret = reinterpret_cast<struct DerivedStructCpp*>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}}
+  struct DerivedStructCpp *derived_c = (struct DerivedStructCpp*)base_struct;
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}}
+}
+
+typedef struct BaseStructCpp BStructCpp;
+typedef struct DerivedStructCpp DStructCpp;
+
+void test_struct_cpp_refs(BStructCpp &base_struct, DStructCpp &derived_struct) {
+  DStructCpp &derived_static = static_cast<DStructCpp&>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}}
+  DStructCpp &derived_reinterpret = reinterpret_cast<DStructCpp&>(base_struct);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}}
+  DStructCpp &derived_c = (DStructCpp&)base_struct;
+  // expected-warning at -1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}}
+  BStructCpp &base = (BStructCpp&)derived_struct; // no warning
+  BStructCpp &base_static = static_cast<BStructCpp&>(derived_struct); // no warning
+  BStructCpp &base_reinterpret = reinterpret_cast<BStructCpp&>(derived_struct); // no warning
+}
+
+struct stack_st { };
+
+#define STACK_OF(type) struct stack_st_##type
+
+void test_stack(stack_st *base) {
+  STACK_OF(void) *derived = (STACK_OF(void)*)base;
+  // expected-warning at -1{{Unsafe cast from type 'stack_st' to an unrelated type 'stack_st_void'}}
+}
+
+class Parent { };
+class Child1 : public Parent { };
+class Child2 : public Parent { };
+
+void test_common_parent(Child1 *c1, Child2 *c2) {
+  Child2 *c2_cstyle = (Child2 *)c1;
+  // expected-warning at -1{{Unsafe cast from type 'Child1' to an unrelated type 'Child2'}}
+  Child2 *c2_reinterpret = reinterpret_cast<Child2 *>(c1);
+  // expected-warning at -1{{Unsafe cast from type 'Child1' to an unrelated type 'Child2'}}
+}
+
+class Type1 { };
+class Type2 { };
+
+void test_unrelated_ref(Type1 &t1, Type2 &t2) {
+  Type2 &t2_cstyle = (Type2 &)t1;
+  // expected-warning at -1{{Unsafe cast from type 'Type1' to an unrelated type 'Type2'}}
+  Type2 &t2_reinterpret = reinterpret_cast<Type2 &>(t1);
+  // expected-warning at -1{{Unsafe cast from type 'Type1' to an unrelated type 'Type2'}}
+  Type2 &t2_same = reinterpret_cast<Type2 &>(t2); // no warning
+}
+
+
+class VirtualClass1 {
+  virtual void virtual_base_function();
+};
+
+class VirtualClass2 {
+  void virtual_base_function();
+};
+
+void test_unrelated_virtual(VirtualClass1 &v1) {
+  VirtualClass2 &v2 = dynamic_cast<VirtualClass2 &>(v1);
+  // expected-warning at -1{{Unsafe cast from type 'VirtualClass1' to an unrelated type 'VirtualClass2'}}
+}
+
+struct StructA { };
+struct StructB { };
+
+typedef struct StructA StA;
+typedef struct StructB StB;
+
+void test_struct_unrelated_refs(StA &a, StB &b) {
+  StB &b_reinterpret = reinterpret_cast<StB&>(a);
+  // expected-warning at -1{{Unsafe cast from type 'StructA' to an unrelated type 'StructB'}}
+  StB &b_c = (StB&)a;
+  // expected-warning at -1{{Unsafe cast from type 'StructA' to an unrelated type 'StructB'}}
+  StA &a_local = (StA&)b;
+  // expected-warning at -1{{Unsafe cast from type 'StructB' to an unrelated type 'StructA'}}
+  StA &a_reinterpret = reinterpret_cast<StA&>(b);
+  // expected-warning at -1{{Unsafe cast from type 'StructB' to an unrelated type 'StructA'}}
+  StA &a_same = (StA&)a; // no warning
+}
+
+template<typename T>
+class DeferrableRefCounted {
+public:
+  void deref() const {
+    auto this_to_T =  static_cast<const T*>(this); // no warning
+  }
+};
+
+class SomeArrayClass : public DeferrableRefCounted<SomeArrayClass> { };
+
+void test_this_to_template(SomeArrayClass *ptr) {
+  ptr->deref();
+};
+
+template<typename WeakPtrFactoryType>
+class CanMakeWeakPtrBase {
+public:
+  void initializeWeakPtrFactory() const {
+    auto &this_to_T = static_cast<const WeakPtrFactoryType&>(*this);
+  }
+};
+
+template<typename T>
+using CanMakeWeakPtr = CanMakeWeakPtrBase<T>;
+
+class EventLoop : public CanMakeWeakPtr<EventLoop> { };
+
+void test_this_to_template_ref(EventLoop *ptr) {
+  ptr->initializeWeakPtrFactory();
+};

diff  --git a/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm
new file mode 100644
index 00000000000000..f9046d79817849
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm
@@ -0,0 +1,64 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.MemoryUnsafeCastChecker -verify %s
+
+ at protocol NSObject
++alloc;
+-init;
+ at end
+
+ at interface NSObject <NSObject> {}
+ at end
+
+ at interface BaseClass : NSObject
+ at end
+
+ at interface DerivedClass : BaseClass
+-(void)testCasts:(BaseClass*)base;
+ at end
+
+ at implementation DerivedClass
+-(void)testCasts:(BaseClass*)base {
+  DerivedClass *derived = (DerivedClass*)base;
+  // expected-warning at -1{{Unsafe cast from base type 'BaseClass' to derived type 'DerivedClass'}}
+  DerivedClass *derived_static = static_cast<DerivedClass*>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseClass' to derived type 'DerivedClass'}}
+  DerivedClass *derived_reinterpret = reinterpret_cast<DerivedClass*>(base);
+  // expected-warning at -1{{Unsafe cast from base type 'BaseClass' to derived type 'DerivedClass'}}
+  base = (BaseClass*)derived;  // no warning
+  base = (BaseClass*)base;  // no warning
+}
+ at end
+
+template <typename T>
+class WrappedObject
+{
+public:
+  T get() const { return mMetalObject; }
+  T mMetalObject = nullptr;
+};
+
+ at protocol MTLCommandEncoder
+ at end
+ at protocol MTLRenderCommandEncoder
+ at end
+class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>> { };
+
+class RenderCommandEncoder final : public CommandEncoder
+{
+private:
+    id<MTLRenderCommandEncoder> get()
+    {
+        return static_cast<id<MTLRenderCommandEncoder>>(CommandEncoder::get());
+    }
+};
+
+ at interface Class1
+ at end
+
+ at interface Class2
+ at end
+
+void testUnrelated(Class1 *c1) {
+  Class2 *c2 = (Class2*)c1;
+  // expected-warning at -1{{Unsafe cast from type 'Class1' to an unrelated type 'Class2'}}
+  Class1 *c1_same = reinterpret_cast<Class1*>(c1); // no warning
+}


        


More information about the cfe-commits mailing list