From 1314bab39f75a89f996b6e6082a9ba71b7bb0cb6 Mon Sep 17 00:00:00 2001 From: snowpaper <121743268+snowpaper@users.noreply.github.com> Date: Thu, 9 Jul 2026 11:57:51 +0700 Subject: [PATCH 1/2] wireguardif: route decrypted RX through netif->input, not ip_input() The decrypted-RX path called ip_input() directly on the caller's task, entering the lwIP core concurrently with tcpip_thread. Under sustained TCP traffic through the tunnel this double-frees a sent-segment pbuf ('pbuf_free: p->ref > 0' assert) and reboots the device. ml_wg_mgr already sets netif->input = tcpip_input, i.e. the integration intends all input serialized through the tcpip mailbox; the direct ip_input() call bypassed that. Hand the pbuf to netif->input instead so the lwIP core is only entered from tcpip_thread. Verified on ESP32-S3 (ESP-IDF v5.3): multi-hundred-KB TCP relays that previously crashed mid-transfer now run to completion. Fixes #17 (cherry picked from commit e74be46469c80089cef935d79667f4cca2fc00b0) Upstream-PR: CamM2325/microlink#20 Upstream-Issue: CamM2325/microlink#17 --- .../components/wireguard_lwip/src/wireguardif.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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"); } From be5c0e33ea547bb3f5f30ca3f8e94473941279f7 Mon Sep 17 00:00:00 2001 From: fudio101 Date: Fri, 14 Aug 2026 16:04:09 +0700 Subject: [PATCH 2/2] docs(wireguard_lwip): note the RX-path fix absorbed from upstream PR #20 The divergence record in this README exists so a future sync attempt knows what's already been fixed independently. Add the netif->input fix so it isn't rediscovered as a fresh divergence. --- components/microlink/components/wireguard_lwip/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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