Skip to content

fix: invalid isa pointer access in ios 14 #1275

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 9, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions build/npm/inspector_package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tns-ios-inspector",
"description": "Telerik NativeScript Inspector for iOS Runtime",
"version": "6.5.1",
"description": "NativeScript Inspector for iOS Runtime",
"version": "6.5.2",
"keywords": [
"NativeScript",
"iOS",
Expand All @@ -12,8 +12,8 @@
"url": "https://github.com/NativeScript/ios-runtime"
},
"author": {
"name": "Telerik",
"email": "support@telerik.com"
"name": "NativeScript",
"email": "support@nativescript.org"
},
"os": [
"darwin"
Expand Down
8 changes: 4 additions & 4 deletions build/npm/runtime_package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "tns-ios",
"description": "Telerik NativeScript Runtime for iOS",
"version": "6.5.1",
"description": "NativeScript Runtime for iOS",
"version": "6.5.2",
"keywords": [
"NativeScript",
"iOS",
Expand All @@ -12,8 +12,8 @@
"url": "https://github.com/NativeScript/ios-runtime"
},
"author": {
"name": "Telerik",
"email": "support@telerik.com"
"name": "NativeScript",
"email": "support@nativescript.org"
},
"directories": {},
"files": [
Expand Down
2 changes: 1 addition & 1 deletion plugins/TKLiveSync/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@ SPEC CHECKSUMS:

PODFILE CHECKSUM: f5ca86f956288d6d48fd6aa384d742ee2b5999b3

COCOAPODS: 1.9.1
COCOAPODS: 1.9.3
194 changes: 10 additions & 184 deletions src/NativeScript/ObjC/IsObjcObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,144 +20,27 @@
// Objective-C runtime
#include <objc/runtime.h>

// For dlsym
#include <dlfcn.h>

// For malloc_size
#include <malloc/malloc.h>

// Do not thoroughly check valid memory pointers - it's too computationally intensive and slows down the runtime by 3 to 10 times
#define SKIP_CHECK_FOR_KNOWN_CLASS 1

#pragma mark - Expose non exported Tagged Pointer functions from objc4-706/runtime/objc-internal.h
#pragma mark - Expose non exported Tagged Pointer functions from objc4-781.2/runtime/objc-internal.h

#if TARGET_OS_OSX && __x86_64__
// 64-bit Mac - tag bit is LSB
#define OBJC_MSB_TAGGED_POINTERS 0
#else
// Everything else - tag bit is MSB
#define OBJC_MSB_TAGGED_POINTERS 1
#endif

#define _OBJC_TAG_INDEX_MASK 0x7
#define _OBJC_TAG_EXT_INDEX_MASK 0xff

#if OBJC_MSB_TAGGED_POINTERS
#define _OBJC_TAG_MASK (1ULL << 63)
#define _OBJC_TAG_INDEX_SHIFT 60
#define _OBJC_TAG_EXT_INDEX_SHIFT 52
// 64-bit Mac - tag bit is LSB
# define _OBJC_TAG_MASK 1UL
#else
#define _OBJC_TAG_MASK 1
#define _OBJC_TAG_INDEX_SHIFT 1
#define _OBJC_TAG_EXT_INDEX_SHIFT 4
// Everything else - tag bit is MSB
# define _OBJC_TAG_MASK (1ULL << 63)
#endif

#if defined __arm64__ && __arm64__
#define ISA_MASK 0x0000000ffffffff8ULL
#define ISA_MAGIC_MASK 0x000003f000000001ULL
#define ISA_MAGIC_VALUE 0x000001a000000001ULL

#elif defined __x86_64__ && __x86_64__
#define ISA_MASK 0x00007ffffffffff8ULL
#define ISA_MAGIC_MASK 0x001f800000000001ULL
#define ISA_MAGIC_VALUE 0x001d800000000001ULL

#elif (defined __arm__ && __arm__) || (defined __i386__ && __i386__)
#define SKIP_OBJECTIVE_C_CHECKS 1
#else
// Available bits in isa field are architecture-specific.
#error unknown architecture
#endif

#if !defined SKIP_OBJECTIVE_C_CHECKS || !SKIP_OBJECTIVE_C_CHECKS

typedef enum {
OBJC_TAG_NSAtom = 0,
OBJC_TAG_1 = 1,
OBJC_TAG_NSString = 2,
OBJC_TAG_NSNumber = 3,
OBJC_TAG_NSIndexPath = 4,
OBJC_TAG_NSManagedObjectID = 5,
OBJC_TAG_NSDate = 6,
OBJC_TAG_RESERVED_7 = 7,

OBJC_TAG_First60BitPayload = 0,
OBJC_TAG_Last60BitPayload = 6,
OBJC_TAG_First52BitPayload = 8,
OBJC_TAG_Last52BitPayload = 263,

OBJC_TAG_RESERVED_264 = 264
} objc_tag_index_t;

static inline bool _objc_isTaggedPointer(const void* ptr) {
return ((intptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
static inline bool _objc_isTaggedPointer(const void * _Nullable ptr) {
return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
}

static inline objc_tag_index_t _objc_getTaggedPointerTag(const void* ptr) {
uintptr_t basicTag = ((uintptr_t)ptr >> _OBJC_TAG_INDEX_SHIFT) & _OBJC_TAG_INDEX_MASK;
uintptr_t extTag = ((uintptr_t)ptr >> _OBJC_TAG_EXT_INDEX_SHIFT) & _OBJC_TAG_EXT_INDEX_MASK;
if (basicTag == _OBJC_TAG_INDEX_MASK) {
return (objc_tag_index_t)(extTag + OBJC_TAG_First52BitPayload);
} else {
return (objc_tag_index_t)basicTag;
}
}

#pragma mark - Tagged Pointer

/**
Returns the registered class for the given tag.
Returns nil if the tag is valid but has no registered class.

This function searches the exported function: _objc_getClassForTag(objc_tag_index_t tag)
declared in https://opensource.apple.com/source/objc4/objc4-706/runtime/objc-internal.h
*/
static Class _objc_getClassForTag(objc_tag_index_t tag) {
static bool _objc_getClassForTag_searched = false;
static Class (*_objc_getClassForTag_func)(objc_tag_index_t) = NULL;
if (!_objc_getClassForTag_searched) {
_objc_getClassForTag_func = (Class(*)(objc_tag_index_t))dlsym(RTLD_DEFAULT, "_objc_getClassForTag");
_objc_getClassForTag_searched = true;
if (_objc_getClassForTag_func == NULL) {
fprintf(stderr, "*** Could not find _objc_getClassForTag()!\n");
}
}

if (_objc_getClassForTag_func != NULL) {
return _objc_getClassForTag_func(tag);
}

return NULL;
}

/**
Test if a pointer is a tagged pointer

@param inPtr is the pointer to check
@param outClass returns the registered class for the tagged pointer.
@return true if the pointer is a tagged pointer.
*/
bool IsObjcTaggedPointer(const void* inPtr, Class* outClass) {
bool isTaggedPointer = _objc_isTaggedPointer(inPtr);
if (outClass != NULL) {
if (isTaggedPointer) {
objc_tag_index_t tagIndex = _objc_getTaggedPointerTag(inPtr);
*outClass = _objc_getClassForTag(tagIndex);
} else {
*outClass = NULL;
}
}

return isTaggedPointer;
}

#endif // !defined SKIP_OBJECTIVE_C_CHECKS || !SKIP_OBJECTIVE_C_CHECKS

#pragma mark - Readable and valid memory

/**
Test if the pointer points to readable and valid memory.

@param inPtr is the pointer
@return true if the pointer points to readable and valid memory.
*/
Expand Down Expand Up @@ -214,14 +97,12 @@ bool IsObjcObject(const void* inPtr) {
return false;
}

#if !defined SKIP_OBJECTIVE_C_CHECKS || !SKIP_OBJECTIVE_C_CHECKS
//
// Check for tagged pointers
// Check if the pointer is a tagged objc pointer
//
if (IsObjcTaggedPointer(inPtr, NULL)) {
if (_objc_isTaggedPointer(inPtr)) {
return true;
}
#endif

//
// Check if the pointer is aligned
Expand All @@ -237,67 +118,13 @@ bool IsObjcObject(const void* inPtr) {
return false;
}

#if !defined SKIP_OBJECTIVE_C_CHECKS || !SKIP_OBJECTIVE_C_CHECKS
//
// From LLDB:
// Objective-C runtime has a rule that pointers in a class_t will only have bits 0 thru 46 set
// so if any pointer has bits 47 thru 63 high we know that this is not a valid isa
// See http://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/objc_runtime.py
//
if (((uintptr_t)inPtr & 0xFFFF800000000000) != 0) {
return false;
}

//
// Get the Class from the pointer
// From http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html :
// If you are writing a debugger-like tool, the Objective-C runtime exports some variables
// to help decode isa fields. objc_debug_isa_class_mask describes which bits are the class pointer:
// (isa & class_mask) == class pointer.
// objc_debug_isa_magic_mask and objc_debug_isa_magic_value describe some bits that help
// distinguish valid isa fields from other invalid values:
// (isa & magic_mask) == magic_value for isa fields that are not raw class pointers.
// These variables may change in the future so do not use them in application code.
//

uintptr_t isa = (*(uintptr_t*)inPtr);
Class ptrClass = NULL;

if ((isa & ~ISA_MASK) == 0) {
ptrClass = (Class)isa;
} else {
if ((isa & ISA_MAGIC_MASK) == ISA_MAGIC_VALUE) {
ptrClass = (Class)(isa & ISA_MASK);
} else {
ptrClass = (Class)isa;
}
}
ptrClass = object_getClass((id)inPtr);

if (ptrClass == NULL) {
return false;
}

#if !defined(SKIP_CHECK_FOR_KNOWN_CLASS) || !SKIP_CHECK_FOR_KNOWN_CLASS
//
// Verifies that the found Class is a known class.
//
bool isKnownClass = false;

unsigned int numClasses = 0;
Class* classesList = objc_copyClassList(&numClasses);
for (unsigned i = 0; i < numClasses; i++) {
if (classesList[i] == ptrClass) {
isKnownClass = true;
break;
}
}
free(classesList);

if (!isKnownClass) {
return false;
}
#endif // !defined(SKIP_CHECK_FOR_KNOWN_CLASS) || !SKIP_CHECK_FOR_KNOWN_CLASS

//
// From Greg Parker
// https://twitter.com/gparker/status/801894068502433792
Expand All @@ -307,7 +134,6 @@ bool IsObjcObject(const void* inPtr) {
if (pointerSize > 0 && pointerSize < class_getInstanceSize(ptrClass)) {
return false;
}
#endif // !defined SKIP_OBJECTIVE_C_CHECKS || !SKIP_OBJECTIVE_C_CHECKS

return true;
}
9 changes: 0 additions & 9 deletions src/NativeScript/ObjC/IsObjcObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,4 @@
extern "C" {

bool IsObjcObject(const void* inPtr);

/**
Test if a pointer is a tagged pointer

@param inPtr is the pointer to check
@param outClass returns the registered class for the tagged pointer.
@return true if the pointer is a tagged pointer.
*/
bool IsObjcTaggedPointer(const void* inPtr, Class* outClass);
}