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@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + Derived *derived_reinterpret = reinterpret_cast<Derived*>(base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + Derived *derived_c = (Derived*)base; + // expected-warning@-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@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + Derived &derived_reinterpret = reinterpret_cast<Derived&>(base); + // expected-warning@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + Derived &derived_c = (Derived&)base; + // expected-warning@-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@-1{{Unsafe cast from base type 'BaseVirtual' to derived type 'DerivedVirtual'}} + DerivedVirtual &derived_dynamic_ref = dynamic_cast<DerivedVirtual&>(base_ref); + // expected-warning@-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@-1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}} + struct DerivedStruct *derived_reinterpret = reinterpret_cast<struct DerivedStruct*>(base_struct); + // expected-warning@-1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}} + struct DerivedStruct *derived_c = (struct DerivedStruct*)base_struct; + // expected-warning@-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@-1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}} + DStruct &derived_reinterpret = reinterpret_cast<DStruct&>(base_struct); + // expected-warning@-1{{Unsafe cast from base type 'BaseStruct' to derived type 'DerivedStruct'}} + DStruct &derived_c = (DStruct&)base_struct; + // expected-warning@-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@-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@-1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}} + DerivedTemplate<int> *derived_reinterpret = reinterpret_cast<DerivedTemplate<int>*>(base); + // expected-warning@-1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}} + DerivedTemplate<int> *derived_c = (DerivedTemplate<int>*)base; + // expected-warning@-1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}} + DerivedTemplate<int> &derived_static_ref = static_cast<DerivedTemplate<int>&>(base_ref); + // expected-warning@-1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}} + DerivedTemplate<int> &derived_reinterpret_ref = reinterpret_cast<DerivedTemplate<int>&>(base_ref); + // expected-warning@-1{{Unsafe cast from base type 'BaseTemplate' to derived type 'DerivedTemplate'}} + DerivedTemplate<int> &derived_c_ref = (DerivedTemplate<int>&)base_ref; + // expected-warning@-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@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + Derived &derived_static_ref = CAST_MACRO_STATIC(base_ref, Derived&); + // expected-warning@-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@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + Derived &derived_reinterpret_ref = CAST_MACRO_REINTERPRET(base_ref, Derived&); + // expected-warning@-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@-1{{Unsafe cast from base type 'Base' to derived type 'Derived'}} + Derived &derived_c_ref = CAST_MACRO_C(base_ref, Derived&); + // expected-warning@-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@-1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}} + struct DerivedStructCpp *derived_reinterpret = reinterpret_cast<struct DerivedStructCpp*>(base_struct); + // expected-warning@-1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}} + struct DerivedStructCpp *derived_c = (struct DerivedStructCpp*)base_struct; + // expected-warning@-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@-1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}} + DStructCpp &derived_reinterpret = reinterpret_cast<DStructCpp&>(base_struct); + // expected-warning@-1{{Unsafe cast from base type 'BaseStructCpp' to derived type 'DerivedStructCpp'}} + DStructCpp &derived_c = (DStructCpp&)base_struct; + // expected-warning@-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@-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@-1{{Unsafe cast from type 'Child1' to an unrelated type 'Child2'}} + Child2 *c2_reinterpret = reinterpret_cast<Child2 *>(c1); + // expected-warning@-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@-1{{Unsafe cast from type 'Type1' to an unrelated type 'Type2'}} + Type2 &t2_reinterpret = reinterpret_cast<Type2 &>(t1); + // expected-warning@-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@-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@-1{{Unsafe cast from type 'StructA' to an unrelated type 'StructB'}} + StB &b_c = (StB&)a; + // expected-warning@-1{{Unsafe cast from type 'StructA' to an unrelated type 'StructB'}} + StA &a_local = (StA&)b; + // expected-warning@-1{{Unsafe cast from type 'StructB' to an unrelated type 'StructA'}} + StA &a_reinterpret = reinterpret_cast<StA&>(b); + // expected-warning@-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 + +@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{{Unsafe cast from base type 'BaseClass' to derived type 'DerivedClass'}} + DerivedClass *derived_static = static_cast<DerivedClass*>(base); + // expected-warning@-1{{Unsafe cast from base type 'BaseClass' to derived type 'DerivedClass'}} + DerivedClass *derived_reinterpret = reinterpret_cast<DerivedClass*>(base); + // expected-warning@-1{{Unsafe cast from base type 'BaseClass' to derived type 'DerivedClass'}} + base = (BaseClass*)derived; // no warning + base = (BaseClass*)base; // no warning +} +@end + +template <typename T> +class WrappedObject +{ +public: + T get() const { return mMetalObject; } + T mMetalObject = nullptr; +}; + +@protocol MTLCommandEncoder +@end +@protocol MTLRenderCommandEncoder +@end +class CommandEncoder : public WrappedObject<id<MTLCommandEncoder>> { }; + +class RenderCommandEncoder final : public CommandEncoder +{ +private: + id<MTLRenderCommandEncoder> get() + { + return static_cast<id<MTLRenderCommandEncoder>>(CommandEncoder::get()); + } +}; + +@interface Class1 +@end + +@interface Class2 +@end + +void testUnrelated(Class1 *c1) { + Class2 *c2 = (Class2*)c1; + // expected-warning@-1{{Unsafe cast from type 'Class1' to an unrelated type 'Class2'}} + Class1 *c1_same = reinterpret_cast<Class1*>(c1); // no warning +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits