From 7e1eb3c4ea55e06c350c4de00c9b56ce6384cfd7 Mon Sep 17 00:00:00 2001 From: Dmitry Verenitsin Date: Sun, 9 Aug 2026 01:17:57 +0500 Subject: [PATCH] [mod_xml_rpc] Fix OOB write and read-loop hang in WebSocket parser (#3114) `ws_read_frame()` had two defects in the framing path: - After header parsing, the remaining payload count `need = plen - (datalen - header)` could go negative when the initial read buffered more bytes than the frame's declared length, even with an in-range `plen`. A negative `need` passed the signed size guard and reached `ws_raw_read()` as a `size_t` near its maximum, driving a `memcpy` past `wsh->buffer`. Reject `need < 0` with a protocol-error close before the read loop. - The loop filling the frame header called `ws_raw_read()` without checking its result, so a connection that stopped delivering header bytes left the loop with no terminating condition, spinning or hanging the handler thread. Close on a non-advancing read, matching the payload read loop. --- src/mod/xml_int/mod_xml_rpc/ws.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/mod/xml_int/mod_xml_rpc/ws.c b/src/mod/xml_int/mod_xml_rpc/ws.c index c4c212ac21..bb9c22e8ba 100644 --- a/src/mod/xml_int/mod_xml_rpc/ws.c +++ b/src/mod/xml_int/mod_xml_rpc/ws.c @@ -430,14 +430,17 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) } if ((wsh->datalen = ws_raw_read(wsh, wsh->buffer, 14)) < need) { - while (!wsh->down && (wsh->datalen += ws_raw_read(wsh, wsh->buffer + wsh->datalen, 14 - wsh->datalen)) < need) ; + while (!wsh->down && wsh->datalen < need) { + issize_t r = ws_raw_read(wsh, wsh->buffer + wsh->datalen, 14 - wsh->datalen); -#if 0 - if (0 && (wsh->datalen += ws_raw_read(wsh, wsh->buffer + wsh->datalen, 14 - wsh->datalen)) < need) { - /* too small - protocol err */ - return ws_close(wsh, WS_PROTO_ERR); + if (r < 1) { + /* invalid read - protocol err .. */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + + wsh->datalen += r; } -#endif } *oc = *wsh->buffer & 0xf; @@ -517,6 +520,12 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data) need = (wsh->plen - (wsh->datalen - need)); + if (need < 0) { + /* more buffered than the frame declares - protocol err */ + *oc = WSOC_CLOSE; + return ws_close(wsh, WS_PROTO_ERR); + } + /* Reserve 1 byte for the trailing NUL below. */ if ((need + wsh->datalen) >= (issize_t)wsh->buflen) { /* too big - Ain't nobody got time fo' dat */