Skip to content

W5500 RX path corrupts a heap neighbour: LoadProhibited in w5500_tsk when an extra internal-DRAM allocation lands next to the emac #146

Description

@iliabaranov

Symptom

HIL DUT 01d7f344 (Ethernet primary, W5500 over SPI, IDF 5.5.4): LoadProhibited in w5500_tsk ~150 s after boot, reproducible every boot, on any image where one extra ~4.3 KB internal-DRAM allocation is made at tether start (a FreeRTOS TCB + 4 KB stack). Same code with that allocation moved to PSRAM: no crash in 360 s+. main alone: no crash. Bisected in #135 (three builds); details in that PR body.

Coredump (decoded against the crashing ELF, v1.2-21-gb467f85)

exccause 0x1c LoadProhibited, excvaddr 0x0
#1 uninstall_priv_desc (spi_master.c:1176)  memcpy(orig_rx_buffer, trans_buf->buffer_to_rcv, ...)
#5 w5500_spi_read (value=0x3fcccbbc, len=1494)
#6 w5500_read (emac=0x5d6 ...)                      <- emac pointer garbage
#7 w5500_read_buffer (buffer=0x3fcccbbc, len=1494)  <- emac->rx_buffer is a w5500_tsk STACK address
#8 emac_w5500_receive (mac=0x3fccb388, buf=0x3fcf74d0)
#9 emac_w5500_task

emac->rx_buffer should be the heap_caps_malloc(ETH_MAX_PACKET_SIZE, MALLOC_CAP_DMA) block from emac_w5500_new; here it reads as a stack address, and trans_buf->buffer_to_rcv is NULL. The emac_w5500_t struct is being overwritten by a neighbour in internal DRAM. The FreeRTOS stack canary never fired on any task, so it is not a task-stack overflow; the write comes from whoever owns the block adjacent to emac, and which block that is depends on allocation order (hence "only with one more allocation").

Raw coredump: ops/coredumps/2026-09-17-dut-w5500-rx-v1.2-21-gb467f85.bin (ESP coredump format, 24-byte header). Decode with esp_coredump info_corefile -t raw against the ELF in ops/elf-archive/ (build is not bit-reproducible, so strip the header and load in gdb, or rebuild b467f85).

Not yet known

  • Which allocation writes past its end. Candidates by proximity at tether start: esp_netif_new internals, TinyUSB task/queues (tinyusb_driver_install), the W5500 alloc_recv_buf (1522 B per frame, freed per frame — heap churn right at the crash site).
  • Whether heap_caps_check_integrity_all() at 1 Hz on a bench build catches the first corrupt block (cheap next step).

Why it matters

#135 sidesteps it (PSRAM stack), but the corruption is main's and will surface again with the next unrelated internal-DRAM allocation. Also motivated #145 (the crash loop never tripped the rollback ladder).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions