From: Artem Dergachev Date: Wed, 19 Jun 2019 23:33:34 +0000 (+0000) Subject: [analyzer] RetainCount: Add support for OSRequiredCast(). X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=dd80aeb14dc7261c585619e26fbfdde0ae003ecc;p=clang [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 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@363891 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Analysis/RetainSummaryManager.cpp b/lib/Analysis/RetainSummaryManager.cpp index 4f0fced60b..132053fd2c 100644 --- a/lib/Analysis/RetainSummaryManager.cpp +++ b/lib/Analysis/RetainSummaryManager.cpp @@ -152,6 +152,10 @@ static bool isOSObjectDynamicCast(StringRef S) { return S == "safeMetaCast"; } +static bool isOSObjectRequiredCast(StringRef S) { + return S == "requiredMetaCast"; +} + static bool isOSObjectThisCast(StringRef S) { return S == "metaCast"; } @@ -234,7 +238,8 @@ RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD, if (RetTy->isPointerType()) { const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl(); if (PD && isOSObjectSubclass(PD)) { - if (isOSObjectDynamicCast(FName) || isOSObjectThisCast(FName)) + if (isOSObjectDynamicCast(FName) || isOSObjectRequiredCast(FName) || + isOSObjectThisCast(FName)) return getDefaultSummary(); // TODO: Add support for the slightly common *Matching(table) idiom. @@ -745,6 +750,8 @@ RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD, if (TrackOSObjects) { if (isOSObjectDynamicCast(FName) && FD->param_size() >= 1) { return BehaviorSummary::IdentityOrZero; + } else if (isOSObjectRequiredCast(FName) && FD->param_size() >= 1) { + return BehaviorSummary::Identity; } else if (isOSObjectThisCast(FName) && isa(FD) && !cast(FD)->isStatic()) { return BehaviorSummary::IdentityThis; diff --git a/test/Analysis/os_object_base.h b/test/Analysis/os_object_base.h index cd59e4f0bc..37a3fc07df 100644 --- a/test/Analysis/os_object_base.h +++ b/test/Analysis/os_object_base.h @@ -12,6 +12,8 @@ #define OSDynamicCast(type, inst) \ ((type *) OSMetaClassBase::safeMetaCast((inst), OSTypeID(type))) +#define OSRequiredCast(type, inst) \ + ((type *) OSMetaClassBase::requiredMetaCast((inst), OSTypeID(type))) #define OSTypeAlloc(type) ((type *) ((type::metaClass)->alloc())) @@ -22,6 +24,8 @@ struct OSMetaClass; struct OSMetaClassBase { static OSMetaClassBase *safeMetaCast(const OSMetaClassBase *inst, const OSMetaClass *meta); + static OSMetaClassBase *requiredMetaCast(const OSMetaClassBase *inst, + const OSMetaClass *meta); OSMetaClassBase *metaCast(const char *toMeta); diff --git a/test/Analysis/osobject-retain-release.cpp b/test/Analysis/osobject-retain-release.cpp index 10ef144bf3..afcc242583 100644 --- a/test/Analysis/osobject-retain-release.cpp +++ b/test/Analysis/osobject-retain-release.cpp @@ -1,9 +1,11 @@ // RUN: %clang_analyze_cc1 -fblocks -analyze -analyzer-output=text\ -// RUN: -analyzer-checker=core,osx -verify %s +// RUN: -analyzer-checker=core,osx,debug.ExprInspection -verify %s #include "os_object_base.h" #include "os_smart_ptr.h" +void clang_analyzer_eval(bool); + struct OSIterator : public OSObject { static const OSMetaClass * const metaClass; }; @@ -483,6 +485,23 @@ void check_dynamic_cast() { arr->release(); } +void check_required_cast() { + OSArray *arr = OSRequiredCast(OSArray, OSObject::generateObject(1)); + arr->release(); // no-warning +} + +void check_cast_behavior(OSObject *obj) { + OSArray *arr1 = OSDynamicCast(OSArray, obj); + clang_analyzer_eval(arr1 == obj); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} + // expected-note@-2{{Assuming 'arr1' is not equal to 'obj'}} + // expected-warning@-3{{FALSE}} + // expected-note@-4 {{FALSE}} + OSArray *arr2 = OSRequiredCast(OSArray, obj); + clang_analyzer_eval(arr2 == obj); // expected-warning{{TRUE}} + // expected-note@-1{{TRUE}} +} + unsigned int check_dynamic_cast_no_null_on_orig(OSObject *obj) { OSArray *arr = OSDynamicCast(OSArray, obj); if (arr) {