[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.
This commit is contained in:
Dmitry Verenitsin
2026-08-08 23:17:57 +03:00
committed by GitHub
parent a047b7a258
commit 7e1eb3c4ea
+15 -6
View File
@@ -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 */