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:
@@ -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)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user