Skip to content

wireguardif.c RX path calls ip_input() off the tcpip_thread — pbuf double-free crash under TCP load #17

Description

@snowpaper

Summary

The decrypted-RX path in wireguardif.c calls ip_input() directly from the caller's task (the task that runs wireguardif_network_rx / the wg manager task), not from lwIP's tcpip_thread. lwIP's core is not thread-safe: entering it from two threads concurrently corrupts pbuf refcounts. Under sustained TCP traffic through the tunnel this reliably triggers Assertion "pbuf_free: p->ref > 0" and reboots the device mid-transfer.

Environment

  • ESP32-S3 (N16R8), ESP-IDF v5.3
  • microlink @ 216da33 (also present on current main)
  • Reproduced with a TCP proxy on the tailnet IP relaying a ~600 KB stream (LPR print job)

Root cause

components/microlink/components/wireguard_lwip/src/wireguardif.c line 542: after decrypt, the pbuf is handed to ip_input(pbuf, device->netif) on the calling task. Meanwhile tcpip_thread is processing TCP for the same netif (acks for the outbound leg of the proxy). The race double-frees a sent-segment pbuf:

Assertion "pbuf_free: p->ref > 0" failed at line 753 in .../lwip/src/core/pbuf.c

Note: ml_wg_mgr.c already sets netif->input = tcpip_input, i.e. the integration intends all input to be serialized through the tcpip mailbox — the direct ip_input() call in wireguardif.c bypasses that.

Fix

Route the decrypted pbuf through netif->input (== tcpip_input) instead of calling ip_input() directly, so the lwIP core is only ever entered from tcpip_thread. ~8-line change; PR follows.

Verification

On hardware (ESP32-S3): before the fix, a real print job crashed the board mid-job every time (truncated output, reboot). After the fix, multi-hundred-KB jobs relay to completion; the node also survived overnight idle + WAN modem restart and printed first try afterwards.

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

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions