Loading...
tests/scribble_tests.c /dev/null libmalloc-715.120.13
--- /dev/null
+++ libmalloc/libmalloc-715.120.13/tests/scribble_tests.c
@@ -0,0 +1,146 @@
+//
+//  scribble_tests.c
+//  libsystem_malloc
+//
+//  Created by Aaron Morrison on 7/14/23.
+//
+#include <stdlib.h>
+#include <darwintest.h>
+
+#include <malloc_private.h>
+
+#include "base.h"
+
+#define SCRIBBLE_ALLOC_BYTE 0xaa
+
+static bool memchk(void *ptr, uint8_t byte, size_t size)
+{
+	for (int i = 0; i < size; i++) {
+		if (((uint8_t*)ptr)[i] != byte) {
+			T_LOG("memchk: offset 0x%x in %p - expected 0x%02x, actual 0x%02x",
+					i, ptr, byte, ((uint8_t*)ptr)[i]);
+			return false;
+		}
+	}
+	return true;
+}
+
+// note that scribble seems to break leaks for szone malloc
+T_DECL(malloc_scribble_check, "check MallocScribble works",
+	T_META_TAG_XZONE,
+	T_META_ENVVAR("MallocScribble=1"),
+	T_META_ENVVAR("MallocProbGuard=0"),  // rdar://121458833
+	T_META_CHECK_LEAKS(false))
+{
+	// Ensure that TINY allocations are scribbled on allocation
+	// It would be nice to check that they're set to the scrabble value (0x55)
+	// on free, but we can't rely on that always working in an integration test
+	void *ptr = malloc(KiB(1));
+	T_EXPECT_TRUE(memchk(ptr, SCRIBBLE_ALLOC_BYTE, KiB(1)), "Scribble on malloc");
+	free(ptr);
+
+	// Realloc a TINY allocation into a SMALL allocation. The newly allocated
+	// bytes should be set to the scribble value, while the old bytes should
+	// stay at whatever we set them to (0x33 in this case)
+	ptr = malloc(KiB(4));
+	memset(ptr, 0x33, KiB(4));
+	ptr = realloc(ptr, KiB(8));
+	T_EXPECT_TRUE(memchk(ptr, 0x33, KiB(4)), "Memory rewritten on realloc");
+	T_EXPECT_TRUE(memchk((uint8_t*)ptr + KiB(4), SCRIBBLE_ALLOC_BYTE, KiB(4)),
+			"Scribble on realloc");
+	ptr = realloc(ptr, KiB(2));
+	T_EXPECT_TRUE(memchk(ptr, 0x33, KiB(2)), "realloc down");
+
+	free(ptr);
+
+	// Now test out LARGE (>32K) pointers
+	ptr = malloc(KiB(64));
+	T_EXPECT_TRUE(memchk(ptr, SCRIBBLE_ALLOC_BYTE, KiB(64)),
+			"Scribble on LARGE allocation");
+	free(ptr);
+
+	// Realloc a LARGE allocation, which may be done in-place. The new bytes
+	// should be the scribble value
+	ptr = malloc(KiB(64));
+	memset(ptr, 0x33, KiB(64));
+	ptr = realloc(ptr, KiB(128));
+	T_EXPECT_TRUE(memchk(ptr, 0x33, KiB(64)), "Memory retained on realloc");
+	T_EXPECT_TRUE(memchk((uint8_t*)ptr + KiB(64), SCRIBBLE_ALLOC_BYTE, KiB(64)),
+			 "Scribble on realloc");
+	ptr = realloc(ptr, KiB(48));
+	T_EXPECT_TRUE(memchk(ptr, 0x33, KiB(48)), "realloc down");
+	free(ptr);
+
+	// Now test out HUGE (>2M) pointers
+	ptr = malloc(MiB(4)); // 4M
+	T_EXPECT_TRUE(memchk(ptr, SCRIBBLE_ALLOC_BYTE, MiB(4)),
+			"Scribble on malloc HUGE");
+	free(ptr);
+
+	// Realloc HUGE allocation, same as above
+	ptr = malloc(MiB(4));
+	memset(ptr, 0x33, MiB(4));
+	ptr = realloc(ptr, MiB(8));
+	T_EXPECT_TRUE(memchk(ptr, 0x33, MiB(4)), "Memory retained on realloc");
+	T_EXPECT_TRUE(memchk((uint8_t*)ptr + MiB(4), SCRIBBLE_ALLOC_BYTE, MiB(4)),
+			"Scribble on realloc");
+	ptr = realloc(ptr, MiB(3));
+	T_EXPECT_TRUE(memchk(ptr, 0x33, MiB(3)), "realloc down");
+	free(ptr);
+
+	// Exhaust the early allocator and make sure that new allocations are still
+	// scribbled
+	for (int i = 0; i < 64; i++) {
+		ptr = malloc(64);
+		free(ptr);
+	}
+	ptr = malloc(64);
+	T_EXPECT_TRUE(memchk(ptr, SCRIBBLE_ALLOC_BYTE, 64), "Scribble on malloc");
+	free(ptr);
+
+	// Make sure that memory returned by calloc is zeroed, for all size classes
+	for (uint64_t s = 1; s < 0x4000000; s <<= 1) {
+		ptr = calloc(s, 1);
+		T_EXPECT_TRUE(memchk(ptr, 0, s), "Calloc should return zeroed memory");
+		free(ptr);
+	}
+
+	// Make sure memory returned by malloc_zone_malloc_with_options_np() is
+	// correct
+	ptr = malloc_zone_malloc_with_options_np(NULL, sizeof(void *), KiB(1), 0);
+	T_EXPECT_TRUE(memchk(ptr, SCRIBBLE_ALLOC_BYTE, KiB(1)),
+			"malloc_zone_malloc_with_options_np()");
+	free(ptr);
+
+	ptr = malloc_zone_malloc_with_options_np(NULL, sizeof(void *), KiB(1),
+			MALLOC_NP_OPTION_CLEAR);
+	T_EXPECT_TRUE(memchk(ptr, 0, KiB(1)),
+			"malloc_zone_malloc_with_options_np(MALLOC_NP_OPTION_CLEAR)");
+	free(ptr);
+
+	// Allocate and free many allocations smaller than the zero on free
+	// threshold (1024), in order to push several pages into the isolation zone.
+	// Then calloc those same allocations, to make sure that the memory is zeroed
+	const size_t num_ptrs = 256;
+	void **ptr_array = calloc(sizeof(void*), num_ptrs);
+	for (int i = 0; i < num_ptrs; i++) {
+		ptr_array[i] = malloc(512);
+		T_QUIET;
+		T_EXPECT_TRUE(memchk(ptr_array[i], SCRIBBLE_ALLOC_BYTE, 512),
+				"Scribble on malloc");
+	}
+	for (int i = 0; i < num_ptrs; i++) {
+		free(ptr_array[i]);
+		ptr_array[i] = NULL;
+	}
+	for (int i = 0; i < num_ptrs; i++) {
+		ptr_array[i] = calloc(512, 1);
+		T_QUIET;
+		T_EXPECT_TRUE(memchk(ptr_array[i], 0x00, 512),
+				"Calloc should return zeroed memory");
+	}
+	for (int i = 0; i < num_ptrs; i++) {
+		free(ptr_array[i]);
+		ptr_array[i] = NULL;
+	}
+}