Web: fix crash/deadlock when disabling web panel via HTTP command

Calling `set web off` through the web panel caused the device to either
hang permanently or crash with a Guru Meditation Error (LoadProhibited).

Root cause: the HTTP command handler runs inside the httpd task and
synchronously called `setWebEnabled(false)`, which called
`_panel.stop()` -> `httpd_ssl_stop()` on the very connection that was
serving the request. This is unsafe in two ways:

1. `httpd_ssl_stop()` blocks waiting for the httpd task to finish, but
   the httpd task is the one executing the handler — a self-deadlock.

2. Even if the stop proceeds, closing the active TLS connection from
   within its own handler triggers lwIP teardown (esp_netif_down_api ->
   dhcp_stop -> TCP RST) while the WiFi driver is in an inconsistent
   state, causing a null-pointer dereference in ieee80211_output_do.

The deadlock was further compounded by a second concurrent call:
while the httpd task was stuck in httpd_ssl_stop(), the main loop kept
running, called ensureWebServer(), saw _server != nullptr (never cleared
because httpd_ssl_stop() never returned), and issued a second
httpd_ssl_stop() on the same handle — blocking the main loop as well
and stopping all radio packet processing until reboot.

The explicit start/stop calls in setWebEnabled() were redundant:
WebService::loop() already calls ensureWebServer() on every iteration,
which starts the server when enabled and WiFi is up, and stops it
otherwise.  Remove the block entirely and let loop() handle both
transitions safely from the main loop context, outside the httpd task.
This commit is contained in:
Valentin V. Bartenev
2026-05-08 02:28:19 +03:00
parent de978414ce
commit 918c839e09
+1 -9
View File
@@ -56,15 +56,7 @@ bool WebService::setWebEnabled(bool enabled) {
if (!enabled) {
_suspended_for_ota = false;
}
bool ok = savePrefs();
#if defined(ESP_PLATFORM) && WITH_WEB_PANEL
if (_prefs.web_enabled != 0 && !_suspended_for_ota) {
ensureWebServer();
} else {
_panel.stop();
}
#endif
return ok;
return savePrefs();
}
bool WebService::setWebStatsEnabled(bool enabled) {