From fa101829009c3fdbb0f9c5881584613053eadcf1 Mon Sep 17 00:00:00 2001 From: Mikey Sklar Date: Sat, 22 Aug 2026 18:56:59 -0700 Subject: [PATCH] zephyr-cp: don't build the heap control structure in an undersized region tlsf_create_with_pool() forwards its max_bytes argument to tlsf_create(), so control_construct() dimensions the control structure from the maximum heap size and checks that maximum against itself, not against the region it is writing into. A region too small for the structure is overrun rather than rejected. On a SiWx917-DK2605A the port skips the large SRAM region because Zephyr's malloc arena owns it (CONFIG_COMMON_LIBC_MALLOC with ARENA_SIZE=-1), leaving a 1 KB DMA buffer as the first candidate. With circuitpy_max_ram_size of 8 MB the control structure is 2412 bytes, so it overran that region by 1388 bytes. Require the region that hosts the control structure to be at least 8 KB, and handle tlsf_create_with_pool() returning NULL by moving to the next region instead of leaving heap NULL for the first allocation to trip over. The bound applies only to the region hosting the control structure. Region order is unchanged and smaller regions are still eligible as additional pools, so boards whose first region already works are unaffected. --- ports/zephyr-cp/supervisor/port.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 1ecde0b0141..a46ce7c68f6 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -43,6 +43,18 @@ static tlsf_t heap; static size_t tlsf_heap_used = 0; +// TLSF writes its control structure into the first pool it is given, but +// dimensions that structure from the maximum heap size rather than from the +// region itself: tlsf_create_with_pool() forwards max_bytes to tlsf_create(), +// so both of its size checks test the maximum, not the space available. A +// region too small for the structure is therefore overrun rather than +// rejected. On a SiWx917-DK2605A with an 8 MB maximum the structure is 2412 +// bytes and landed in a 1 KB region, overrunning it by 1388 bytes. +// +// Only the region hosting the control structure has to satisfy this; smaller +// regions are still fine as additional pools. +#define MIN_FIRST_POOL_SIZE (8 * 1024) + // Auto generated in pins.c extern const struct device *const rams[]; extern const uint32_t *const ram_bounds[]; @@ -290,10 +302,23 @@ void port_heap_init(void) { } #endif + if (valid_pool_count == 0 && size < MIN_FIRST_POOL_SIZE) { + // Too small to hold the control structure. Leave it for a later + // pass rather than letting TLSF overrun it. + printk("Skipping region at %p: too small to host the heap control structure\n", heap_bottom); + pools[i] = NULL; + continue; + } + printk("Init heap at %p - %p with size %d\n", heap_bottom, heap_top, size); // If this crashes, then make sure you've enabled all of the Kconfig needed for the drivers. if (valid_pool_count == 0) { heap = tlsf_create_with_pool(heap_bottom, size, circuitpy_max_ram_size); + if (heap == NULL) { + printk("Heap creation failed at %p; trying the next region\n", heap_bottom); + pools[i] = NULL; + continue; + } pools[i] = tlsf_get_pool(heap); } else { pools[i] = tlsf_add_pool(heap, heap_bottom + 1, size - sizeof(uint32_t));