[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:
@@ -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.
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
266
clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp
Normal file
266
clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.cpp
Normal 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();
|
||||
};
|
||||
64
clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm
Normal file
64
clang/test/Analysis/Checkers/WebKit/memory-unsafe-cast.mm
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user