[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
This commit is contained in:
Rashmi Mudduluru
2024-12-05 11:01:27 -08:00
committed by GitHub
parent cb6a02abe2
commit 51a5b77b57
6 changed files with 548 additions and 0 deletions

View File

@@ -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.

View File

@@ -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>;

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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();
};

View File

@@ -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
}