Skip to content

Commit b03854f

Browse files
committed
[analyzer] RetainCount: Add support for OSRequiredCast().
It's a new API for custom RTTI in Apple IOKit/DriverKit framework that is similar to OSDynamicCast() that's already supported, but crashes instead of returning null (and therefore causing UB when the cast fails unexpectedly). Kind of like cast_or_null<> as opposed to dyn_cast_or_null<> in LLVM's RTTI. Historically, RetainCountChecker was responsible for modeling OSDynamicCast. This is simply an extension of the same functionality. Differential Revision: https://reviews.llvm.org/D63117 llvm-svn: 363891
1 parent 2415161 commit b03854f

File tree

3 files changed

+32
-2
lines changed

3 files changed

+32
-2
lines changed

clang/lib/Analysis/RetainSummaryManager.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ static bool isOSObjectDynamicCast(StringRef S) {
152152
return S == "safeMetaCast";
153153
}
154154

155+
static bool isOSObjectRequiredCast(StringRef S) {
156+
return S == "requiredMetaCast";
157+
}
158+
155159
static bool isOSObjectThisCast(StringRef S) {
156160
return S == "metaCast";
157161
}
@@ -234,7 +238,8 @@ RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
234238
if (RetTy->isPointerType()) {
235239
const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
236240
if (PD && isOSObjectSubclass(PD)) {
237-
if (isOSObjectDynamicCast(FName) || isOSObjectThisCast(FName))
241+
if (isOSObjectDynamicCast(FName) || isOSObjectRequiredCast(FName) ||
242+
isOSObjectThisCast(FName))
238243
return getDefaultSummary();
239244

240245
// TODO: Add support for the slightly common *Matching(table) idiom.
@@ -745,6 +750,8 @@ RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD,
745750
if (TrackOSObjects) {
746751
if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) {
747752
return BehaviorSummary::IdentityOrZero;
753+
} else if (isOSObjectRequiredCast(FName) && FD->param_size() >= 1) {
754+
return BehaviorSummary::Identity;
748755
} else if (isOSObjectThisCast(FName) && isa<CXXMethodDecl>(FD) &&
749756
!cast<CXXMethodDecl>(FD)->isStatic()) {
750757
return BehaviorSummary::IdentityThis;

clang/test/Analysis/os_object_base.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#define OSDynamicCast(type, inst) \
1414
((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type)))
15+
#define OSRequiredCast(type, inst) \
16+
((type *) OSMetaClassBase::requiredMetaCast((inst), OSTypeID(type)))
1517

1618
#define OSTypeAlloc(type) ((type *) ((type::metaClass)->alloc()))
1719

@@ -22,6 +24,8 @@ struct OSMetaClass;
2224
struct OSMetaClassBase {
2325
static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst,
2426
const OSMetaClass *meta);
27+
static OSMetaClassBase *requiredMetaCast(const OSMetaClassBase *inst,
28+
const OSMetaClass *meta);
2529

2630
OSMetaClassBase *metaCast(const char *toMeta);
2731

clang/test/Analysis/osobject-retain-release.cpp

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\
2-
// RUN: -analyzer-checker=core,osx -verify %s
2+
// RUN: -analyzer-checker=core,osx,debug.ExprInspection -verify %s
33

44
#include "os_object_base.h"
55
#include "os_smart_ptr.h"
66

7+
void clang_analyzer_eval(bool);
8+
79
struct OSIterator : public OSObject {
810
static const OSMetaClass * const metaClass;
911
};
@@ -483,6 +485,23 @@ void check_dynamic_cast() {
483485
arr->release();
484486
}
485487

488+
void check_required_cast() {
489+
OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1));
490+
arr->release(); // no-warning
491+
}
492+
493+
void check_cast_behavior(OSObject *obj) {
494+
OSArray *arr1 = OSDynamicCast(OSArray, obj);
495+
clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}}
496+
// expected-note@-1{{TRUE}}
497+
// expected-note@-2{{Assuming 'arr1' is not equal to 'obj'}}
498+
// expected-warning@-3{{FALSE}}
499+
// expected-note@-4 {{FALSE}}
500+
OSArray *arr2 = OSRequiredCast(OSArray, obj);
501+
clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}}
502+
// expected-note@-1{{TRUE}}
503+
}
504+
486505
unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) {
487506
OSArray *arr = OSDynamicCast(OSArray, obj);
488507
if (arr) {

0 commit comments

Comments
 (0)