id CHLViableObjcInstance()

in Chisel/Chisel/CHLObjcInstances.mm [77:136]


id CHLViableObjcInstance(vm_range_t range, const std::unordered_set<Class> &classSet)
{
  // Check if this address points to an object embedded into Mach-O.
  if (range.size == 0) {
    return embeddedObjcInstance(range);
  }

  id obj = reinterpret_cast<id>(range.address);

  // It's safe to call object_getClass on memory that isn't objc objects.
  // Check that the returned Class points to an expected class.
  Class cls = object_getClass(obj);
  if (classSet.find(cls) == classSet.end()) {
    return nil;
  }

  // Instance size is the byte count needed for an object's ivars, plus any padding.
  // Allocation size is the byte count that malloc will actually allocate for instances of a Class.
  const auto instanceSize = class_getInstanceSize(cls);
  const auto expectedAllocationSize = malloc_good_size(instanceSize);
  const auto extraSize = expectedAllocationSize - instanceSize;

  const bool debug = getenv("FINDINSTANCES_DEBUG") != NULL;

  if (range.size < expectedAllocationSize) {
    if (debug) {
      printf("%p has class %s but is too small\n", obj, class_getName(cls));
    }
    return nil;
  }

  if (range.size > expectedAllocationSize && isFixedSizeClass(cls)) {
    // Range is too big and the class has no way of allocating larger instances.
    if (debug) {
      printf("%p has fixed size class %s but is too large\n", obj, class_getName(cls));
    }
    return nil;
  }

  if (range.size == expectedAllocationSize && extraSize) {
    // ObjC instances are allocated with calloc, memory beyond the instance size should be zeros.
    // Some classes have been known to store data in the extra space, ex NSConcreteValue.
    static const unsigned char ZEROS[1024] = {0};
    auto extra = object_getIndexedIvars(obj);
    auto compareSize = std::min(extraSize, sizeof(ZEROS));
    if (memcmp(extra, &ZEROS, compareSize) != 0) {
      if (debug) {
        printf("%p has class %s but has non-zero memory\n", obj, class_getName(cls));
      }
      return nil;
    }
  }

  // Ignore deallocating objects.
  if ([obj _isDeallocating]) {
    return nil;
  }

  return obj;
}