Skip to content

[Webkit Checkers] Introduce a Webkit checker for memory unsafe casts #114606

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 5, 2024

Conversation

t-rasmud
Copy link
Contributor

@t-rasmud t-rasmud commented Nov 1, 2024

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

rdar://137766829

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Nov 1, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 1, 2024

@llvm/pr-subscribers-clang-static-analyzer-1

Author: Rashmi Mudduluru (t-rasmud)

Changes

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

rdar://137766829


Full diff: https://github.com/llvm/llvm-project/pull/114606.diff

7 Files Affected:

  • (modified) clang/docs/analyzer/checkers.rst (+25)
  • (modified) clang/docs/tools/clang-formatted-files.txt (+1)
  • (modified) clang/include/clang/StaticAnalyzer/Checkers/Checkers.td (+4)
  • (modified) clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt (+1)
  • (added) clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp (+86)
  • (added) clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp (+151)
  • (added) clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm (+29)
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 87b03438e6e0b9..f01755ce7a236a 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3452,6 +3452,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/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt
index 67ff085144f4de..74ab155d6174fd 100644
--- a/clang/docs/tools/clang-formatted-files.txt
+++ b/clang/docs/tools/clang-formatted-files.txt
@@ -537,6 +537,7 @@ clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
 clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
+clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
 clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 9a6b35c1b9f774..445379e88ab9e3 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1752,6 +1752,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 62aa5ff7f002a9..7e987740f9ee2d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -132,6 +132,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   VirtualCallChecker.cpp
   WebKit/RawPtrRefMemberChecker.cpp
   WebKit/ASTUtils.cpp
+  WebKit/MemoryUnsafeCastChecker.cpp
   WebKit/PtrTypesSemantics.cpp
   WebKit/RefCntblBaseVirtualDtorChecker.cpp
   WebKit/UncountedCallArgsChecker.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..05a5f89d28c8fe
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp
@@ -0,0 +1,86 @@
+//=======- 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/AST/ASTContext.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MemoryUnsafeCastChecker : public Checker<check::PreStmt<CastExpr>> {
+  BugType BT{this, ""};
+
+public:
+  void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
+};
+} // end namespace
+
+void emitWarning(CheckerContext &C, const CastExpr &CE, const BugType &BT,
+                 QualType FromType, QualType ToType) {
+  ExplodedNode *errorNode = C.generateNonFatalErrorNode();
+  if (!errorNode)
+    return;
+  SmallString<192> Buf;
+  llvm::raw_svector_ostream OS(Buf);
+  OS << "Memory unsafe cast from base type '";
+  QualType::print(FromType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
+                  llvm::Twine());
+  OS << "' to derived type '";
+  QualType::print(ToType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
+                  llvm::Twine());
+  OS << "'";
+  auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), errorNode);
+  R->addRange(CE.getSourceRange());
+  C.emitReport(std::move(R));
+}
+
+void MemoryUnsafeCastChecker::checkPreStmt(const CastExpr *CE,
+                                           CheckerContext &C) const {
+  auto ExpCast = dyn_cast_or_null<ExplicitCastExpr>(CE);
+  if (!ExpCast)
+    return;
+
+  auto ToDerivedQualType = ExpCast->getTypeAsWritten();
+  auto *SE = CE->getSubExprAsWritten();
+  if (ToDerivedQualType->isObjCObjectPointerType()) {
+    auto FromBaseQualType = SE->getType();
+    bool IsObjCSubType =
+        !C.getASTContext().hasSameType(ToDerivedQualType, FromBaseQualType) &&
+        C.getASTContext().canAssignObjCInterfaces(
+            FromBaseQualType->getAsObjCInterfacePointerType(),
+            ToDerivedQualType->getAsObjCInterfacePointerType());
+    if (IsObjCSubType)
+      emitWarning(C, *CE, BT, FromBaseQualType, ToDerivedQualType);
+    return;
+  }
+  auto ToDerivedType = ToDerivedQualType->getPointeeCXXRecordDecl();
+  auto FromBaseType = SE->getType()->getPointeeCXXRecordDecl();
+  if (!FromBaseType)
+    FromBaseType = SE->getType()->getAsCXXRecordDecl();
+  if (!FromBaseType)
+    return;
+  if (ToDerivedType->isDerivedFrom(FromBaseType))
+    emitWarning(C, *CE, BT, SE->getType(), ToDerivedQualType);
+}
+
+void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<MemoryUnsafeCastChecker>();
+}
+
+bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &) {
+  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..1a4ef6858d2180
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp
@@ -0,0 +1,151 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.MemoryUnsafeCastChecker -verify %s
+
+class Base { };
+class Derived : public Base { };
+
+void test_pointers(Base *base) {
+  Derived *derived_static = static_cast<Derived*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived *derived_reinterpret = reinterpret_cast<Derived*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived *derived_c = (Derived*)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+}
+
+void test_refs(Base &base) {
+  Derived &derived_static = static_cast<Derived&>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &'}}
+  Derived &derived_reinterpret = reinterpret_cast<Derived&>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &'}}
+  Derived &derived_c = (Derived&)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &'}}
+}
+
+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@-1{{Memory unsafe cast from base type 'BaseVirtual *' to derived type 'DerivedVirtual *'}}
+  DerivedVirtual &derived_dynamic_ref = dynamic_cast<DerivedVirtual&>(base_ref);
+  // expected-warning@-1{{Memory 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@-1{{Memory unsafe cast from base type 'struct BaseStruct *' to derived type 'struct DerivedStruct *'}}
+  struct DerivedStruct *derived_reinterpret = reinterpret_cast<struct DerivedStruct*>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStruct *' to derived type 'struct DerivedStruct *'}}
+  struct DerivedStruct *derived_c = (struct DerivedStruct*)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStruct *' to derived type 'struct 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@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+  DStruct &derived_reinterpret = reinterpret_cast<DStruct&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+  DStruct &derived_c = (DStruct&)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+}
+
+int counter = 0;
+void test_recursive(BStruct &base_struct) {
+  if (counter == 5)
+    return;
+  counter++;
+  DStruct &derived_static = static_cast<DStruct&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+}
+
+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@-1{{Memory unsafe cast from base type 'BaseTemplate<int> *' to derived type 'DerivedTemplate<int> *'}}
+  DerivedTemplate<int> *derived_reinterpret = reinterpret_cast<DerivedTemplate<int>*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int> *' to derived type 'DerivedTemplate<int> *'}}
+  DerivedTemplate<int> *derived_c = (DerivedTemplate<int>*)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int> *' to derived type 'DerivedTemplate<int> *'}}
+  DerivedTemplate<int> &derived_static_ref = static_cast<DerivedTemplate<int>&>(base_ref);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int>' to derived type 'DerivedTemplate<int> &'}}
+  DerivedTemplate<int> &derived_reinterpret_ref = reinterpret_cast<DerivedTemplate<int>&>(base_ref);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int>' to derived type 'DerivedTemplate<int> &'}}
+  DerivedTemplate<int> &derived_c_ref = (DerivedTemplate<int>&)base_ref;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int>' to derived type 'DerivedTemplate<int> &'}}
+}
+
+#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@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived &derived_static_ref = CAST_MACRO_STATIC(base_ref, Derived&);
+  // expected-warning@-1{{Memory 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@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived &derived_reinterpret_ref = CAST_MACRO_REINTERPRET(base_ref, Derived&);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &' [alpha.webkit.MemoryUnsafeCastChecker]}}
+  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@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *' [alpha.webkit.MemoryUnsafeCastChecker]}}
+  Derived &derived_c_ref = CAST_MACRO_C(base_ref, Derived&);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &' [alpha.webkit.MemoryUnsafeCastChecker]}}
+  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@-1{{Memory unsafe cast from base type 'struct BaseStructCpp *' to derived type 'struct DerivedStructCpp *'}}
+  struct DerivedStructCpp *derived_reinterpret = reinterpret_cast<struct DerivedStructCpp*>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStructCpp *' to derived type 'struct DerivedStructCpp *'}}
+  struct DerivedStructCpp *derived_c = (struct DerivedStructCpp*)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStructCpp *' to derived type 'struct DerivedStructCpp *'}}
+}
+
+typedef struct BaseStructCpp BStructCpp;
+typedef struct DerivedStructCpp DStructCpp;
+
+void test_struct_cpp_refs(BStructCpp &base_struct) {
+  DStructCpp &derived_static = static_cast<DStructCpp&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStructCpp' to derived type 'DStructCpp &'}}
+  DStructCpp &derived_reinterpret = reinterpret_cast<DStructCpp&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStructCpp' to derived type 'DStructCpp &'}}
+  DStructCpp &derived_c = (DStructCpp&)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStructCpp' to derived type 'DStructCpp &'}}
+}
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..1ea2f4aa472715
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm
@@ -0,0 +1,29 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.MemoryUnsafeCastChecker -verify %s
+
+@protocol NSObject
++alloc;
+-init;
+@end
+
+@interface NSObject <NSObject> {}
+@end
+
+@interface BaseClass : NSObject
+@end
+
+@interface DerivedClass : BaseClass
+-(void)testCasts:(BaseClass*)base;
+@end
+
+@implementation DerivedClass
+-(void)testCasts:(BaseClass*)base {
+  DerivedClass *derived = (DerivedClass*)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseClass *' to derived type 'DerivedClass *'}}
+  DerivedClass *derived_static = static_cast<DerivedClass*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseClass *' to derived type 'DerivedClass *'}}
+  DerivedClass *derived_reinterpret = reinterpret_cast<DerivedClass*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseClass *' to derived type 'DerivedClass *'}}
+  base = (BaseClass*)derived;  // no warning
+  base = (BaseClass*)base;  // no warning
+}
+@end

@llvmbot
Copy link
Member

llvmbot commented Nov 1, 2024

@llvm/pr-subscribers-clang

Author: Rashmi Mudduluru (t-rasmud)

Changes

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

rdar://137766829


Full diff: https://github.com/llvm/llvm-project/pull/114606.diff

7 Files Affected:

  • (modified) clang/docs/analyzer/checkers.rst (+25)
  • (modified) clang/docs/tools/clang-formatted-files.txt (+1)
  • (modified) clang/include/clang/StaticAnalyzer/Checkers/Checkers.td (+4)
  • (modified) clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt (+1)
  • (added) clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp (+86)
  • (added) clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp (+151)
  • (added) clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm (+29)
diff --git a/clang/docs/analyzer/checkers.rst b/clang/docs/analyzer/checkers.rst
index 87b03438e6e0b9..f01755ce7a236a 100644
--- a/clang/docs/analyzer/checkers.rst
+++ b/clang/docs/analyzer/checkers.rst
@@ -3452,6 +3452,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/docs/tools/clang-formatted-files.txt b/clang/docs/tools/clang-formatted-files.txt
index 67ff085144f4de..74ab155d6174fd 100644
--- a/clang/docs/tools/clang-formatted-files.txt
+++ b/clang/docs/tools/clang-formatted-files.txt
@@ -537,6 +537,7 @@ clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
 clang/lib/StaticAnalyzer/Checkers/WebKit/DiagOutputUtils.h
+clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.cpp
 clang/lib/StaticAnalyzer/Checkers/WebKit/PtrTypesSemantics.h
 clang/lib/StaticAnalyzer/Checkers/WebKit/RefCntblBaseVirtualDtorChecker.cpp
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
index 9a6b35c1b9f774..445379e88ab9e3 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
+++ b/clang/include/clang/StaticAnalyzer/Checkers/Checkers.td
@@ -1752,6 +1752,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 62aa5ff7f002a9..7e987740f9ee2d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
+++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt
@@ -132,6 +132,7 @@ add_clang_library(clangStaticAnalyzerCheckers
   VirtualCallChecker.cpp
   WebKit/RawPtrRefMemberChecker.cpp
   WebKit/ASTUtils.cpp
+  WebKit/MemoryUnsafeCastChecker.cpp
   WebKit/PtrTypesSemantics.cpp
   WebKit/RefCntblBaseVirtualDtorChecker.cpp
   WebKit/UncountedCallArgsChecker.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..05a5f89d28c8fe
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/MemoryUnsafeCastChecker.cpp
@@ -0,0 +1,86 @@
+//=======- 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/AST/ASTContext.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MemoryUnsafeCastChecker : public Checker<check::PreStmt<CastExpr>> {
+  BugType BT{this, ""};
+
+public:
+  void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
+};
+} // end namespace
+
+void emitWarning(CheckerContext &C, const CastExpr &CE, const BugType &BT,
+                 QualType FromType, QualType ToType) {
+  ExplodedNode *errorNode = C.generateNonFatalErrorNode();
+  if (!errorNode)
+    return;
+  SmallString<192> Buf;
+  llvm::raw_svector_ostream OS(Buf);
+  OS << "Memory unsafe cast from base type '";
+  QualType::print(FromType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
+                  llvm::Twine());
+  OS << "' to derived type '";
+  QualType::print(ToType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
+                  llvm::Twine());
+  OS << "'";
+  auto R = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), errorNode);
+  R->addRange(CE.getSourceRange());
+  C.emitReport(std::move(R));
+}
+
+void MemoryUnsafeCastChecker::checkPreStmt(const CastExpr *CE,
+                                           CheckerContext &C) const {
+  auto ExpCast = dyn_cast_or_null<ExplicitCastExpr>(CE);
+  if (!ExpCast)
+    return;
+
+  auto ToDerivedQualType = ExpCast->getTypeAsWritten();
+  auto *SE = CE->getSubExprAsWritten();
+  if (ToDerivedQualType->isObjCObjectPointerType()) {
+    auto FromBaseQualType = SE->getType();
+    bool IsObjCSubType =
+        !C.getASTContext().hasSameType(ToDerivedQualType, FromBaseQualType) &&
+        C.getASTContext().canAssignObjCInterfaces(
+            FromBaseQualType->getAsObjCInterfacePointerType(),
+            ToDerivedQualType->getAsObjCInterfacePointerType());
+    if (IsObjCSubType)
+      emitWarning(C, *CE, BT, FromBaseQualType, ToDerivedQualType);
+    return;
+  }
+  auto ToDerivedType = ToDerivedQualType->getPointeeCXXRecordDecl();
+  auto FromBaseType = SE->getType()->getPointeeCXXRecordDecl();
+  if (!FromBaseType)
+    FromBaseType = SE->getType()->getAsCXXRecordDecl();
+  if (!FromBaseType)
+    return;
+  if (ToDerivedType->isDerivedFrom(FromBaseType))
+    emitWarning(C, *CE, BT, SE->getType(), ToDerivedQualType);
+}
+
+void ento::registerMemoryUnsafeCastChecker(CheckerManager &Mgr) {
+  Mgr.registerChecker<MemoryUnsafeCastChecker>();
+}
+
+bool ento::shouldRegisterMemoryUnsafeCastChecker(const CheckerManager &) {
+  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..1a4ef6858d2180
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp
@@ -0,0 +1,151 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.MemoryUnsafeCastChecker -verify %s
+
+class Base { };
+class Derived : public Base { };
+
+void test_pointers(Base *base) {
+  Derived *derived_static = static_cast<Derived*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived *derived_reinterpret = reinterpret_cast<Derived*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived *derived_c = (Derived*)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+}
+
+void test_refs(Base &base) {
+  Derived &derived_static = static_cast<Derived&>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &'}}
+  Derived &derived_reinterpret = reinterpret_cast<Derived&>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &'}}
+  Derived &derived_c = (Derived&)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &'}}
+}
+
+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@-1{{Memory unsafe cast from base type 'BaseVirtual *' to derived type 'DerivedVirtual *'}}
+  DerivedVirtual &derived_dynamic_ref = dynamic_cast<DerivedVirtual&>(base_ref);
+  // expected-warning@-1{{Memory 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@-1{{Memory unsafe cast from base type 'struct BaseStruct *' to derived type 'struct DerivedStruct *'}}
+  struct DerivedStruct *derived_reinterpret = reinterpret_cast<struct DerivedStruct*>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStruct *' to derived type 'struct DerivedStruct *'}}
+  struct DerivedStruct *derived_c = (struct DerivedStruct*)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStruct *' to derived type 'struct 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@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+  DStruct &derived_reinterpret = reinterpret_cast<DStruct&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+  DStruct &derived_c = (DStruct&)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+}
+
+int counter = 0;
+void test_recursive(BStruct &base_struct) {
+  if (counter == 5)
+    return;
+  counter++;
+  DStruct &derived_static = static_cast<DStruct&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStruct' to derived type 'DStruct &'}}
+}
+
+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@-1{{Memory unsafe cast from base type 'BaseTemplate<int> *' to derived type 'DerivedTemplate<int> *'}}
+  DerivedTemplate<int> *derived_reinterpret = reinterpret_cast<DerivedTemplate<int>*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int> *' to derived type 'DerivedTemplate<int> *'}}
+  DerivedTemplate<int> *derived_c = (DerivedTemplate<int>*)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int> *' to derived type 'DerivedTemplate<int> *'}}
+  DerivedTemplate<int> &derived_static_ref = static_cast<DerivedTemplate<int>&>(base_ref);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int>' to derived type 'DerivedTemplate<int> &'}}
+  DerivedTemplate<int> &derived_reinterpret_ref = reinterpret_cast<DerivedTemplate<int>&>(base_ref);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int>' to derived type 'DerivedTemplate<int> &'}}
+  DerivedTemplate<int> &derived_c_ref = (DerivedTemplate<int>&)base_ref;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseTemplate<int>' to derived type 'DerivedTemplate<int> &'}}
+}
+
+#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@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived &derived_static_ref = CAST_MACRO_STATIC(base_ref, Derived&);
+  // expected-warning@-1{{Memory 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@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *'}}
+  Derived &derived_reinterpret_ref = CAST_MACRO_REINTERPRET(base_ref, Derived&);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &' [alpha.webkit.MemoryUnsafeCastChecker]}}
+  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@-1{{Memory unsafe cast from base type 'Base *' to derived type 'Derived *' [alpha.webkit.MemoryUnsafeCastChecker]}}
+  Derived &derived_c_ref = CAST_MACRO_C(base_ref, Derived&);
+  // expected-warning@-1{{Memory unsafe cast from base type 'Base' to derived type 'Derived &' [alpha.webkit.MemoryUnsafeCastChecker]}}
+  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@-1{{Memory unsafe cast from base type 'struct BaseStructCpp *' to derived type 'struct DerivedStructCpp *'}}
+  struct DerivedStructCpp *derived_reinterpret = reinterpret_cast<struct DerivedStructCpp*>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStructCpp *' to derived type 'struct DerivedStructCpp *'}}
+  struct DerivedStructCpp *derived_c = (struct DerivedStructCpp*)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'struct BaseStructCpp *' to derived type 'struct DerivedStructCpp *'}}
+}
+
+typedef struct BaseStructCpp BStructCpp;
+typedef struct DerivedStructCpp DStructCpp;
+
+void test_struct_cpp_refs(BStructCpp &base_struct) {
+  DStructCpp &derived_static = static_cast<DStructCpp&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStructCpp' to derived type 'DStructCpp &'}}
+  DStructCpp &derived_reinterpret = reinterpret_cast<DStructCpp&>(base_struct);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStructCpp' to derived type 'DStructCpp &'}}
+  DStructCpp &derived_c = (DStructCpp&)base_struct;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BStructCpp' to derived type 'DStructCpp &'}}
+}
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..1ea2f4aa472715
--- /dev/null
+++ b/clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm
@@ -0,0 +1,29 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=alpha.webkit.MemoryUnsafeCastChecker -verify %s
+
+@protocol NSObject
++alloc;
+-init;
+@end
+
+@interface NSObject <NSObject> {}
+@end
+
+@interface BaseClass : NSObject
+@end
+
+@interface DerivedClass : BaseClass
+-(void)testCasts:(BaseClass*)base;
+@end
+
+@implementation DerivedClass
+-(void)testCasts:(BaseClass*)base {
+  DerivedClass *derived = (DerivedClass*)base;
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseClass *' to derived type 'DerivedClass *'}}
+  DerivedClass *derived_static = static_cast<DerivedClass*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseClass *' to derived type 'DerivedClass *'}}
+  DerivedClass *derived_reinterpret = reinterpret_cast<DerivedClass*>(base);
+  // expected-warning@-1{{Memory unsafe cast from base type 'BaseClass *' to derived type 'DerivedClass *'}}
+  base = (BaseClass*)derived;  // no warning
+  base = (BaseClass*)base;  // no warning
+}
+@end

@t-rasmud t-rasmud requested a review from rniwa November 1, 2024 21:31
Copy link
Collaborator

@haoNoQ haoNoQ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice nice nice!!

@rniwa
Copy link
Contributor

rniwa commented Nov 7, 2024

I'm hitting this crash in the checker when I try to compile WebKit with this patch applied:

Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  clang-17                 0x000000010fb6c15d llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 61
1  clang-17                 0x000000010fb6c70b PrintStackTraceSignalHandler(void*) + 27
2  clang-17                 0x000000010fb6a4b6 llvm::sys::RunSignalHandlers() + 134
3  clang-17                 0x000000010fb6b9de llvm::sys::CleanupOnSignal(unsigned long) + 110
4  clang-17                 0x000000010fa1d717 (anonymous namespace)::CrashRecoveryContextImpl::HandleCrash(int, unsigned long) + 183
5  clang-17                 0x000000010fa1dabb CrashRecoverySignalHandler(int) + 187
6  libsystem_platform.dylib 0x00007ff802bc637d _sigtramp + 29
7  libsystem_platform.dylib 0x00007ff7b4ce10c8 _sigtramp + 18446744072402087272
8  clang-17                 0x00000001163f26f1 clang::CXXRecordDecl::isDerivedFrom(clang::CXXRecordDecl const*, clang::CXXBasePaths&) const + 33
9  clang-17                 0x00000001163f2681 clang::CXXRecordDecl::isDerivedFrom(clang::CXXRecordDecl const*) const + 97
10 clang-17                 0x0000000113a3768e (anonymous namespace)::WalkAST::VisitCastExpr(clang::CastExpr*) + 526
11 clang-17                 0x0000000113a3746d clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::VisitExplicitCastExpr(clang::ExplicitCastExpr*) + 29
12 clang-17                 0x0000000113a361ed clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::VisitCStyleCastExpr(clang::CStyleCastExpr*) + 29
13 clang-17                 0x0000000113a3328a clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::Visit(clang::Stmt*) + 3770
14 clang-17                 0x0000000113a372b2 (anonymous namespace)::WalkAST::VisitChildren(clang::Stmt*) + 146
15 clang-17                 0x0000000113a3720d (anonymous namespace)::WalkAST::VisitStmt(clang::Stmt*) + 29
16 clang-17                 0x0000000113a3477d clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::VisitCompoundStmt(clang::CompoundStmt*) + 29
17 clang-17                 0x0000000113a328a0 clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::Visit(clang::Stmt*) + 1232
18 clang-17                 0x0000000113a3237f (anonymous namespace)::MemoryUnsafeCastChecker::checkASTCodeBody(clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) const + 95
19 clang-17                 0x0000000113a3230d void clang::ento::check::ASTCodeBody::_checkBody<(anonymous namespace)::MemoryUnsafeCastChecker>(void*, clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) + 45
20 clang-17                 0x0000000113df2901 clang::ento::CheckerFn<void (clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&)>::operator()(clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) const + 49
21 clang-17                 0x0000000113df29e7 clang::ento::CheckerManager::runCheckersOnASTBody(clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) + 215
22 clang-17                 0x00000001131f63fc (anonymous namespace)::AnalysisConsumer::HandleCode(clang::Decl*, unsigned int, clang::ento::ExprEngine::InliningModes, llvm::DenseSet<clang::Decl const*, llvm::DenseMapInfo<clang::Decl const*, void>>*) + 540
23 clang-17                 0x00000001131fc597 (anonymous namespace)::AnalysisConsumer::VisitFunctionDecl(clang::FunctionDecl*) + 311
24 clang-17                 0x00000001131fbced clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::WalkUpFromFunctionDecl(clang::FunctionDecl*) + 93
25 clang-17                 0x00000001131829cf clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseFunctionDecl(clang::FunctionDecl*) + 79
26 clang-17                 0x00000001131797e6 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseDecl(clang::Decl*) + 2534
27 clang-17                 0x00000001131e0b18 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseDeclContextHelper(clang::DeclContext*) + 200
28 clang-17                 0x000000011317c495 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseLinkageSpecDecl(clang::LinkageSpecDecl*) + 165
29 clang-17                 0x0000000113179103 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseDecl(clang::Decl*) + 771
30 clang-17                 0x0000000113178c0b (anonymous namespace)::AnalysisConsumer::runAnalysisOnTranslationUnit(clang::ASTContext&) + 443
31 clang-17                 0x00000001131736fe (anonymous namespace)::AnalysisConsumer::HandleTranslationUnit(clang::ASTContext&) + 446
32 clang-17                 0x00000001141e1736 clang::ParseAST(clang::Sema&, bool, bool) + 870
33 clang-17                 0x000000011131e8b1 clang::ASTFrontendAction::ExecuteAction() + 305
34 clang-17                 0x000000011131df8c clang::FrontendAction::Execute() + 124
35 clang-17                 0x00000001112026df clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) + 975
36 clang-17                 0x000000011144b193 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) + 899
37 clang-17                 0x000000010b234dca cc1_main(llvm::ArrayRef<char const*>, char const*, void*) + 1802
38 clang-17                 0x000000010b2206b4 ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&, llvm::ToolContext const&) + 788
39 clang-17                 0x000000010b22ce6d clang_main(int, char**, llvm::ToolContext const&)::$_0::operator()(llvm::SmallVectorImpl<char const*>&) const + 29
40 clang-17                 0x000000010b22ce3d int llvm::function_ref<int (llvm::SmallVectorImpl<char const*>&)>::callback_fn<clang_main(int, char**, llvm::ToolContext const&)::$_0>(long, llvm::SmallVectorImpl<char const*>&) + 29
41 clang-17                 0x0000000110f2a671 llvm::function_ref<int (llvm::SmallVectorImpl<char const*>&)>::operator()(llvm::SmallVectorImpl<char const*>&) const + 33
42 clang-17                 0x0000000110f2a638 clang::driver::CC1Command::Execute(llvm::ArrayRef<std::__1::optional<llvm::StringRef>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*, bool*) const::$_1::operator()() const + 40
43 clang-17                 0x0000000110f2a605 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<std::__1::optional<llvm::StringRef>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*, bool*) const::$_1>(long) + 21
44 clang-17                 0x000000010fa1d5b9 llvm::function_ref<void ()>::operator()() const + 25
45 clang-17                 0x000000010fa1d55c llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) + 236
46 clang-17                 0x0000000110f2681c clang::driver::CC1Command::Execute(llvm::ArrayRef<std::__1::optional<llvm::StringRef>>, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>*, bool*) const + 508
47 clang-17                 0x0000000110eb9d7f clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&, bool) const + 799
48 clang-17                 0x0000000110eba027 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::__1::pair<int, clang::driver::Command const*>>&, bool) const + 167
49 clang-17                 0x0000000110eda248 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::__1::pair<int, clang::driver::Command const*>>&) + 568
50 clang-17                 0x000000010b21fbef clang_main(int, char**, llvm::ToolContext const&) + 4511
51 clang-17                 0x000000010b27fa9d main + 61

I think we need an early return when ToDerivedType is nullptr like this:

  auto ToDerivedType = ToDerivedQualType->getPointeeCXXRecordDecl();
  if (!ToDerivedType)
    return;

@rniwa
Copy link
Contributor

rniwa commented Nov 7, 2024

Hm... I'm still hitting a crash. In debug builds, we hit this assertion:

Assertion failed: (DD && "queried property of class with no definition"), function data, file DeclCXX.h, line 452.
PLEASE submit a bug report to https://github.com/llvm/llvm-project/issues/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.	Program arguments: /Volumes/Data/llvm-project/build-debug/bin/clang-17 -x c++ -target x86_64-apple-macos14.0 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -std=c++2b -stdlib=libc++ -Wno-trigraphs -fno-exceptions -fno-rtti -fno-sanitize=vptr -fpascal-strings -O0 -fno-common -Werror -Wno-missing-field-initializers -Wno-missing-prototypes -Wno-non-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wno-unused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wno-shorten-64-to-32 -Wnewline-eof -Wno-c++11-extensions -Wno-implicit-fallthrough -DCLANG_WEBKIT_BRANCH=1 -DOPENSSL_NO_ASM -DABSL_ALLOCATOR_NOTHROW -DWEBRTC_WEBKIT_BUILD -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.0.sdk -fasm-blocks -fstrict-aliasing -Wdeprecated-declarations -Winvalid-offsetof -g -fvisibility=hidden -fvisibility-inlines-hidden -Wno-sign-conversion -Winfinite-recursion -Wmove -Wno-comma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wno-range-loop-analysis -Wno-semicolon-before-method-body -D__clang_analyzer__ -Xclang -analyzer-output=html -Xclang -analyzer-config -Xclang report-in-main-source-file=true -Xclang -analyzer-config -Xclang nullability:NoDiagnoseCallsToSystemHeaders=true -Xclang -analyzer-checker -Xclang optin.osx.cocoa.localizability.NonLocalizedStringChecker -Xclang -analyzer-checker -Xclang security.insecureAPI.UncheckedReturn -Xclang -analyzer-checker -Xclang security.insecureAPI.getpw -Xclang -analyzer-checker -Xclang security.insecureAPI.gets -Xclang -analyzer-checker -Xclang security.insecureAPI.mkstemp -Xclang -analyzer-checker -Xclang security.insecureAPI.mktemp -Xclang -analyzer-disable-checker -Xclang security.insecureAPI.rand -Xclang -analyzer-disable-checker -Xclang security.insecureAPI.strcpy -Xclang -analyzer-checker -Xclang security.insecureAPI.vfork -Xclang -analyzer-disable-checker -Xclang alpha,apiModeling,core,cplusplus,deadcode,debug,fuchsia,nullability,optin,osx,security,unix,webkit -Xclang -analyzer-checker -Xclang alpha.webkit.MemoryUnsafeCastChecker,alpha.webkit.NoUncheckedPtrMemberChecker,alpha.webkit.UncountedCallArgsChecker,alpha.webkit.UncountedLocalVarsChecker,webkit.NoUncountedMemberChecker,webkit.RefCntblBaseVirtualDtor -Xclang -analyzer-config -Xclang max-nodes=10000000 -Xclang -analyzer-config -Xclang verbose-report-filename=true -I/Volumes/Data/safari-4/OpenSource/WebKitBuild/Debug/include -ISource/third_party/boringssl/src/include -I/Volumes/Data/safari-4/OpenSource/WebKitBuild/libwebrtc.build/Debug/boringssl.build/DerivedSources-normal/x86_64 -I/Volumes/Data/safari-4/OpenSource/WebKitBuild/libwebrtc.build/Debug/boringssl.build/DerivedSources/x86_64 -I/Volumes/Data/safari-4/OpenSource/WebKitBuild/libwebrtc.build/Debug/boringssl.build/DerivedSources -Wall -Wc99-designator -Wconditional-uninitialized -Wextra -Wdeprecated-enum-enum-conversion -Wdeprecated-enum-float-conversion -Wenum-float-conversion -Wfinal-dtor-non-final-class -Wformat=2 -Wmisleading-indentation -Wreorder-init-list -Wundef -Wvla -Wno-elaborated-enum-base -Wthread-safety -Wno-conditional-uninitialized -Wno-missing-field-initializers -Wno-sign-compare -Wno-undef -Wno-unknown-warning-option -Wno-unused-but-set-parameter -Wno-unused-parameter -Wno-array-parameter -Wno-unused-but-set-variable -Wno-thread-safety-reference-return -Wno-vla -Wexit-time-destructors -Wglobal-constructors -F/Volumes/Data/safari-4/OpenSource/WebKitBuild/Debug -fvisibility=default -D_LIBCPP_ENABLE_ASSERTIONS=1 -isystem /Volumes/Data/safari-4/OpenSource/WebKitLibraries/SDKs/macosx14.0-additions.sdk/usr/local/include -MMD -MT dependencies -MF /Volumes/Data/safari-4/analyzer-output/StaticAnalyzer/libwebrtc/boringssl/normal/x86_64/tls_record.d --analyze /Volumes/Data/safari-4/OpenSource/Source/ThirdParty/libwebrtc/Source/third_party/boringssl/src/ssl/tls_record.cc -o /Volumes/Data/safari-4/analyzer-output/StaticAnalyzer/libwebrtc/boringssl/normal/x86_64/tls_record.plist
1.	<eof> parser at end of file
Stack dump without symbol names (ensure you have llvm-symbolizer in your PATH or set the environment var `LLVM_SYMBOLIZER_PATH` to point to it):
0  clang-17                 0x00000001139060dd llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) + 61
1  clang-17                 0x000000011390668b PrintStackTraceSignalHandler(void*) + 27
2  clang-17                 0x0000000113904436 llvm::sys::RunSignalHandlers() + 134
3  clang-17                 0x000000011390595e llvm::sys::CleanupOnSignal(unsigned long) + 110
4  clang-17                 0x00000001137b7697 (anonymous namespace)::CrashRecoveryContextImpl::HandleCrash(int, unsigned long) + 183
5  clang-17                 0x00000001137b7a3b CrashRecoverySignalHandler(int) + 187
6  libsystem_platform.dylib 0x00007ff802bc637d _sigtramp + 29
7  libsystem_platform.dylib 0x0000000000000021 _sigtramp + 18446603370535034049
8  libsystem_c.dylib        0x00007ff802ab7a4d abort + 126
9  libsystem_c.dylib        0x00007ff802ab6d60 err + 0
10 clang-17                 0x0000000114b64b24 clang::CXXRecordDecl::data() const + 100
11 clang-17                 0x0000000119e1b4b5 clang::CXXRecordDecl::bases_begin() const + 21
12 clang-17                 0x0000000119d9ae29 clang::CXXRecordDecl::bases() const + 25
13 clang-17                 0x000000011a18ceda clang::CXXBasePaths::lookupInBases(clang::ASTContext&, clang::CXXRecordDecl const*, llvm::function_ref<bool (clang::CXXBaseSpecifier const*, clang::CXXBasePath&)>, bool) + 106
14 clang-17                 0x000000011a18c7b6 clang::CXXRecordDecl::lookupInBases(llvm::function_ref<bool (clang::CXXBaseSpecifier const*, clang::CXXBasePath&)>, clang::CXXBasePaths&, bool) const + 102
15 clang-17                 0x000000011a18c735 clang::CXXRecordDecl::isDerivedFrom(clang::CXXRecordDecl const*, clang::CXXBasePaths&) const + 149
16 clang-17                 0x000000011a18c651 clang::CXXRecordDecl::isDerivedFrom(clang::CXXRecordDecl const*) const + 97
17 clang-17                 0x00000001177d165f (anonymous namespace)::WalkAST::VisitCastExpr(clang::CastExpr*) + 607
18 clang-17                 0x00000001177d13ed clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::VisitExplicitCastExpr(clang::ExplicitCastExpr*) + 29
19 clang-17                 0x00000001177d016d clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::VisitCStyleCastExpr(clang::CStyleCastExpr*) + 29
20 clang-17                 0x00000001177cd20a clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::Visit(clang::Stmt*) + 3770
21 clang-17                 0x00000001177d1232 (anonymous namespace)::WalkAST::VisitChildren(clang::Stmt*) + 146
22 clang-17                 0x00000001177d118d (anonymous namespace)::WalkAST::VisitStmt(clang::Stmt*) + 29
23 clang-17                 0x00000001177cf80d clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::VisitReturnStmt(clang::ReturnStmt*) + 29
24 clang-17                 0x00000001177cce86 clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::Visit(clang::Stmt*) + 2870
25 clang-17                 0x00000001177d1232 (anonymous namespace)::WalkAST::VisitChildren(clang::Stmt*) + 146
26 clang-17                 0x00000001177d118d (anonymous namespace)::WalkAST::VisitStmt(clang::Stmt*) + 29
27 clang-17                 0x00000001177ce6fd clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::VisitCompoundStmt(clang::CompoundStmt*) + 29
28 clang-17                 0x00000001177cc820 clang::StmtVisitorBase<std::__1::add_pointer, (anonymous namespace)::WalkAST, void>::Visit(clang::Stmt*) + 1232
29 clang-17                 0x00000001177cc2ff (anonymous namespace)::MemoryUnsafeCastChecker::checkASTCodeBody(clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) const + 95
30 clang-17                 0x00000001177cc28d void clang::ento::check::ASTCodeBody::_checkBody<(anonymous namespace)::MemoryUnsafeCastChecker>(void*, clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) + 45
31 clang-17                 0x0000000117b8c8d1 clang::ento::CheckerFn<void (clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&)>::operator()(clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) const + 49
32 clang-17                 0x0000000117b8c9b7 clang::ento::CheckerManager::runCheckersOnASTBody(clang::Decl const*, clang::ento::AnalysisManager&, clang::ento::BugReporter&) + 215
33 clang-17                 0x0000000116f9037c (anonymous namespace)::AnalysisConsumer::HandleCode(clang::Decl*, unsigned int, clang::ento::ExprEngine::InliningModes, llvm::DenseSet<clang::Decl const*, llvm::DenseMapInfo<clang::Decl const*, void>>*) + 540
34 clang-17                 0x0000000116f96517 (anonymous namespace)::AnalysisConsumer::VisitFunctionDecl(clang::FunctionDecl*) + 311
35 clang-17                 0x0000000116f95c6d clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::WalkUpFromFunctionDecl(clang::FunctionDecl*) + 93
36 clang-17                 0x0000000116f1c94f clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseFunctionDecl(clang::FunctionDecl*) + 79
37 clang-17                 0x0000000116f13766 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseDecl(clang::Decl*) + 2534
38 clang-17                 0x0000000116f7aa98 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseDeclContextHelper(clang::DeclContext*) + 200
39 clang-17                 0x0000000116f16415 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseLinkageSpecDecl(clang::LinkageSpecDecl*) + 165
40 clang-17                 0x0000000116f13083 clang::RecursiveASTVisitor<(anonymous namespace)::AnalysisConsumer>::TraverseDecl(clang::Decl*) + 771
41 clang-17                 0x0000000116f12b8b (anonymous namespace)::AnalysisConsumer::runAnalysisOnTranslationUnit(clang::ASTContext&) + 443
42 clang-17                 0x0000000116f0d67e (anonymous namespace)::AnalysisConsumer::HandleTranslationUnit(clang::ASTContext&) + 446
43 clang-17                 0x0000000117f7b706 clang::ParseAST(clang::Sema&, bool, bool) + 870
44 clang-17                 0x00000001150b8831 clang::ASTFrontendAction::ExecuteAction() + 305
45 clang-17                 0x00000001150b7f0c clang::FrontendAction::Execute() + 124

Copy link
Contributor

@devincoughlin devincoughlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great to see!

public:
void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
BugReporter &BR) const {
WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we can use ASTMatchers in static analyzer checkers, I would recommend using that rather than an explicit AST walker. For example, as Artem recommended, you can use forEachDescendant(). This saves the boilerplate of setting up a custom walker.

Copy link
Contributor

@rniwa rniwa Nov 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... wouldn't that make it hard to cherry-pick this change to stable/20230723 branch? We're still using that branch as a base so it would be ideal if this checker was written in such a way that it works with the branch.

Copy link
Contributor Author

@t-rasmud t-rasmud Nov 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I did not find a way to chain match CxxRecordDecl in a way that can pull the type of the parent class and match it with the explicitly cast type. Are there examples/documentation for matching on base class for the current cxxRecordDecl? @devincoughlin @haoNoQ

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... wouldn't that make it hard to cherry-pick this change to stable/20230723 branch?

The ability to use ASTMatchers in static analyzer checkers has been there for ~5 years, I think it should be safe.

Also, I did not find a way to chain match CxxRecordDecl in a way that can pull the type of the parent class and match it with the explicitly cast type.

You can use cxxRecordDecl(isDerivedFrom(...)) to match parent types. If necessary, you can use equalsBoundNode("binding_name") to back-reference the parent type if it's already being matched elsewhere.

That said, you also don't have to do everything (or anything really) with ASTMatchers. It's ok to stop the matcher at the CxxRecordDecl and plug the visitor back into it in order to confirm that the types match correctly (either directly inside the imperative code of the match results callback, or in the form of a custom matcher).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the patch with AST matchers and added support for matching ObjectiveC pointers that was missing earlier.
@haoNoQ @rniwa @devincoughlin

// Override CommandEncoder
id<MTLRenderCommandEncoder> get()
{
return static_cast<id<MTLRenderCommandEncoder>>(CommandEncoder::get());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we be getting a warning here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm.. This is a reproducer for the crash when BaseObjCPtrType is an invalid type class and we just return in that case.

@devincoughlin
Copy link
Contributor

Thanks Rashmi! I suggest pulling the minor ASTMatcher infrastructure improvements for Objective-C into a separate PR and merging that first, separately from the new checker. For the ASTMatcher work, @AaronBallman is a good person to review.


auto MatchExprPtr = allOf(
hasSourceExpression(hasTypePointingTo(cxxRecordDecl().bind(BaseNode))),
hasTypePointingTo(cxxRecordDecl(isDerivedFrom(equalsBoundNode(BaseNode)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this also catch cross casts? For example, if A and B both derive from D and A is cast to B, do we want to reject that? (And similarly if A and B don't have a common base?)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to reject all those casts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the checker to reject unrelated casts. Also relaxed casting rules for CRTP classes based on our offline discussion.

@t-rasmud
Copy link
Contributor Author

Thanks Rashmi! I suggest pulling the minor ASTMatcher infrastructure improvements for Objective-C into a separate PR and merging that first, separately from the new checker. For the ASTMatcher work, @AaronBallman is a good person to review.

PR: #117021

static void emitReport(AnalysisDeclContext *ADC, BugReporter &BR,
const MemoryUnsafeCastChecker *Checker,
std::string &Diagnostics, const CastExpr *CE) {
BR.EmitBasicReport(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use emitReport like other WebKit checkers?

std::string &Diagnostics, const CastExpr *CE) {
BR.EmitBasicReport(
ADC->getDecl(), Checker,
/*Name=*/"Unsafe type cast", categories::SecurityError, Diagnostics,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This lists the warnings for this kind under "Security error" instead of "WebKit coding guidelines" in the results page.

Copy link

github-actions bot commented Dec 4, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

Unverified

The signing certificate or its chain could not be verified.
The checker warns all downcasts from a base type to a derived type.

rdar://137766829
@t-rasmud t-rasmud merged commit 51a5b77 into llvm:main Dec 5, 2024
9 checks passed
rniwa pushed a commit to rniwa/llvm-project that referenced this pull request Feb 3, 2025
…lvm#114606)

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

rdar://137766829
devincoughlin pushed a commit to swiftlang/llvm-project that referenced this pull request Feb 25, 2025
…lvm#114606)

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

rdar://137766829
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:static analyzer clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants