diff --git a/components/microlink/components/wireguard_lwip/README.md b/components/microlink/components/wireguard_lwip/README.md index f118ddc..346fcb4 100644 --- a/components/microlink/components/wireguard_lwip/README.md +++ b/components/microlink/components/wireguard_lwip/README.md @@ -6,7 +6,11 @@ > ⚠️ **This copy has diverged substantially from upstream — any future sync is a manual merge, not > a fast-forward.** The most visible divergence: upstream's single `allowed_ip`/`allowed_mask` pair > was replaced with an `allowed_source_ips[WIREGUARD_MAX_SRC_IPS]` array, plus the ESP-IDF 6.x/GCC 15 -> patches above and `WG_DEBUG` logging throughout. +> patches above and `WG_DEBUG` logging throughout. It also carries a fix absorbed from +> [CamM2325/microlink#20](https://github.com/CamM2325/microlink/pull/20) (not present upstream at +> smartalock/wireguard-lwip): decrypted RX is handed to `device->netif->input` instead of calling +> `ip_input()` directly, so the lwIP core is only ever entered from `tcpip_thread` — see +> `UPSTREAM_PRS.md` at the microlink repo root. > > **To check upstream for new fixes:** > ```bash diff --git a/components/microlink/components/wireguard_lwip/src/wireguardif.c b/components/microlink/components/wireguard_lwip/src/wireguardif.c index 0bc7131..9890e64 100644 --- a/components/microlink/components/wireguard_lwip/src/wireguardif.c +++ b/components/microlink/components/wireguard_lwip/src/wireguardif.c @@ -539,9 +539,14 @@ static void wireguardif_process_data_message(struct wireguard_device *device, st if (dest_ok) { // Send packet to be processed by LWIP WG_DEBUG("[WG_RX_IP] Passing %u bytes to IP layer\n", (unsigned)pbuf->tot_len); - ip_input(pbuf, device->netif); - // pbuf is owned by IP layer now - pbuf = NULL; + // Enter the lwIP core ONLY from the tcpip_thread. netif->input is + // tcpip_input (set in ml_wg_mgr): posting the packet defers ip_input + // to the tcpip_thread. Calling ip_input() directly here runs on the + // wg_mgr task and races the tcpip_thread's WiFi TCP processing -> + // unsynchronized pbuf/PCB access -> double-free crash (pbuf.c:753). + if (device->netif->input(pbuf, device->netif) == ERR_OK) { + pbuf = NULL; // ownership handed to the tcpip_thread queue + } // else: still ours -> freed by pbuf_free() at the end of this block } else { WG_DEBUG("[WG_RX_IP] DROPPED: dest_ok=false\n"); }