[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