Commit fb9f37b9c7 for freeswitch.com

commit fb9f37b9c7869630ee52a3ae87399fbbf99ad74d
Author: Dmitry Verenitsin <morbit85@gmail.com>
Date:   Sat Aug 8 20:22:30 2026 +0500

    Merge commit from fork

    Bound the XOR unmasking loop to `wsh->rplen` (payload only) instead of
    `wsh->datalen`, which also covers the header and caused up to a 14-byte
    OOB write past the frame payload in `wsh->buffer` using the
    client-supplied mask key.

    Tighten the size guard from `>` to `>=` to reserve 1 byte for the
    trailing NUL written after the payload; a frame filling `buflen` exactly
    otherwise NUL-wrote 1 byte past `wsh->buffer`.

    Only reachable when `enable-websocket` is set in `mod_xml_rpc.conf.xml`
    (off by default).

diff --git a/src/mod/xml_int/mod_xml_rpc/ws.c b/src/mod/xml_int/mod_xml_rpc/ws.c
index b660b644f6..59dfe16b5a 100644
--- a/src/mod/xml_int/mod_xml_rpc/ws.c
+++ b/src/mod/xml_int/mod_xml_rpc/ws.c
@@ -485,7 +485,8 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)

 			need = (wsh->plen - (wsh->datalen - need));

-			if ((need + wsh->datalen) > (issize_t)wsh->buflen) {
+			/* 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 */
 				*oc = WSOC_CLOSE;
 				return ws_close(wsh, WS_DATA_TOO_BIG);
@@ -510,7 +511,8 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
 			if (mask && maskp) {
 				issize_t i;

-				for (i = 0; i < wsh->datalen; i++) {
+				/* Unmask payload only. */
+				for (i = 0; i < wsh->rplen; i++) {
 					wsh->payload[i] ^= maskp[i % 4];
 				}
 			}