Author: Andy Green Date: Mon Sep 21 07:19:39 2026 +0100 server: don't touch ws close payload at CLOSED unless the conn established LWS_CALLBACK_CLOSED can arrive at the comms protocol for a wsi that was never established as a builder or power conn, eg on a vhost where the protocol init failed, or a conn dropped at the ESTABLISHED URL checks, or one caught by a context destroy in a non-ws condition. Asking about the peer's close payload dereferences wsi->ws, which only exists on a conn that completed a ws upgrade, crashing the server at shutdown with lws_get_close_length (ops-ws.c:1017) s_callback_ws __lws_close_free_wsi (close.c:1030) lws_context_destroy (context.c:2467) Bail out of the CLOSED handling unless the pss is on a vhd conn list, ie, it actually established on /builder or /power and so has teardown state and a ws condition. This also subsumes the !vhd check that followed it: a pss on a vhd conn list necessarily has a vhd. diff --git a/src/server/s-comms.c b/src/server/s-comms.c index 29198ba..2b1fc36 100644 --- a/src/server/s-comms.c +++ b/src/server/s-comms.c @@ -655,6 +655,18 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, return -1; case LWS_CALLBACK_CLOSED: + /* + * This can also arrive for wsis that were never established + * as a builder or power conn, eg on a vhost where protocol + * init failed, or one dropped at the ESTABLISHED URL checks. + * Those were never added to a vhd conn list and have no + * teardown state, and may not even be in a ws condition: + * asking about the peer's close payload dereferences wsi->ws, + * which only exists on a conn that got that far. + */ + if (!pss || lws_dll2_is_detached(&pss->same)) + break; + lwsac_free(&pss->query_ac); /* a conn closed mid-message must not leak its reassembly */ @@ -672,14 +684,6 @@ s_callback_ws(struct lws *wsi, enum lws_callback_reasons reason, void *user, /* remove pss from vhd->builders (active connection list) */ lws_dll2_remove(&pss->same); - /* - * On a vhost where protocol init failed there is no vhd and - * the conn was never established as a builder: there is - * nothing vhd-relative to tear down. - */ - if (!vhd) - break; - sais_builder_disconnected(vhd, wsi); sais_resource_wellknown_remove_pss(&pss->vhd->server, pss);