ios: More deflaking handler forbidden allocators.

- Stop overloading introspect (or implement this in the future)
- Store each overridden allocation zone and correctly direct calls to
  the requested zone.

Change-Id: Ia56e0a5090076d4ca78e2a9fa1ebeb30b1b0b705
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4574037
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
GitOrigin-RevId: 1fdbd3736ca4fb523b5bd6fc9a663ac5cf8f6de4
diff --git a/test/ios/host/handler_forbidden_allocators.cc b/test/ios/host/handler_forbidden_allocators.cc
index 38f1c38..6d41b8d 100644
--- a/test/ios/host/handler_forbidden_allocators.cc
+++ b/test/ios/host/handler_forbidden_allocators.cc
@@ -30,7 +30,25 @@
 
 uint64_t g_main_thread = 0;
 uint64_t g_mach_exception_thread = 0;
-malloc_zone_t g_old_zone;
+
+// Somewhat simplified logic copied from Chromium's
+// base/allocator/partition_allocator/shim/malloc_zone_functions_mac.h. The
+// arrays g_original_zones and g_original_zones_ptr stores all information about
+// malloc zones before they are shimmed. This information needs to be accessed
+// during dispatch back into the zone.
+constexpr int kMaxZoneCount = 30;
+malloc_zone_t g_original_zones[kMaxZoneCount];
+malloc_zone_t* g_original_zones_ptr[kMaxZoneCount];
+unsigned int g_zone_count = 0;
+
+struct _malloc_zone_t original_zone_for_zone(struct _malloc_zone_t* zone) {
+  for (unsigned int i = 0; i < g_zone_count; ++i) {
+    if (g_original_zones_ptr[i] == zone) {
+      return g_original_zones[i];
+    }
+  }
+  return g_original_zones[0];
+}
 
 bool is_handler_thread() {
   uint64_t thread_self;
@@ -44,7 +62,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_malloc allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.malloc(zone, size);
+  return original_zone_for_zone(zone).malloc(zone, size);
 }
 
 void* handler_forbidden_calloc(struct _malloc_zone_t* zone,
@@ -54,7 +72,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_calloc allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.calloc(zone, num_items, size);
+  return original_zone_for_zone(zone).calloc(zone, num_items, size);
 }
 
 void* handler_forbidden_valloc(struct _malloc_zone_t* zone, size_t size) {
@@ -62,7 +80,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_valloc allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.valloc(zone, size);
+  return original_zone_for_zone(zone).valloc(zone, size);
 }
 
 void handler_forbidden_free(struct _malloc_zone_t* zone, void* ptr) {
@@ -70,7 +88,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_free allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  g_old_zone.free(zone, ptr);
+  original_zone_for_zone(zone).free(zone, ptr);
 }
 
 void* handler_forbidden_realloc(struct _malloc_zone_t* zone,
@@ -80,7 +98,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_realloc allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.realloc(zone, ptr, size);
+  return original_zone_for_zone(zone).realloc(zone, ptr, size);
 }
 
 void handler_forbidden_destroy(struct _malloc_zone_t* zone) {
@@ -88,7 +106,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_destroy allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  g_old_zone.destroy(zone);
+  original_zone_for_zone(zone).destroy(zone);
 }
 
 void* handler_forbidden_memalign(struct _malloc_zone_t* zone,
@@ -98,7 +116,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_memalign allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.memalign(zone, alignment, size);
+  return original_zone_for_zone(zone).memalign(zone, alignment, size);
 }
 
 unsigned handler_forbidden_batch_malloc(struct _malloc_zone_t* zone,
@@ -110,7 +128,8 @@
         "handler_forbidden_batch_malloc allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.batch_malloc(zone, size, results, num_requested);
+  return original_zone_for_zone(zone).batch_malloc(
+      zone, size, results, num_requested);
 }
 
 void handler_forbidden_batch_free(struct _malloc_zone_t* zone,
@@ -120,7 +139,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_batch_free allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  g_old_zone.batch_free(zone, to_be_freed, num_to_be_freed);
+  original_zone_for_zone(zone).batch_free(zone, to_be_freed, num_to_be_freed);
 }
 
 void handler_forbidden_free_definite_size(struct _malloc_zone_t* zone,
@@ -131,7 +150,7 @@
         "handler_forbidden_free_definite_size allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  g_old_zone.free_definite_size(zone, ptr, size);
+  original_zone_for_zone(zone).free_definite_size(zone, ptr, size);
 }
 
 size_t handler_forbidden_pressure_relief(struct _malloc_zone_t* zone,
@@ -141,7 +160,7 @@
         "handler_forbidden_pressure_relief allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.pressure_relief(zone, goal);
+  return original_zone_for_zone(zone).pressure_relief(zone, goal);
 }
 
 boolean_t handler_forbidden_claimed_address(struct _malloc_zone_t* zone,
@@ -152,14 +171,14 @@
     exit(EXIT_FAILURE);
   }
 
-  if (g_old_zone.claimed_address) {
-    return g_old_zone.claimed_address(zone, ptr);
+  if (original_zone_for_zone(zone).claimed_address) {
+    return original_zone_for_zone(zone).claimed_address(zone, ptr);
   }
 
   // If the fast API 'claimed_address' is not implemented in the specified zone,
   // fall back to 'size' function, which also tells whether the given address
   // belongs to the zone or not although it'd be slow.
-  return g_old_zone.size(zone, ptr);
+  return original_zone_for_zone(zone).size(zone, ptr);
 }
 
 #if defined(__IPHONE_16_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_1
@@ -199,8 +218,8 @@
     exit(EXIT_FAILURE);
   }
 
-  if (g_old_zone.try_free_default) {
-    return g_old_zone.try_free_default(zone, ptr);
+  if (original_zone_for_zone(zone).try_free_default) {
+    return original_zone_for_zone(zone).try_free_default(zone, ptr);
   }
   TryFreeDefaultFallbackToFindZoneAndFree(ptr);
 }
@@ -211,7 +230,7 @@
     CRASHPAD_RAW_LOG("handler_forbidden_size allocator used in handler.");
     exit(EXIT_FAILURE);
   }
-  return g_old_zone.size(zone, ptr);
+  return original_zone_for_zone(zone).size(zone, ptr);
 }
 
 bool DeprotectMallocZone(malloc_zone_t* default_zone,
@@ -293,7 +312,6 @@
   zone->destroy = functions->destroy;
   zone->batch_malloc = functions->batch_malloc;
   zone->batch_free = functions->batch_free;
-  zone->introspect = functions->introspect;
   zone->memalign = functions->memalign;
   zone->free_definite_size = functions->free_definite_size;
   zone->pressure_relief = functions->pressure_relief;
@@ -326,8 +344,6 @@
   CrashpadClient crashpad_client;
   g_mach_exception_thread = crashpad_client.GetThreadIdForTesting();
 
-  malloc_zone_t* default_zone = malloc_default_zone();
-  memcpy(&g_old_zone, default_zone, sizeof(g_old_zone));
   malloc_zone_t new_functions = {};
   new_functions.size = handler_forbidden_size;
   new_functions.malloc = handler_forbidden_malloc;
@@ -345,8 +361,16 @@
 #if defined(__IPHONE_16_1) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_16_1
   new_functions.try_free_default = handler_forbidden_try_free_default;
 #endif
+  malloc_zone_t* default_zone = malloc_default_zone();
+  g_original_zones_ptr[g_zone_count] = default_zone;
+  ReplaceZoneFunctions(&g_original_zones[g_zone_count++], default_zone);
   ReplaceZoneFunctions(default_zone, &new_functions);
 
+  malloc_zone_t* purgeable_zone = malloc_default_purgeable_zone();
+  g_original_zones_ptr[g_zone_count] = purgeable_zone;
+  ReplaceZoneFunctions(&g_original_zones[g_zone_count++], purgeable_zone);
+  ReplaceZoneFunctions(purgeable_zone, &new_functions);
+
   vm_address_t* zones;
   unsigned int count;
   kern_return_t kr =
@@ -355,11 +379,13 @@
     return;
   for (unsigned int i = 0; i < count; ++i) {
     malloc_zone_t* zone = reinterpret_cast<malloc_zone_t*>(zones[i]);
+    g_original_zones_ptr[g_zone_count] = zone;
+    ReplaceZoneFunctions(&g_original_zones[g_zone_count++], zone);
     ReplaceZoneFunctions(zone, &new_functions);
-  }
 
-  malloc_zone_t* purgeable_zone = malloc_default_purgeable_zone();
-  ReplaceZoneFunctions(purgeable_zone, &new_functions);
+    if (g_zone_count >= kMaxZoneCount)
+      break;
+  }
 }
 
 }  // namespace test