1
0
mirror of https://github.com/weechat/weechat.git synced 2026-07-07 02:03:13 +02:00

relay: limit size of decompressed websocket frame to prevent memory exhaustion (GHSA-v2v4-45wm-5cr3)

An authenticated relay client using the permessage-deflate websocket
extension could send a small compressed frame that decompresses to an
unbounded amount of data, exhausting all memory and crashing WeeChat.

The output buffer in relay_websocket_inflate is now capped to
WEBSOCKET_INFLATE_MAX_SIZE: frames decompressing beyond this limit are
rejected and the connection is closed.
This commit is contained in:
Sébastien Helleu
2026-05-30 16:38:42 +02:00
parent 2a272a7543
commit 405707d544
4 changed files with 64 additions and 5 deletions
+1
View File
@@ -8,6 +8,7 @@
### Fixed
- relay: limit size of decompressed websocket frame with permessage-deflate to prevent memory exhaustion ([GHSA-v2v4-45wm-5cr3](https://github.com/weechat/weechat/security/advisories/GHSA-v2v4-45wm-5cr3))
- relay/weechat: fix empty buffers in client when WeeChat is running on Solaris/illumos
- build: fix build on Solaris/illumos (issue #2251)
+21 -5
View File
@@ -530,7 +530,7 @@ relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
int rc;
unsigned char append_bytes[4] = { 0x00, 0x00, 0xFF, 0xFF };
Bytef *data2, *dest, *dest2;
uLongf size2, dest_size;
uLongf size2, dest_size, new_size;
if (!data || (size == 0) || !strm || !size_decompressed)
return NULL;
@@ -547,8 +547,13 @@ relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
memcpy (data2, data, size);
memcpy (data2 + size, append_bytes, sizeof (append_bytes));
/* estimate the decompressed size, by default 10 * size */
dest_size = 10 * size2;
/*
* estimate the decompressed size, by default 10 * size, capped to the
* maximum allowed size (also prevents an integer overflow on the
* multiplication)
*/
dest_size = (size2 > WEBSOCKET_INFLATE_MAX_SIZE / 10) ?
WEBSOCKET_INFLATE_MAX_SIZE : 10 * size2;
dest = malloc (dest_size);
if (!dest)
goto error;
@@ -577,8 +582,19 @@ relay_websocket_inflate (const void *data, size_t size, z_stream *strm,
&& (strm->avail_in > 0)))
{
/* output buffer is not large enough */
strm->avail_out += dest_size;
dest_size *= 2;
if (dest_size >= WEBSOCKET_INFLATE_MAX_SIZE)
{
/*
* decompressed data is too large: reject the frame
* (protection against a "deflate bomb")
*/
goto error;
}
/* double the buffer, capped to the maximum allowed size */
new_size = (dest_size > WEBSOCKET_INFLATE_MAX_SIZE / 2) ?
WEBSOCKET_INFLATE_MAX_SIZE : dest_size * 2;
strm->avail_out += (uInt)(new_size - dest_size);
dest_size = new_size;
dest2 = realloc (dest, dest_size);
if (!dest2)
goto error;
+7
View File
@@ -39,6 +39,13 @@
#define WEBSOCKET_SUB_PROTOCOL_API_WEECHAT "api.weechat"
/*
* maximum size of a decompressed websocket frame (with "permessage-deflate"):
* used as an upper bound when inflating, to prevent a small compressed frame
* from decompressing to an unbounded amount of data ("deflate bomb")
*/
#define WEBSOCKET_INFLATE_MAX_SIZE (8 * 1024 * 1024)
struct t_relay_client;
struct t_relay_http_request;
@@ -464,6 +464,41 @@ TEST(RelayWebsocket, Inflate)
free (payload_comp);
relay_websocket_deflate_free (ws_deflate);
/*
* protection against "deflate bomb": a small compressed frame that
* decompresses to more than WEBSOCKET_INFLATE_MAX_SIZE must be rejected
* (relay_websocket_inflate returns NULL)
*/
ws_deflate = relay_websocket_deflate_alloc ();
CHECK(ws_deflate);
ws_deflate->window_bits_deflate = 15;
ws_deflate->window_bits_inflate = 15;
ws_deflate->strm_deflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_deflate));
CHECK(ws_deflate->strm_deflate);
LONGS_EQUAL(1, relay_websocket_deflate_init_stream_deflate (ws_deflate));
ws_deflate->strm_inflate = (z_stream *)calloc (1, sizeof (*ws_deflate->strm_inflate));
CHECK(ws_deflate->strm_inflate);
LONGS_EQUAL(1, relay_websocket_deflate_init_stream_inflate (ws_deflate));
/* highly compressible payload that decompresses past the maximum size */
size_t bomb_size = WEBSOCKET_INFLATE_MAX_SIZE + (1024 * 1024);
char *bomb = (char *)calloc (1, bomb_size);
CHECK(bomb);
payload_comp = (char *)relay_websocket_deflate (bomb, bomb_size,
ws_deflate->strm_deflate, &size_comp);
CHECK(payload_comp);
CHECK(size_comp < bomb_size);
payload_decomp = (char *)relay_websocket_inflate (payload_comp, size_comp,
ws_deflate->strm_inflate, &size_decomp);
POINTERS_EQUAL(NULL, payload_decomp);
free (payload_comp);
free (bomb);
relay_websocket_deflate_free (ws_deflate);
}
/*