Loading...
--- libmalloc/libmalloc-140.40.1/src/magazine_inline.h
+++ libmalloc/libmalloc-317.121.1/src/magazine_inline.h
@@ -24,6 +24,42 @@
 #ifndef __MAGAZINE_INLINE_H
 #define __MAGAZINE_INLINE_H
 
+extern unsigned int _os_cpu_number_override;
+
+/*
+ * MALLOC_ABSOLUTE_MAX_SIZE - There are many instances of addition to a
+ * user-specified size_t, which can cause overflow (and subsequent crashes)
+ * for values near SIZE_T_MAX.  Rather than add extra "if" checks everywhere
+ * this occurs, it is easier to just set an absolute maximum request size,
+ * and immediately return an error if the requested size exceeds this maximum.
+ * Of course, values less than this absolute max can fail later if the value
+ * is still too large for the available memory.  The largest value added
+ * seems to be large_vm_page_quanta_size (in the macro round_large_page_quanta()), so to be safe, we set
+ * the maximum to be 2 * PAGE_SIZE less than SIZE_T_MAX.
+ */
+#define MALLOC_ABSOLUTE_MAX_SIZE (SIZE_T_MAX - (2 * large_vm_page_quanta_size))
+
+// Gets the allocation size for a calloc(). Multiples size by num_items and adds
+// extra_size, storing the result in *total_size. Returns 0 on success, -1 (with
+// errno set to ENOMEM) on overflow.
+static int MALLOC_INLINE MALLOC_ALWAYS_INLINE
+calloc_get_size(size_t num_items, size_t size, size_t extra_size, size_t *total_size)
+{
+	size_t alloc_size = size;
+	if (num_items != 1 && (os_mul_overflow(num_items, size, &alloc_size)
+			|| alloc_size > MALLOC_ABSOLUTE_MAX_SIZE)) {
+		errno = ENOMEM;
+		return -1;
+	}
+	if (extra_size && (os_add_overflow(alloc_size, extra_size, &alloc_size)
+			|| alloc_size > MALLOC_ABSOLUTE_MAX_SIZE)) {
+		errno = ENOMEM;
+		return -1;
+	}
+	*total_size = alloc_size;
+	return 0;
+}
+
 /*********************	FREE LIST UTILITIES  ************************/
 
 // A free list entry is comprised of a pair of pointers, previous and next.
@@ -51,7 +87,9 @@
 static MALLOC_INLINE uintptr_t free_list_gen_checksum(uintptr_t ptr) MALLOC_ALWAYS_INLINE;
 static MALLOC_INLINE uintptr_t free_list_checksum_ptr(rack_t *rack, void *p) MALLOC_ALWAYS_INLINE;
 static MALLOC_INLINE void *free_list_unchecksum_ptr(rack_t *rack, inplace_union *ptr) MALLOC_ALWAYS_INLINE;
-static MALLOC_INLINE unsigned free_list_count(rack_t *rack, free_list_t ptr);
+static MALLOC_INLINE unsigned free_list_count(task_t task,
+		memory_reader_t reader, print_task_printer_t printer,
+		rack_t *mapped_rack, free_list_t ptr);
 
 static MALLOC_INLINE void recirc_list_extract(rack_t *rack, magazine_t *mag_ptr, region_trailer_t *node) MALLOC_ALWAYS_INLINE;
 static MALLOC_INLINE void recirc_list_splice_last(rack_t *rack, magazine_t *mag_ptr, region_trailer_t *node) MALLOC_ALWAYS_INLINE;
@@ -64,7 +102,7 @@
 }
 
 static MALLOC_INLINE kern_return_t
-_szone_default_reader(task_t task, vm_address_t address, vm_size_t size, void **ptr)
+_malloc_default_reader(task_t task, vm_address_t address, vm_size_t size, void **ptr)
 {
 	*ptr = (void *)address;
 	return 0;
@@ -151,12 +189,12 @@
 #pragma mark free list
 
 static MALLOC_NOINLINE void
-free_list_checksum_botch(rack_t *rack, void *ptr)
-{
-	szone_error(rack->debug_flags, 1,
-				"incorrect checksum for freed object "
-				"- object was probably modified after being freed.",
-				ptr, NULL);
+free_list_checksum_botch(rack_t *rack, void *ptr, void *value)
+{
+	malloc_zone_error(rack->debug_flags, true,
+			"Incorrect checksum for freed object %p: "
+			"probably modified after being freed.\n"
+			"Corrupt value: %p\n", ptr, value);
 }
 
 static MALLOC_INLINE uintptr_t
@@ -175,17 +213,25 @@
 	chk += (unsigned char)(ptr >> 56);
 #endif
 
-	return chk & (uintptr_t)0xF;
+	return chk;
 }
 
 static unsigned
-free_list_count(rack_t *rack, free_list_t ptr)
-{
-	unsigned count = 0;
-
+free_list_count(task_t task, memory_reader_t reader,
+		print_task_printer_t printer, rack_t *mapped_rack, free_list_t ptr)
+{
+	unsigned int count = 0;
+
+	// ptr.p is always pointer in the *target* process address space.
+	inplace_free_entry_t mapped_inplace_free_entry;
 	while (ptr.p) {
 		count++;
-		ptr.p = free_list_unchecksum_ptr(rack, &ptr.inplace->next);
+		if (reader(task, (vm_address_t)ptr.inplace, sizeof(*ptr.inplace),
+				(void **)&mapped_inplace_free_entry)) {
+			printer("** invalid pointer in free list: %p\n", ptr.inplace);
+			break;
+		}
+		ptr.p = free_list_unchecksum_ptr(mapped_rack, &mapped_inplace_free_entry->next);
 	}
 	return count;
 }
@@ -201,7 +247,7 @@
 free_list_checksum_ptr(rack_t *rack, void *ptr)
 {
 	uintptr_t p = (uintptr_t)ptr;
-	return (p >> NYBBLE) | (free_list_gen_checksum(p ^ rack->cookie) << ANTI_NYBBLE); // compiles to rotate instruction
+	return (p >> NYBBLE) | ((free_list_gen_checksum(p ^ rack->cookie) & (uintptr_t)0xF) << ANTI_NYBBLE); // compiles to rotate instruction
 }
 
 static MALLOC_INLINE void *
@@ -213,8 +259,8 @@
 	t = (t << NYBBLE) | (t >> ANTI_NYBBLE); // compiles to rotate instruction
 	p.u = t & ~(uintptr_t)0xF;
 
-	if ((t & (uintptr_t)0xF) != free_list_gen_checksum(p.u ^ rack->cookie)) {
-		free_list_checksum_botch(rack, ptr);
+	if ((t ^ free_list_gen_checksum(p.u ^ rack->cookie)) & (uintptr_t)0xF) {
+		free_list_checksum_botch(rack, ptr, (void *)ptr->u);
 		__builtin_trap();
 	}
 	return p.p;
@@ -241,6 +287,7 @@
 		node->next->prev = node->prev;
 	}
 
+	node->next = node->prev = NULL;
 	mag_ptr->recirculation_entries--;
 }
 
@@ -363,7 +410,7 @@
 hash_regions_alloc_no_lock(size_t num_entries)
 {
 	size_t size = num_entries * sizeof(region_t);
-	return mvm_allocate_pages(round_page_quanta(size), 0, 0, VM_MEMORY_MALLOC);
+	return mvm_allocate_pages(round_page_quanta(size), 0, DISABLE_ASLR, VM_MEMORY_MALLOC);
 }
 
 /*
@@ -390,19 +437,32 @@
 	return new_regions;
 }
 
-#pragma mark mag lock
+#pragma mark mag index
 
 /*
  * These commpage routines provide fast access to the logical cpu number
  * of the calling processor assuming no pre-emption occurs.
  */
 
+extern unsigned int hyper_shift;
+extern unsigned int phys_ncpus;
+extern unsigned int logical_ncpus;
+
 static MALLOC_INLINE MALLOC_ALWAYS_INLINE
-mag_index_t
-mag_get_thread_index(void)
-{
-	return _os_cpu_number() & (TINY_MAX_MAGAZINES - 1);
-}
+unsigned int
+mag_max_magazines(void)
+{
+	return max_magazines;
+}
+
+static MALLOC_INLINE MALLOC_ALWAYS_INLINE
+unsigned int
+mag_max_medium_magazines(void)
+{
+	return max_medium_magazines;
+}
+
+#pragma mark mag lock
 
 static MALLOC_INLINE magazine_t *
 mag_lock_zine_for_region_trailer(magazine_t *magazines, region_trailer_t *trailer, mag_index_t mag_index)
@@ -428,6 +488,34 @@
 	return mag_ptr;
 }
 
+#pragma mark Region Cookie
+
+extern uint64_t malloc_entropy[2];
+
+static region_cookie_t
+region_cookie(void)
+{
+	return (region_cookie_t)(malloc_entropy[0] >> 8) & 0xffff;
+}
+
+static MALLOC_INLINE void
+region_check_cookie(region_t region, region_cookie_t *cookiep)
+{
+	if (*cookiep != region_cookie())
+	{
+		malloc_zone_error(MALLOC_ABORT_ON_ERROR, true,
+				"Region cookie corrupted for region %p (value is %x)[%p]\n",
+				region, *cookiep, cookiep);
+		__builtin_unreachable();
+	}
+}
+
+static MALLOC_INLINE void
+region_set_cookie(region_cookie_t *cookiep)
+{
+	*cookiep = region_cookie();
+}
+
 #pragma mark tiny allocator
 
 /*
@@ -448,34 +536,42 @@
 /*
  * Obtain the size of a free tiny block (in msize_t units).
  */
-static msize_t
-get_tiny_free_size(const void *ptr)
+static MALLOC_INLINE msize_t
+get_tiny_free_size_offset(const void *ptr, off_t mapped_offset)
 {
 	void *next_block = (void *)((uintptr_t)ptr + TINY_QUANTUM);
-	void *region_end = TINY_REGION_END(TINY_REGION_FOR_PTR(ptr));
+	void *region_end = TINY_REGION_HEAP_END(TINY_REGION_FOR_PTR(ptr));
 
 	// check whether the next block is outside the tiny region or a block header
 	// if so, then the size of this block is one, and there is no stored size.
 	if (next_block < region_end) {
-		uint32_t *next_header = TINY_BLOCK_HEADER_FOR_PTR(next_block);
+		uint32_t *next_header = (uint32_t *)
+				((char *)TINY_BLOCK_HEADER_FOR_PTR(next_block) + mapped_offset);
 		msize_t next_index = TINY_INDEX_FOR_PTR(next_block);
 
 		if (!BITARRAY_BIT(next_header, next_index)) {
-			return TINY_FREE_SIZE(ptr);
+			return TINY_FREE_SIZE((uintptr_t)ptr + mapped_offset);
 		}
 	}
 	return 1;
 }
 
 static MALLOC_INLINE msize_t
-get_tiny_meta_header(const void *ptr, boolean_t *is_free)
+get_tiny_free_size(const void *ptr)
+{
+	return get_tiny_free_size_offset(ptr, 0);
+}
+
+static MALLOC_INLINE msize_t
+get_tiny_meta_header_offset(const void *ptr, off_t mapped_offset,
+		boolean_t *is_free)
 {
 	// returns msize and is_free
 	// may return 0 for the msize component (meaning 65536)
 	uint32_t *block_header;
 	msize_t index;
 
-	block_header = TINY_BLOCK_HEADER_FOR_PTR(ptr);
+	block_header = (uint32_t *)((char *)TINY_BLOCK_HEADER_FOR_PTR(ptr) + mapped_offset);
 	index = TINY_INDEX_FOR_PTR(ptr);
 
 	msize_t midx = (index >> 5) << 1;
@@ -486,14 +582,14 @@
 	}
 	if (0 == (block_header[midx + 1] & mask)) { // if (!BITARRAY_BIT(in_use, index))
 		*is_free = 1;
-		return get_tiny_free_size(ptr);
+		return get_tiny_free_size_offset(ptr, mapped_offset);
 	}
 
 	// index >> 5 identifies the uint32_t to manipulate in the conceptually contiguous bits array
 	// (index >> 5) << 1 identifies the uint32_t allowing for the actual interleaving
 #if defined(__LP64__)
 	// The return value, msize, is computed as the distance to the next 1 bit in block_header.
-	// That's guaranteed to be somewhwere in the next 64 bits. And those bits could span three
+	// That's guaranteed to be somewhere in the next 64 bits. And those bits could span three
 	// uint32_t block_header elements. Collect the bits into a single uint64_t and measure up with ffsl.
 	uint32_t *addr = ((uint32_t *)block_header) + ((index >> 5) << 1);
 	uint32_t bitidx = index & 31;
@@ -505,7 +601,7 @@
 	uint32_t result = __builtin_ffsl(word >> 1);
 #else
 	// The return value, msize, is computed as the distance to the next 1 bit in block_header.
-	// That's guaranteed to be somwhwere in the next 32 bits. And those bits could span two
+	// That's guaranteed to be somewhere in the next 32 bits. And those bits could span two
 	// uint32_t block_header elements. Collect the bits into a single uint32_t and measure up with ffs.
 	uint32_t *addr = ((uint32_t *)block_header) + ((index >> 5) << 1);
 	uint32_t bitidx = index & 31;
@@ -515,6 +611,39 @@
 	return result;
 }
 
+static MALLOC_INLINE msize_t
+get_tiny_meta_header(const void *ptr, boolean_t *is_free)
+{
+	return get_tiny_meta_header_offset(ptr, 0, is_free);
+}
+
+#if CONFIG_RECIRC_DEPOT
+/**
+ * Returns true if a tiny region is below the emptiness threshold that allows it
+ * to be moved to the recirc depot.
+ */
+static MALLOC_INLINE boolean_t
+tiny_region_below_recirc_threshold(region_t region)
+{
+	region_trailer_t *trailer = REGION_TRAILER_FOR_TINY_REGION(region);
+	return trailer->bytes_used < DENSITY_THRESHOLD(TINY_HEAP_SIZE);
+}
+
+/**
+ * Returns true if a tiny magazine has crossed the emptiness threshold that
+ * allows regions to be moved to the recirc depot.
+ */
+static MALLOC_INLINE boolean_t
+tiny_magazine_below_recirc_threshold(magazine_t *mag_ptr)
+{
+	size_t a = mag_ptr->num_bytes_in_magazine;	// Total bytes allocated to this magazine
+	size_t u = mag_ptr->mag_num_bytes_in_objects; // In use (malloc'd) from this magaqzine
+
+	return a - u > ((3 * TINY_HEAP_SIZE) / 2)
+			&& u < DENSITY_THRESHOLD(a);
+}
+#endif // CONFIG_RECIRC_DEPOT
+
 #pragma mark small allocator
 
 /*
@@ -530,4 +659,55 @@
 	return r ? *r : r;
 }
 
+#if CONFIG_RECIRC_DEPOT
+/**
+ * Returns true if a small region is below the emptiness threshold that allows
+ * it to be moved to the recirc depot.
+ */
+static MALLOC_INLINE boolean_t
+small_region_below_recirc_threshold(region_t region)
+{
+	region_trailer_t *trailer = REGION_TRAILER_FOR_SMALL_REGION(region);
+	return trailer->bytes_used < DENSITY_THRESHOLD(SMALL_HEAP_SIZE);
+}
+
+/**
+ * Returns true if a small magazine has crossed the emptiness threshold that
+ * allows regions to be moved to the recirc depot.
+ */
+static MALLOC_INLINE boolean_t
+small_magazine_below_recirc_threshold(magazine_t *mag_ptr)
+{
+	size_t a = mag_ptr->num_bytes_in_magazine;	// Total bytes allocated to this magazine
+	size_t u = mag_ptr->mag_num_bytes_in_objects; // In use (malloc'd) from this magaqzine
+
+	return a - u > ((3 * SMALL_HEAP_SIZE) / 2) && u < DENSITY_THRESHOLD(a);
+}
+#endif // CONFIG_RECIRC_DEPOT
+
+#pragma mark medium allocator
+/**
+ * Returns true if a small region is below the emptiness threshold that allows
+ * it to be moved to the recirc depot.
+ */
+static MALLOC_INLINE boolean_t
+medium_region_below_recirc_threshold(region_t region)
+{
+	region_trailer_t *trailer = REGION_TRAILER_FOR_MEDIUM_REGION(region);
+	return trailer->bytes_used < DENSITY_THRESHOLD(MEDIUM_REGION_PAYLOAD_BYTES);
+}
+
+/*
+ * medium_region_for_ptr_no_lock - Returns the medium region containing the pointer,
+ * or NULL if not found.
+ */
+static MALLOC_INLINE region_t
+medium_region_for_ptr_no_lock(rack_t *rack, const void *ptr)
+{
+	rgnhdl_t r = hash_lookup_region_no_lock(rack->region_generation->hashed_regions,
+			rack->region_generation->num_regions_allocated, rack->region_generation->num_regions_allocated_shift,
+			MEDIUM_REGION_FOR_PTR(ptr));
+	return r ? *r : r;
+}
+
 #endif // __MAGAZINE_INLINE_H