Commit d557ca6d29 for freeswitch.com

commit d557ca6d29e6da4f10824dc158100906a592b91a
Author: Dmitry Verenitsin <morbit85@gmail.com>
Date:   Sat Aug 8 20:27:47 2026 +0500

    Merge commit from fork

    The 127-length extended payload field was decoded with `ntohl()`, a
    32-bit byte swap, dropping the upper word of the 64-bit length. Combined
    with the signed `issize_t plen`, a high low-word truncated to a negative
    length that slipped past the signed size guard and became `SIZE_MAX` in
    the read loop, driving an out-of-bounds write past `wsh->buffer`.

    Decode the full 64-bit length in network byte order and reject any value
    that cannot fit the buffer with an unsigned comparison before narrowing
    to `issize_t`, so a truncated or oversized length can no longer yield a
    negative `plen` or pass the guard.

    `ws_write_frame()` had the symmetric defect: it encoded the 64-bit length
    field with a 32-bit `htonl()`, mis-framing any payload large enough to
    use the 8-byte (127) length field. Emit the full 8 bytes there too.

    Factor the byte assembly into `ws_get_be64()` / `ws_put_be64()` helpers.

diff --git a/src/mod/xml_int/mod_xml_rpc/ws.c b/src/mod/xml_int/mod_xml_rpc/ws.c
index 59dfe16b5a..c4c212ac21 100644
--- a/src/mod/xml_int/mod_xml_rpc/ws.c
+++ b/src/mod/xml_int/mod_xml_rpc/ws.c
@@ -384,6 +384,32 @@ issize_t ws_close(wsh_t *wsh, int16_t reason)
 	return reason * -1;
 }

+/* Read a big-endian (network byte order) 64-bit integer from a byte buffer. */
+static uint64_t ws_get_be64(const uint8_t *p)
+{
+	return ((uint64_t)p[0] << 56) |
+		((uint64_t)p[1] << 48) |
+		((uint64_t)p[2] << 40) |
+		((uint64_t)p[3] << 32) |
+		((uint64_t)p[4] << 24) |
+		((uint64_t)p[5] << 16) |
+		((uint64_t)p[6] << 8) |
+		((uint64_t)p[7]);
+}
+
+/* Write a big-endian (network byte order) 64-bit integer to a byte buffer. */
+static void ws_put_be64(uint8_t *p, uint64_t v)
+{
+	p[0] = (uint8_t)(v >> 56);
+	p[1] = (uint8_t)(v >> 48);
+	p[2] = (uint8_t)(v >> 40);
+	p[3] = (uint8_t)(v >> 32);
+	p[4] = (uint8_t)(v >> 24);
+	p[5] = (uint8_t)(v >> 16);
+	p[6] = (uint8_t)(v >> 8);
+	p[7] = (uint8_t)(v);
+}
+
 issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
 {

@@ -447,7 +473,7 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
 			wsh->payload = &wsh->buffer[2];

 			if (wsh->plen == 127) {
-				uint64_t *u64;
+				uint64_t plen64;

 				need += 8;

@@ -457,10 +483,16 @@ issize_t ws_read_frame(wsh_t *wsh, ws_opcode_t *oc, uint8_t **data)
 					return ws_close(wsh, WS_PROTO_ERR);
 				}

-				u64 = (uint64_t *) wsh->payload;
+				plen64 = ws_get_be64((const uint8_t *)wsh->payload);
 				wsh->payload += 8;

-				wsh->plen = ntohl((u_long)*u64);
+				/* Bound-check unsigned, before narrowing to the signed issize_t plen. */
+				if (plen64 >= wsh->buflen) {
+					*oc = WSOC_CLOSE;
+					return ws_close(wsh, WS_DATA_TOO_BIG);
+				}
+
+				wsh->plen = (issize_t)plen64;

 			} else if (wsh->plen == 126) {
 				uint16_t *u16;
@@ -598,13 +630,10 @@ issize_t ws_write_frame(wsh_t *wsh, ws_opcode_t oc, void *data, size_t bytes)
 		*u16 = htons((uint16_t) bytes);

 	} else {
-		uint64_t *u64;
-
 		hdr[1] = 127;
 		hlen += 8;

-		u64 = (uint64_t *) &hdr[2];
-		*u64 = htonl((unsigned long)bytes);
+		ws_put_be64(&hdr[2], (uint64_t)bytes);
 	}

 	if (ws_raw_write(wsh, (void *) &hdr[0], hlen) != (issize_t)hlen) {