+ /* Expand the zone allocation size to include the redzones. For page-multiple
+ * zones add a full guard page because they likely require alignment. kalloc
+ * and fakestack handles its own KASan state, so ignore those zones. */
+ /* XXX: remove this when zinit_with_options() is a thing */
+ const char *kalloc_name = "kalloc.";
+ const char *fakestack_name = "fakestack.";
+ if (strncmp(name, kalloc_name, strlen(kalloc_name)) == 0) {
+ zone->kasan_redzone = 0;
+ } else if (strncmp(name, fakestack_name, strlen(fakestack_name)) == 0) {
+ zone->kasan_redzone = 0;
+ } else {
+ if ((*size % PAGE_SIZE) != 0) {
+ zone->kasan_redzone = KASAN_GUARD_SIZE;
+ } else {
+ zone->kasan_redzone = PAGE_SIZE;
+ }
+ *max = (*max / *size) * (*size + zone->kasan_redzone * 2);
+ *size += zone->kasan_redzone * 2;
+ }
+}
+
+/*
+ * Called from zalloc_internal() to fix up the address of the newly
+ * allocated element.
+ *
+ * Returns the element address skipping over the redzone on the left.
+ */
+static vm_offset_t
+kasan_fixup_allocated_element_address(
+ zone_t zone, /* the zone the element belongs to */
+ vm_offset_t addr) /* address of the element, including the redzone */
+{
+ /* Fixup the return address to skip the redzone */
+ if (zone->kasan_redzone) {
+ addr = kasan_alloc(addr, zone->elem_size,
+ zone->elem_size - 2 * zone->kasan_redzone, zone->kasan_redzone);
+ }
+ return addr;
+}
+
+/*
+ * Called from zfree() to add the element being freed to the KASan quarantine.
+ *
+ * Returns true if the newly-freed element made it into the quarantine without
+ * displacing another, false otherwise. In the latter case, addrp points to the
+ * address of the displaced element, which will be freed by the zone.
+ */
+static bool
+kasan_quarantine_freed_element(
+ zone_t *zonep, /* the zone the element is being freed to */
+ void **addrp) /* address of the element being freed */
+{
+ zone_t zone = *zonep;
+ void *addr = *addrp;
+
+ /*
+ * Resize back to the real allocation size and hand off to the KASan
+ * quarantine. `addr` may then point to a different allocation, if the
+ * current element replaced another in the quarantine. The zone then
+ * takes ownership of the swapped out free element.
+ */
+ vm_size_t usersz = zone->elem_size - 2 * zone->kasan_redzone;
+ vm_size_t sz = usersz;
+
+ if (addr && zone->kasan_redzone) {
+ kasan_check_free((vm_address_t)addr, usersz, KASAN_HEAP_ZALLOC);
+ addr = (void *)kasan_dealloc((vm_address_t)addr, &sz);
+ assert(sz == zone->elem_size);
+ }
+ if (addr && zone->kasan_quarantine) {
+ kasan_free(&addr, &sz, KASAN_HEAP_ZALLOC, zonep, usersz, true);
+ if (!addr) {
+ return TRUE;
+ }
+ }
+ *addrp = addr;
+ return FALSE;
+}
+
+#endif /* KASAN_ZALLOC */
+
+/*
+ * zinit initializes a new zone. The zone data structures themselves
+ * are stored in a zone, which is initially a static structure that
+ * is initialized by zone_init.
+ */
+
+zone_t
+zinit(
+ vm_size_t size, /* the size of an element */
+ vm_size_t max, /* maximum memory to use */
+ vm_size_t alloc, /* allocation size */
+ const char *name) /* a name for the zone */
+{
+ zone_t z;