FLEXObjcInternal.mm 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  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. #if __arm64e__
  36. #include <ptrauth.h>
  37. #endif
  38. #define ALWAYS_INLINE inline __attribute__((always_inline))
  39. #define NEVER_INLINE inline __attribute__((noinline))
  40. // The macros below are copied straight from
  41. // objc-internal.h, objc-private.h, objc-object.h, and objc-config.h with
  42. // as few modifications as possible. Changes are noted in boxed comments.
  43. // https://opensource.apple.com/source/objc4/objc4-723/
  44. // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-internal.h.auto.html
  45. // https://opensource.apple.com/source/objc4/objc4-723/runtime/objc-object.h.auto.html
  46. /////////////////////
  47. // objc-internal.h //
  48. /////////////////////
  49. #if OBJC_HAVE_TAGGED_POINTERS
  50. ///////////////////
  51. // objc-object.h //
  52. ///////////////////
  53. ////////////////////////////////////////////////
  54. // originally objc_object::isExtTaggedPointer //
  55. ////////////////////////////////////////////////
  56. NS_INLINE BOOL flex_isExtTaggedPointer(const void *ptr) {
  57. return ((uintptr_t)ptr & _OBJC_TAG_EXT_MASK) == _OBJC_TAG_EXT_MASK;
  58. }
  59. #endif // OBJC_HAVE_TAGGED_POINTERS
  60. /////////////////////////////////////
  61. // FLEXObjectInternal //
  62. // No Apple code beyond this point //
  63. /////////////////////////////////////
  64. extern "C" {
  65. BOOL FLEXPointerIsReadable(const void *inPtr) {
  66. kern_return_t error = KERN_SUCCESS;
  67. vm_size_t vmsize;
  68. #if __arm64e__
  69. // On arm64e, we need to strip the PAC from the pointer so the adress is readable
  70. vm_address_t address = (vm_address_t)ptrauth_strip(inPtr, ptrauth_key_function_pointer);
  71. #else
  72. vm_address_t address = (vm_address_t)inPtr;
  73. #endif
  74. vm_region_basic_info_data_t info;
  75. mach_msg_type_number_t info_count = VM_REGION_BASIC_INFO_COUNT_64;
  76. memory_object_name_t object;
  77. error = vm_region_64(
  78. mach_task_self(),
  79. &address,
  80. &vmsize,
  81. VM_REGION_BASIC_INFO,
  82. (vm_region_info_t)&info,
  83. &info_count,
  84. &object
  85. );
  86. if (error != KERN_SUCCESS) {
  87. // vm_region/vm_region_64 returned an error
  88. return NO;
  89. } else if (!(BOOL)(info.protection & VM_PROT_READ)) {
  90. return NO;
  91. }
  92. // Read the memory
  93. vm_offset_t readMem = 0;
  94. mach_msg_type_number_t size = 0;
  95. #if __arm64e__
  96. address = (vm_address_t)ptrauth_strip(inPtr, ptrauth_key_function_pointer);
  97. #else
  98. address = (vm_address_t)inPtr;
  99. #endif
  100. error = vm_read(mach_task_self(), address, sizeof(uintptr_t), &readMem, &size);
  101. if (error != KERN_SUCCESS) {
  102. // vm_read returned an error
  103. return NO;
  104. }
  105. return YES;
  106. }
  107. /// Accepts addresses that may or may not be readable.
  108. /// https://blog.timac.org/2016/1124-testing-if-an-arbitrary-pointer-is-a-valid-objective-c-object/
  109. BOOL FLEXPointerIsValidObjcObject(const void *ptr) {
  110. uintptr_t pointer = (uintptr_t)ptr;
  111. if (!ptr) {
  112. return NO;
  113. }
  114. #if OBJC_HAVE_TAGGED_POINTERS
  115. // Tagged pointers have 0x1 set, no other valid pointers do
  116. // objc-internal.h -> _objc_isTaggedPointer()
  117. if (flex_isTaggedPointer(ptr) || flex_isExtTaggedPointer(ptr)) {
  118. return YES;
  119. }
  120. #endif
  121. // Check pointer alignment
  122. if ((pointer % sizeof(uintptr_t)) != 0) {
  123. return NO;
  124. }
  125. // From LLDB:
  126. // Pointers in a class_t will only have bits 0 through 46 set,
  127. // so if any pointer has bits 47 through 63 high, we know that this is not a valid isa
  128. // https://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/objc_runtime.py
  129. if ((pointer & 0xFFFF800000000000) != 0) {
  130. return NO;
  131. }
  132. // Make sure dereferencing this address won't crash
  133. if (!FLEXPointerIsReadable(ptr)) {
  134. return NO;
  135. }
  136. // http://www.sealiesoftware.com/blog/archive/2013/09/24/objc_explain_Non-pointer_isa.html
  137. // We check if the returned class is readable because object_getClass
  138. // can return a garbage value when given a non-nil pointer to a non-object
  139. Class cls = object_getClass((__bridge id)ptr);
  140. if (cls && FLEXPointerIsReadable((__bridge void *)cls)) {
  141. // Just because this pointer is readable doesn't mean whatever is at
  142. // it's ISA offset is readable. We need to do the same checks on it's ISA.
  143. // Even this isn't perfect, because once we call object_isClass, we're
  144. // going to dereference a member of the metaclass, which may or may not
  145. // be readable itself. For the time being there is no way to access it
  146. // to check here, and I have yet to hard-code a solution.
  147. Class metaclass = object_getClass(cls);
  148. if (metaclass && FLEXPointerIsReadable((__bridge void *)metaclass)) {
  149. if (object_isClass(cls)) {
  150. return YES;
  151. }
  152. }
  153. }
  154. return NO;
  155. }
  156. } // End extern "C"