Commit 7e1eb3c4ea for freeswitch.com
commit 7e1eb3c4ea55e06c350c4de00c9b56ce6384cfd7
Author: Dmitry Verenitsin <morbit85@gmail.com>
Date: Sun Aug 9 01:17:57 2026 +0500
[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.
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 */