Skip to content

esp32: Drain PPP input before destroying the netif - #2471

Open
gabrielmancini wants to merge 1 commit into
atomvm:release-0.7from
iobee-io:esp32-ppp-rx-drain
Open

gabrielmancini wants to merge 1 commit into
atomvm:release-0.7from
iobee-io:esp32-ppp-rx-drain

Conversation

@gabrielmancini

Copy link
Copy Markdown

Follow-up to #2461, for the P2 in @petermm's review (#2461 (comment)). That PR was merged as approved, so the fix comes separately.

When a PPP session is freed, the RX task has already stopped, but input it queued for the TCP/IP task may still point at the netif. With CONFIG_LWIP_TCPIP_CORE_LOCKING=y (and _INPUT=n), esp_netif_destroy runs at once instead of behind that input, which then reaches a freed netif.

free_ppp now posts a callback through the TCP/IP mailbox, which is FIFO, and waits for it before destroying the netif. It reuses the stopped semaphore. esp_netif_tcpip_exec and tcpip_callback_wait would not work here: under core locking both run inline. None of free_ppp's callers (network:stop/0, a failed start, the late-close teardown task) runs on the TCP/IP task, so the wait cannot deadlock. If the callback cannot be posted, the session is left allocated rather than freed under queued input.

Tested on a T-Vending (ESP32-S3, SIM7600 on LTE). Each of 20 cycles dials, brings PPP up, starts an HTTP download and calls network:stop/0 in the middle of it, with UART input still queued:

build cycles received stop max / mean failures
core locking on, input locking off, comprehensive heap poisoning 20/20 448 KB 643 / 521 ms none
default configuration 20/20 472 KB 659 / 499 ms none

No CORRUPT HEAP, assert, Guru Meditation or reboot. The first build is the configuration in which the use-after-free would show.

These changes are made under both the "Apache 2.0" and the "GNU Lesser General Public License 2.1 or later" license terms (dual license).

SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later

When a PPP session is freed, the RX task has stopped, but input it
queued for the TCP/IP task may still point at the netif. With
CONFIG_LWIP_TCPIP_CORE_LOCKING enabled, esp_netif_destroy runs at once
rather than behind that input, which then reaches a freed netif.

free_ppp now posts a callback through the TCP/IP mailbox, which is FIFO,
and waits for it before destroying the netif. Not esp_netif_tcpip_exec
or tcpip_callback_wait: under core locking both run inline. If the
callback cannot be posted, the session is left allocated rather than
freed under queued input, and the late-close log line no longer says
"freed".

Signed-off-by: Gabriel Mancini <gabriel.mancini@gmail.com>
@bettio

bettio commented Nov 11, 2026

Copy link
Copy Markdown
Collaborator

This PR has been approved and merged locally, do not make changed, do not push here.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants