FLEXObjcInternal.mm 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. //
  2. // FLEXObjcInternal.mm
  3. // FLEX
  4. //
  5. // Created by Tanner Bennett on 11/1/18.
  6. //
  7. /*
  8. * Copyright (c) 2005-2007 Apple Inc. All Rights Reserved.
  9. *
  10. * @APPLE_LICENSE_HEADER_START@
  11. *
  12. * This file contains Original Code and/or Modifications of Original Code
  13. * as defined in and that are subject to the Apple Public Source License
  14. * Version 2.0 (the 'License'). You may not use this file except in
  15. * compliance with the License. Please obtain a copy of the License at
  16. * http://www.opensource.apple.com/apsl/ and read it before using this
  17. * file.
  18. *
  19. * The Original Code and all software distributed under the License are
  20. * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  21. * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  22. * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  24. * Please see the License for the specific language governing rights and
  25. * limitations under the License.
  26. *
  27. * @APPLE_LICENSE_HEADER_END@
  28. */
  29. #import "FLEXObjcInternal.h"
  30. #import <objc/runtime.h>
  31. // For malloc_size
  32. #import <malloc/malloc.h>
  33. // For vm_region_64
  34. #include <mach/mach.h>
  35. #define ALWAYS_INLINE inline __attribute__((always_inline))
  36. #define NEVER_INLINE inline __attribute__((noinline))
  37. // The macros below are copied straight from
  38. // objc-internal.h, objc-private.h, objc-object.h, and objc-config.h with
  39. // as few modifications as possible. Changes are noted in boxed comments.
  40. // https://opensource.apple.com/source/objc4/objc4-723/
  41. // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-internal.h.auto.html
  42. // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-object.h.auto.html
  43. /////////////////////
  44. // objc-internal.h //
  45. /////////////////////
  46. #if __LP64__
  47. #define OBJC_HAVE_TAGGED_POINTERS 1
  48. #endif
  49. #if OBJC_HAVE_TAGGED_POINTERS
  50. #if TARGET_OS_OSX && __x86_64__
  51. // 64-bit Mac - tag bit is LSB
  52. # define OBJC_MSB_TAGGED_POINTERS 0
  53. #else
  54. // Everything else - tag bit is MSB
  55. # define OBJC_MSB_TAGGED_POINTERS 1
  56. #endif
  57. #if OBJC_MSB_TAGGED_POINTERS
  58. # define _OBJC_TAG_MASK (1UL<<63)
  59. # define _OBJC_TAG_EXT_MASK (0xfUL<<60)
  60. #else
  61. # define _OBJC_TAG_MASK 1UL
  62. # define _OBJC_TAG_EXT_MASK 0xfUL
  63. #endif
  64. //////////////////////////////////////
  65. // originally _objc_isTaggedPointer //
  66. //////////////////////////////////////
  67. static BOOL flex_isTaggedPointer(const void *ptr) {
  68. return ((uintptr_t)ptr & _OBJC_TAG_MASK) == _OBJC_TAG_MASK;
  69. }
  70. ///////////////////
  71. // objc-object.h //
  72. ///////////////////
  73. ////////////////////////////////////////////////
  74. // originally objc_object::isExtTaggedPointer //
  75. ////////////////////////////////////////////////
  76. static BOOL flex_isExtTaggedPointer(const void *ptr) {
  77. return ((uintptr_t)ptr & _OBJC_TAG_EXT_MASK) == _OBJC_TAG_EXT_MASK;
  78. }
  79. #endif
  80. /////////////////////////////////////
  81. // FLEXObjectInternal //
  82. // No Apple code beyond this point //
  83. /////////////////////////////////////
  84. extern "C" {
  85. static BOOL FLEXPointerIsReadable(const void *inPtr) {
  86. kern_return_t error = KERN_SUCCESS;
  87. vm_size_t vmsize;
  88. vm_address_t address = (vm_address_t)inPtr;
  89. vm_region_basic_info_data_t info;
  90. mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
  91. memory_object_name_t object;
  92. error = vm_region_64(
  93. mach_task_self(),
  94. &address,
  95. &vmsize,
  96. VM_REGION_BASIC_INFO,
  97. (vm_region_info_t)&info,
  98. &info_count,
  99. &object
  100. );
  101. if (error != KERN_SUCCESS) {
  102. // vm_region/vm_region_64 returned an error
  103. return NO;
  104. } else if (!(BOOL)(info.protection & VM_PROT_READ)) {
  105. return NO;
  106. }
  107. // Read the memory
  108. vm_offset_t readMem = 0;
  109. mach_msg_type_number_t size = 0;
  110. address = (vm_address_t)inPtr;
  111. error = vm_read(mach_task_self(), address, sizeof(uintptr_t), &readMem, &size);
  112. if (error != KERN_SUCCESS) {
  113. // vm_read returned an error
  114. return NO;
  115. }
  116. return YES;
  117. }
  118. /// Accepts addresses that may or may not be readable.
  119. /// https://blog.timac.org/2016/1124-testing-if-an-arbitrary-pointer-is-a-valid-objective-c-object/
  120. BOOL FLEXPointerIsValidObjcObject(const void *ptr) {
  121. uintptr_t pointer = (uintptr_t)ptr;
  122. if (!ptr) {
  123. return NO;
  124. }
  125. #if OBJC_HAVE_TAGGED_POINTERS
  126. // Tagged pointers have 0x1 set, no other valid pointers do
  127. // objc-internal.h -> _objc_isTaggedPointer()
  128. if (flex_isTaggedPointer(ptr) || flex_isExtTaggedPointer(ptr)) {
  129. return YES;
  130. }
  131. #endif
  132. // Check pointer alignment
  133. if ((pointer % sizeof(uintptr_t)) != 0) {
  134. return NO;
  135. }
  136. // From LLDB:
  137. // Pointers in a class_t will only have bits 0 through 46 set,
  138. // so if any pointer has bits 47 through 63 high, we know that this is not a valid isa
  139. // https://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/objc_runtime.py
  140. if ((pointer & 0xFFFF800000000000) != 0) {
  141. return NO;
  142. }
  143. // Make sure dereferencing this address won't crash
  144. if (!FLEXPointerIsReadable(ptr)) {
  145. return NO;
  146. }
  147. // http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
  148. // We check if the returned class is readable because object_getClass
  149. // can return a garbage value when given a non-nil pointer to a non-object
  150. Class cls = object_getClass((__bridge id)ptr);
  151. if (cls && FLEXPointerIsReadable((__bridge void *)cls)) {
  152. // Just because this pointer is readable doesn't mean whatever is at
  153. // it's ISA offset is readable. We need to do the same checks on it's ISA.
  154. // Even this isn't perfect, because once we call object_isClass, we're
  155. // going to dereference a member of the metaclass, which may or may not
  156. // be readable itself. For the time being there is no way to access it
  157. // to check here, and I have yet to hard-code a solution.
  158. Class metaclass = object_getClass(cls);
  159. if (metaclass && FLEXPointerIsReadable((__bridge void *)metaclass)) {
  160. if (object_isClass(cls)) {
  161. return YES;
  162. }
  163. }
  164. }
  165. return NO;
  166. }
  167. } // End extern "C"