Loading...
--- libmalloc/libmalloc-116/src/malloc.c
+++ libmalloc/libmalloc-140.40.1/src/malloc.c
@@ -45,6 +45,17 @@
 
 #define USE_SLEEP_RATHER_THAN_ABORT 0
 
+/*
+	MAX_LITE_MALLOCS
+ 
+	If msl lite is turned on due to a memory resource exception use this value as the maximum
+	number of allocations allowed before msl lite is turned off. This prevents msl lite from being
+	enabled indefinitely if the process never reaches 100% of its jetsam limit.
+	See rdar://problem/25950426 for a discussion of how this number was determined.
+ */
+
+#define MAX_LITE_MALLOCS 100000000
+
 typedef void(malloc_logger_t)(uint32_t type,
 		uintptr_t arg1,
 		uintptr_t arg2,
@@ -91,12 +102,18 @@
 
 static const char Malloc_Facility[] = "com.apple.Libsystem.malloc";
 
+// Used by memory resource exceptions and enabling/disabling malloc stack logging via malloc_memory_event_handler
+static boolean_t warn_mode_entered = false;
+static boolean_t warn_mode_disable_retries = false;
+static stack_logging_mode_type msl_type_enabled_at_runtime = stack_logging_mode_none;
+
 /*
  * Counters that coordinate zone destruction (in malloc_zone_unregister) with
  * find_registered_zone (here abbreviated as FRZ).
  */
-static int counterAlice = 0, counterBob = 0;
-static int *pFRZCounterLive = &counterAlice, *pFRZCounterDrain = &counterBob;
+static int32_t volatile counterAlice = 0, counterBob = 0;
+static int32_t volatile * volatile pFRZCounterLive = &counterAlice;
+static int32_t volatile * volatile pFRZCounterDrain = &counterBob;
 
 static inline malloc_zone_t *inline_malloc_default_zone(void) __attribute__((always_inline));
 
@@ -109,12 +126,8 @@
 #define DEFAULT_PUREGEABLE_ZONE_STRING "DefaultPurgeableMallocZone"
 
 boolean_t malloc_engaged_nano(void);
-#if CONFIG_NANOZONE
-boolean_t _malloc_engaged_nano;
-#endif
 
 /*********	Utilities	************/
-__attribute__((visibility("hidden"))) uint64_t malloc_entropy[2] = {0, 0};
 static bool _malloc_entropy_initialized;
 
 void __malloc_init(const char *apple[]);
@@ -153,18 +166,13 @@
 void
 __malloc_init(const char *apple[])
 {
+#if CONFIG_NANOZONE
+	// TODO: envp should be passed down from Libsystem
+	const char **envp = (const char **)*_NSGetEnviron();
+	nano_init(envp, apple);
+#endif
+
 	const char **p;
-#if CONFIG_NANOZONE
-	_malloc_engaged_nano = 0;
-	for (p = apple; p && *p; p++) {
-		if (0 == strncmp(*p, "MallocNanoZone=1", strlen("MallocNanoZone=1"))) {
-			// _malloc_printf(ASL_LEVEL_INFO, "MallocNanoZone=1\n");
-			_malloc_engaged_nano = 1;
-			break;
-		}
-	}
-#endif
-
 	for (p = apple; p && *p; p++) {
 		if (strstr(*p, "malloc_entropy") == *p) {
 			int count = __entropy_from_kernel(*p);
@@ -180,6 +188,8 @@
 		getentropy((void*)malloc_entropy, sizeof(malloc_entropy));
 		_malloc_entropy_initialized = true;
 	}
+
+	mvm_aslr_init();
 }
 
 static malloc_zone_t* lite_zone = NULL;
@@ -320,7 +330,7 @@
 {
 	zone = runtime_default_zone();
 	
-	return zone->introspect->check(zone);
+	return (void)zone->introspect->check(zone);
 }
 
 static void
@@ -472,11 +482,11 @@
 		}
 	}
 
-	int *pFRZCounter = pFRZCounterLive;   // Capture pointer to the counter of the moment
-	__sync_fetch_and_add(pFRZCounter, 1); // Advance this counter -- our thread is in FRZ
+	int32_t volatile *pFRZCounter = pFRZCounterLive;   // Capture pointer to the counter of the moment
+	OSAtomicIncrement32Barrier(pFRZCounter); // Advance this counter -- our thread is in FRZ
 
 	unsigned index;
-	int32_t limit = *(volatile int32_t *)&malloc_num_zones;
+	int32_t limit = *(int32_t volatile *)&malloc_num_zones;
 	malloc_zone_t **zones = &malloc_zones[1];
 
 	// From this point on, FRZ is accessing the malloc_zones[] array without locking
@@ -498,19 +508,18 @@
 		zone = *zones;
 		size = zone->size(zone, ptr);
 		if (size) { // Claimed by this zone?
-			if (returned_size) {
-				*returned_size = size;
-			}
-			__sync_fetch_and_sub(pFRZCounter, 1); // our thread is leaving FRZ
-			return zone;
+			goto out;
 		}
 	}
 	// Unclaimed by any zone.
+	zone = NULL;
+	size = 0;
+out:
 	if (returned_size) {
-		*returned_size = 0;
-	}
-	__sync_fetch_and_sub(pFRZCounter, 1); // our thread is leaving FRZ
-	return NULL;
+		*returned_size = size;
+	}
+	OSAtomicDecrement32Barrier(pFRZCounter); // our thread is leaving FRZ
+	return zone;
 }
 
 void
@@ -601,7 +610,7 @@
 
 		/* Update the malloc_zones pointer, which we leak if it was previously
 		 * allocated, and the number of zones allocated */
-		protect_size = alloc_size;
+		protect_size = (size_t)alloc_size;
 		malloc_zones = new_zones;
 		malloc_num_zones_allocated = (int32_t)(alloc_size / sizeof(malloc_zone_t *));
 	} else {
@@ -616,11 +625,11 @@
 	 * in malloc_num_zones is visible then the pointer write before it must also be visible.
 	 *
 	 * While we could be slightly more efficent here with atomic ops the cleanest way to
-	 * ensure the proper store-release operation is performed is to use __sync... to update
-	 * malloc_num_zones.
+	 * ensure the proper store-release operation is performed is to use OSAtomic*Barrier
+	 * to update malloc_num_zones.
 	 */
 	malloc_zones[malloc_num_zones] = zone;
-	__sync_fetch_and_add(&malloc_num_zones, 1);
+	OSAtomicIncrement32Barrier(&malloc_num_zones);
 
 	/* Finally, now that the zone is registered, disallow write access to the
 	 * malloc_zones array */
@@ -689,12 +698,13 @@
 						malloc_printf("zone[0] is not the normal default zone so can't turn on lite mode.\n", mode);
 						ret = false;
 					} else {
-						malloc_printf("recording malloc (but not VM allocation) stacks using lite mode\n");
+						malloc_printf("recording malloc (and VM allocation) stacks using lite mode\n");
 						
 						if (lite_zone) {
 							enable_stack_logging_lite();
 						} else {
 							if (__prepare_to_log_stacks(true)) {
+								__syscall_logger = __disk_stack_logging_log_stack;
 								stack_logging_mode = stack_logging_mode_lite;
 								stack_logging_enable_logging = 1;
 								__prepare_to_log_stacks_stage2();
@@ -836,6 +846,7 @@
 			__syscall_logger = __disk_stack_logging_log_stack;
 			break;
 		case stack_logging_mode_lite:
+			__syscall_logger = __disk_stack_logging_log_stack;
 			create_and_insert_lite_zone_while_locked();
 			enable_stack_logging_lite();
 			break;
@@ -965,7 +976,7 @@
 		malloc_zone_t *tmp = create_purgeable_zone(0, inline_malloc_default_scalable_zone(), malloc_debug_flags);
 		malloc_zone_register(tmp);
 		malloc_set_zone_name(tmp, DEFAULT_PUREGEABLE_ZONE_STRING);
-		if (!__sync_bool_compare_and_swap(&dpz, NULL, tmp)) {
+		if (!OSAtomicCompareAndSwapPtrBarrier(NULL, tmp, (void**)&dpz)) {
 			malloc_destroy_zone(tmp);
 		}
 	}
@@ -1068,7 +1079,7 @@
 
 		if (strcmp(flag, "lite") == 0) {
 			stack_logging_mode = stack_logging_mode_lite;
-			_malloc_printf(ASL_LEVEL_INFO, "recording malloc (but not VM allocation) stacks using lite mode\n");
+			_malloc_printf(ASL_LEVEL_INFO, "recording malloc and VM allocation stacks using lite mode\n");
 		} else if (strcmp(flag,"malloc") == 0) {
 			stack_logging_mode = stack_logging_mode_malloc;
 			_malloc_printf(ASL_LEVEL_INFO, "recording malloc (but not VM allocation) stacks to disk using standard recorder\n");
@@ -1100,16 +1111,6 @@
 	if (getenv("MallocTracing")) {
 		malloc_tracing_enabled = true;
 	}
-#if CONFIG_NANOZONE
-	/* Explicit overrides from the environment */
-	if ((flag = getenv("MallocNanoZone"))) {
-		if (flag[0] == '1') {
-			_malloc_engaged_nano = 1;
-		} else if (flag[0] == '0') {
-			_malloc_engaged_nano = 0;
-		}
-	}
-#endif /* CONFIG_NANOZONE */
 
 #if __LP64__
 /* initialization above forces MALLOC_ABORT_ON_CORRUPTION of 64-bit processes */
@@ -1272,7 +1273,7 @@
 			if (b) {
 				_simple_sappend(b, "Stack for last operation where the malloc check succeeded: ");
 				while (index < num_frames)
-					_simple_sprintf(b, "%p ", frames[index++]);
+					_simple_sprintf(b, "%p ", (void*)frames[index++]);
 				malloc_printf("%s\n(Use 'atos' for a symbolic stack)\n", _simple_string(b));
 			} else {
 				/*
@@ -1518,10 +1519,10 @@
 		// Exchange the roles of the FRZ counters. The counter that has captured the number of threads presently
 		// executing *inside* find_regiatered_zone is swapped with the counter drained to zero last time through.
 		// The former is then allowed to drain to zero while this thread yields.
-		int *p = pFRZCounterLive;
+		int32_t volatile *p = pFRZCounterLive;
 		pFRZCounterLive = pFRZCounterDrain;
 		pFRZCounterDrain = p;
-		__sync_synchronize(); // Full memory barrier
+		OSMemoryBarrier(); // Full memory barrier
 
 		while (0 != *pFRZCounterDrain) {
 			yield();
@@ -1930,6 +1931,65 @@
 
 #endif
 
+static void
+handle_msl_memory_event(unsigned long event)
+{
+	// don't mix and match enabling mechanisms
+	if (warn_mode_entered) {
+		return;
+	}
+	
+	event &= NOTE_MEMORYSTATUS_MSL_STATUS;
+	
+	// sanity check
+	if (event == 0) {
+		return;
+	}
+	
+	// first check if the disable bit is set
+	if (event & MEMORYSTATUS_DISABLE_MSL) {
+		turn_off_stack_logging();
+		return;
+	}
+	
+	boolean_t msl_malloc = (event & MEMORYSTATUS_ENABLE_MSL_MALLOC);
+	boolean_t msl_vm = (event & MEMORYSTATUS_ENABLE_MSL_VM);
+	boolean_t msl_lite = (event & MEMORYSTATUS_ENABLE_MSL_LITE);
+	
+	// The following always checks to make it's not possible to enable two different modes
+	// For instance this would not be allowed:
+	// Enable lite
+	// Disable
+	// Enable full
+	
+	// Currently there is no separation of malloc/vm in lite mode
+	if (msl_lite) {
+		if (msl_type_enabled_at_runtime == stack_logging_mode_none || msl_type_enabled_at_runtime == stack_logging_mode_lite) {
+			msl_type_enabled_at_runtime = stack_logging_mode_lite;
+			turn_on_stack_logging(stack_logging_mode_lite);
+		}
+		return;
+	} else if (msl_malloc && msl_vm) {
+		if (msl_type_enabled_at_runtime == stack_logging_mode_none || msl_type_enabled_at_runtime == stack_logging_mode_all) {
+			msl_type_enabled_at_runtime = stack_logging_mode_all;
+			turn_on_stack_logging(stack_logging_mode_all);
+		}
+		return;
+	} else if (msl_malloc) {
+		if (msl_type_enabled_at_runtime == stack_logging_mode_none || msl_type_enabled_at_runtime == stack_logging_mode_malloc) {
+			msl_type_enabled_at_runtime = stack_logging_mode_malloc;
+			turn_on_stack_logging(stack_logging_mode_malloc);
+		}
+		return;
+	} else if (msl_vm) {
+		if (msl_type_enabled_at_runtime == stack_logging_mode_none || msl_type_enabled_at_runtime == stack_logging_mode_vm) {
+			msl_type_enabled_at_runtime = stack_logging_mode_vm;
+			turn_on_stack_logging(stack_logging_mode_vm);
+		}
+		return;
+	}
+}
+
 // Note that malloc_memory_event_handler is not thread-safe, and we are relying on the callers of this for synchronization
 void
 malloc_memory_event_handler(unsigned long event)
@@ -1937,11 +1997,15 @@
 	if (event & NOTE_MEMORYSTATUS_PRESSURE_WARN) {
 		malloc_zone_pressure_relief(0, 0);
 	}
+	
+	// First check for enable/disable MSL - only recognize if all other bits are 0
+	// Don't attempt this if we've either entered or exited MRE mode
+	if ((event & NOTE_MEMORYSTATUS_MSL_STATUS) != 0 && (event & ~NOTE_MEMORYSTATUS_MSL_STATUS) == 0 && !warn_mode_entered && !warn_mode_disable_retries) {
+		handle_msl_memory_event(event);
+		return;
+	}
 
 #if ENABLE_MEMORY_RESOURCE_EXCEPTION_HANDLING
-	static boolean_t warn_mode_entered = false;
-	static boolean_t warn_mode_disable_retries = false;
-
 	// If we have reached EXC_RESOURCE, we no longer need stack log data.
 	// If we are under system-wide memory pressure, we should jettison stack log data.
 	if ((event & (NOTE_MEMORYSTATUS_PROC_LIMIT_CRITICAL | NOTE_MEMORYSTATUS_PRESSURE_CRITICAL)) &&
@@ -1973,6 +2037,9 @@
 		malloc_printf("malloc_memory_event_handler: approaching memory limit. Starting stack-logging.\n");
 		if (turn_on_stack_logging(stack_logging_mode_lite)) {
 			warn_mode_entered = true;
+			
+			// set the maximum allocation threshold
+			max_lite_mallocs = MAX_LITE_MALLOCS;
 		}
 	}
 #endif