Loading...
--- libmalloc/libmalloc-646.0.13/src/pgm_malloc.c
+++ libmalloc/libmalloc-792.60.6/src/pgm_malloc.c
@@ -84,6 +84,7 @@
 	uint32_t max_allocations;
 	uint32_t max_metadata;
 	uint32_t sample_counter_range;
+	uint32_t left_align_pct;
 	bool debug;
 	uint64_t debug_log_throttle_ms;
 
@@ -126,7 +127,7 @@
 
 MALLOC_STATIC_ASSERT(sizeof(void *) >= sizeof(uint32_t), "Pointer is used as 32bit counter");
 
-#define TSD_GET_COUNTER() ((uint32_t)_pthread_getspecific_direct(__TSD_MALLOC_PROB_GUARD_SAMPLE_COUNTER))
+#define TSD_GET_COUNTER() ((uint32_t)(uintptr_t)_pthread_getspecific_direct(__TSD_MALLOC_PROB_GUARD_SAMPLE_COUNTER))
 #define TSD_SET_COUNTER(val) _pthread_setspecific_direct(__TSD_MALLOC_PROB_GUARD_SAMPLE_COUNTER, (void *)(uintptr_t)val)
 
 static const uint32_t k_no_sample = UINT32_MAX;
@@ -366,12 +367,17 @@
 }
 
 static uint16_t
-choose_offset_on_page(size_t size, size_t alignment, uint16_t page_size) {
+choose_offset_on_page(size_t size,
+		size_t alignment,
+		uint32_t left_align_pct,
+		uint16_t page_size)
+{
 	MALLOC_ASSERT(size <= page_size);
 	MALLOC_ASSERT(alignment <= page_size && is_power_of_2(alignment));
 	MALLOC_ASSERT(is_power_of_2(page_size));
-	boolean_t left_align = rand_uniform(2);
-	if (left_align) {
+	MALLOC_ASSERT(left_align_pct <= 100);
+
+	if (rand_uniform(100) < left_align_pct) {
 		return 0;
 	}
 	size_t mask = ~(alignment - 1);
@@ -435,7 +441,8 @@
 	size = block_size(size);
 	uint32_t slot = choose_available_slot(zone);
 	uint32_t metadata = choose_metadata(zone, slot);
-	uint16_t offset = choose_offset_on_page(size, alignment, PAGE_SIZE);
+	uint16_t offset = choose_offset_on_page(
+			size, alignment, zone->left_align_pct, PAGE_SIZE);
 
 	// Mark page writable before updating metadata.  Ensures metadata is correct
 	// whenever a fault could be triggered.
@@ -582,6 +589,13 @@
 }
 
 static void *
+pgm_malloc_type_malloc(pgm_zone_t *zone, size_t size, malloc_type_id_t type_id)
+{
+	SAMPLED_ALLOCATE(size, k_min_alignment, malloc_type_malloc, size, type_id);
+	return ptr;
+}
+
+static void *
 pgm_calloc(pgm_zone_t *zone, size_t num_items, size_t size)
 {
 	size_t total_size;
@@ -589,7 +603,21 @@
 		return DELEGATE(calloc, num_items, size);
 	}
 	SAMPLED_ALLOCATE(total_size, k_min_alignment, calloc, num_items, size);
-	memset(ptr, 0, total_size);
+	bzero(ptr, total_size);
+	return ptr;
+}
+
+static void *
+pgm_malloc_type_calloc(pgm_zone_t *zone, size_t num_items, size_t size,
+		malloc_type_id_t type_id)
+{
+	size_t total_size;
+	if (os_unlikely(os_mul_overflow(num_items, size, &total_size))) {
+		return DELEGATE(malloc_type_calloc, num_items, size, type_id);
+	}
+	SAMPLED_ALLOCATE(total_size, k_min_alignment, malloc_type_calloc, num_items,
+			size, type_id);
+	bzero(ptr, total_size);
 	return ptr;
 }
 
@@ -622,6 +650,23 @@
 	return new_ptr;
 }
 
+static void *
+pgm_malloc_type_realloc(pgm_zone_t *zone, void *ptr, size_t new_size,
+		malloc_type_id_t type_id)
+{
+	if (os_unlikely(!ptr)) {
+		return pgm_malloc_type_malloc(zone, new_size, type_id);
+	}
+	boolean_t sample = should_sample(zone, new_size);
+	if (os_likely(!sample)) {
+		DELEGATE_UNGUARDED(ptr, malloc_type_realloc, ptr, new_size, type_id);
+	}
+	lock(zone);
+	void *new_ptr = (void *)reallocate(zone, (vm_address_t)ptr, new_size, sample);
+	unlock(zone);
+	return new_ptr;
+}
+
 static void my_vm_deallocate(vm_address_t addr, size_t size);
 static void
 pgm_destroy(pgm_zone_t *zone)
@@ -634,8 +679,7 @@
 static void *
 pgm_memalign(pgm_zone_t *zone, size_t alignment, size_t size)
 {
-	// Delegate for (alignment > page size) and invalid alignment sizes.
-	if (alignment > PAGE_SIZE || !is_power_of_2(alignment) || alignment < sizeof(void *)) {
+	if (os_unlikely(alignment > PAGE_SIZE)) {
 		return DELEGATE(memalign, alignment, size);
 	}
 	size_t adj_alignment = MAX(alignment, k_min_alignment);
@@ -643,6 +687,19 @@
 	return ptr;
 }
 
+static void *
+pgm_malloc_type_memalign(pgm_zone_t *zone, size_t alignment, size_t size,
+		malloc_type_id_t type_id)
+{
+	if (os_unlikely(alignment > PAGE_SIZE)) {
+		return DELEGATE(malloc_type_memalign, alignment, size, type_id);
+	}
+	size_t adj_alignment = MAX(alignment, k_min_alignment);
+	SAMPLED_ALLOCATE(size, adj_alignment, malloc_type_memalign, alignment, size,
+			type_id);
+	return ptr;
+}
+
 static void
 pgm_free_definite_size(pgm_zone_t *zone, void *ptr, size_t size)
 {
@@ -658,37 +715,34 @@
 
 static void *
 pgm_malloc_with_options(pgm_zone_t *zone, size_t align, size_t size,
-		uint64_t options)
-{
-	if (os_unlikely(should_sample(zone, size))) {
-		size_t adj_alignment = MAX(align, k_min_alignment);
-		lock(zone);
-		void *ptr = (void *)allocate(zone, size, adj_alignment);
-		unlock(zone);
-		if (ptr) {
-			if (options & MALLOC_NP_OPTION_CLEAR) {
-				bzero(ptr, size);
-			}
-			return ptr;
-		}
-		// If the allocation fails, fall back to the wrapped zone
-	}
-
-	// FIXME: calls wrapped_zone->malloc_with_options/memalign without NULL check
-	if (zone->wrapped_zone->version >= 15 &&
-			zone->wrapped_zone->malloc_with_options) {
+		malloc_zone_malloc_options_t options)
+{
+	if (os_unlikely(align > PAGE_SIZE)) {
 		return DELEGATE(malloc_with_options, align, size, options);
-	} else if (align) {
-		void *ptr = DELEGATE(memalign, align, size);
-		if (ptr && (options & MALLOC_NP_OPTION_CLEAR)) {
-			bzero(ptr, size);
-		}
-		return ptr;
-	} else if (options & MALLOC_NP_OPTION_CLEAR) {
-		return DELEGATE(calloc, 1, size);
-	} else {
-		return DELEGATE(malloc, size);
-	}
+	}
+	size_t adj_alignment = MAX(align, k_min_alignment);
+	SAMPLED_ALLOCATE(size, adj_alignment, malloc_with_options, align, size, options);
+	if (options & MALLOC_NP_OPTION_CLEAR) {
+		bzero(ptr, size);
+	}
+	return ptr;
+}
+
+static void *
+pgm_malloc_type_malloc_with_options(pgm_zone_t *zone, size_t align, size_t size,
+		malloc_zone_malloc_options_t options, malloc_type_id_t type_id)
+{
+	if (os_unlikely(align > PAGE_SIZE)) {
+		return DELEGATE(malloc_type_malloc_with_options, align, size, options,
+				type_id);
+	}
+	size_t adj_alignment = MAX(align, k_min_alignment);
+	SAMPLED_ALLOCATE(size, adj_alignment, malloc_type_malloc_with_options,
+			align, size, options, type_id);
+	if (options & MALLOC_NP_OPTION_CLEAR) {
+		bzero(ptr, size);
+	}
+	return ptr;
 }
 
 #pragma mark -
@@ -704,7 +758,8 @@
 			(zone->max_allocations <= zone->max_metadata / 2) &&  // choose_metadata() relies on max_allocations << max_metadata
 			(zone->max_metadata <= zone->num_slots) &&
 			(zone->num_slots <= k_max_slots) &&
-			(zone->sample_counter_range > 0);
+			(zone->sample_counter_range > 0) &&
+			(0 <= zone->left_align_pct && zone->left_align_pct <= 100);
 }
 
 static bool
@@ -983,7 +1038,8 @@
 #pragma mark -
 #pragma mark Zone Templates
 
-#define PGM_ZONE_VERSION 15
+#define PGM_ZONE_VERSION 16
+#define MIN_WRAPPED_ZONE_VERSION 16
 
 // Suppress warning: incompatible function pointer types
 #define FN_PTR(fn) (void *)(&fn)
@@ -1055,6 +1111,14 @@
 	.claimed_address = FN_PTR(pgm_claimed_address),
 	.try_free_default = NULL,
 	.malloc_with_options = FN_PTR(pgm_malloc_with_options),
+
+	// Typed entrypoints
+	.malloc_type_malloc = FN_PTR(pgm_malloc_type_malloc),
+	.malloc_type_calloc = FN_PTR(pgm_malloc_type_calloc),
+	.malloc_type_realloc = FN_PTR(pgm_malloc_type_realloc),
+	.malloc_type_memalign = FN_PTR(pgm_malloc_type_memalign),
+	.malloc_type_malloc_with_options =
+			FN_PTR(pgm_malloc_type_malloc_with_options),
 };
 
 
@@ -1095,6 +1159,7 @@
 	bool internal_build;
 	bool MallocProbGuard_is_set;
 	bool MallocProbGuard;
+	bool targeted_coverage;
 } g_env;
 
 void
@@ -1107,6 +1172,13 @@
 	if (env_var("MallocProbGuard")) {
 		g_env.MallocProbGuard_is_set = true;
 		g_env.MallocProbGuard = env_bool("MallocProbGuard");
+	}
+
+	const char *all_factors = env_var("__TRIFactors");
+	const char *pgm_factor =
+			"PROBABILISTIC_GUARD_MALLOC_TARGETED_COVERAGE:enable=1";
+	if (all_factors && strstr(all_factors, pgm_factor)) {
+		g_env.targeted_coverage = true;
 	}
 }
 
@@ -1124,6 +1196,11 @@
 static bool
 should_activate(bool internal_build)
 {
+#if CONFIG_MTE
+	if (malloc_has_sec_transition) {
+		return false;
+	}
+#endif
 	if (!internal_build && !is_platform_binary()) {
 		return false;
 	}
@@ -1139,42 +1216,47 @@
 
 #if TARGET_OS_WATCH || TARGET_OS_TV
 static bool
-is_high_memory_device(void)
-{
-	uint64_t high_memory = 1.2 * 1024 * 1024 * 1024;  // 1.2 GB
-	return platform_hw_memsize() > high_memory;
+is_low_memory_device(void)
+{
+	uint64_t low_memory = 1.2 * 1024 * 1024 * 1024;  // 1.2 GB
+	return platform_hw_memsize() <= low_memory;
 }
 #endif
-
-#define PGM_ALLOW_NON_INTERNAL_ACTIVATION 0
-
 
 bool
 pgm_should_enable(void)
 {
+	// Env var override
 	if (g_env.MallocProbGuard_is_set) {
 		return g_env.MallocProbGuard;
 	}
-	bool internal_build = g_env.internal_build;
-	if (FEATURE_FLAG(ProbGuard, true) && should_activate(internal_build)) {
-#if TARGET_OS_OSX || TARGET_OS_IOS
-		return true;
-#elif TARGET_OS_WATCH || TARGET_OS_TV
-		if (is_high_memory_device()) {
-			return true;
-		}
-#elif PGM_ALLOW_NON_INTERNAL_ACTIVATION
-		return true;
-#else
-		if (internal_build) {
-			return true;
-		}
-#endif
+
+	// Feature flags
+	if (!FEATURE_FLAG(ProbGuard, true)) {
+		return false;
 	}
 	if (FEATURE_FLAG(ProbGuardAllProcesses, false)) {
 		return true;
 	}
-	return false;
+
+	// Excluded configurations
+#if TARGET_OS_WATCH || TARGET_OS_TV
+	if (is_low_memory_device()) {
+		return false;
+	}
+#elif TARGET_OS_BRIDGE || TARGET_OS_DRIVERKIT
+	if (!g_env.internal_build) {
+		return false;
+	}
+#endif
+
+	// Targeted coverage via Trial
+	if (g_env.targeted_coverage) {
+		return true;
+	}
+
+	// Random activation
+	return should_activate(g_env.internal_build);
 }
 
 static uint32_t
@@ -1222,6 +1304,7 @@
 	uint32_t sample_rate = env_uint("MallocProbGuardSampleRate", choose_sample_rate());
 	// Approximate a (1 / sample_rate) chance for sampling; 1 means "always sample".
 	zone->sample_counter_range = (sample_rate != 1) ? (2 * sample_rate) : 1;
+	zone->left_align_pct = env_uint("MallocProbGuardLeftAlignPercentage", 10);
 	zone->debug = env_bool("MallocProbGuardDebug");
 	zone->debug_log_throttle_ms = env_uint("MallocProbGuardDebugLogThrottleInMillis", 1000);
 
@@ -1250,9 +1333,18 @@
 disable_unsupported_apis(malloc_zone_t *pgm_zone, const malloc_zone_t *wrapped_zone)
 {
 	#define DISABLE_UNSUPPORTED(api) if (!wrapped_zone->api) pgm_zone->api = NULL;
+
+	// In practice, there are no zones we support wrapping right now that don't
+	// have these entrypoints
 	DISABLE_UNSUPPORTED(memalign)
+	DISABLE_UNSUPPORTED(malloc_type_memalign)
 	DISABLE_UNSUPPORTED(free_definite_size)
 	DISABLE_UNSUPPORTED(claimed_address)
+
+	// These ones are actually load-bearing: the nano and scalable zones do not
+	// implement these entrypoints
+	DISABLE_UNSUPPORTED(malloc_with_options)
+	DISABLE_UNSUPPORTED(malloc_type_malloc_with_options)
 }
 
 static void
@@ -1286,12 +1378,7 @@
 malloc_zone_t *
 pgm_create_zone(malloc_zone_t *wrapped_zone)
 {
-	// The PGM zone includes the `malloc_introspection_t::zone_type` field
-	// (version 14) so we need to support the "malloc()/calloc() set errno to
-	// ENOMEM on failure" behavior (version 13).  Require wrapped zone to be at
-	// least version 13.
-	MALLOC_STATIC_ASSERT(PGM_ZONE_VERSION == 15, "PGM zone version");
-	MALLOC_ASSERT(wrapped_zone->version >= 13);
+	MALLOC_ASSERT(wrapped_zone->version >= MIN_WRAPPED_ZONE_VERSION);
 
 	pgm_zone_t *zone = (pgm_zone_t *)my_vm_map(sizeof(pgm_zone_t), VM_PROT_READ_WRITE, VM_MEMORY_MALLOC);
 	setup_zone(zone, wrapped_zone);