mirror of
https://github.com/weechat/weechat.git
synced 2026-06-12 14:14:48 +02:00
relay: limit size of partial message received while reading an HTTP request to prevent memory exhaustion
A relay client could send data with no end-of-line (an unterminated method or header line) and dribble its payload, making WeeChat accumulate it in the partial message buffer that grew without limit, until all memory was exhausted. This path is reachable before authentication during websocket initialization with the "weechat" and "irc" protocols. The accumulated partial message is now bounded by RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH: once the limit is reached, the extra data is ignored.
This commit is contained in:
@@ -14,6 +14,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
- irc: limit size of data received from the server to prevent memory exhaustion
|
||||
- irc: fix out-of-bounds read on incoming DCC command with a quoted filename ending the message ([#2322](https://github.com/weechat/weechat/issues/2322))
|
||||
- relay: limit size of received websocket frame and HTTP body to prevent memory exhaustion
|
||||
- relay: limit size of partial message received while reading an HTTP request to prevent memory exhaustion
|
||||
- xfer: replace directory separator in remote nick by underscore in download filename to prevent writing the file outside the download directory ([#2321](https://github.com/weechat/weechat/issues/2321))
|
||||
- xfer: fix out-of-bounds read when receiving empty line in DCC chat ([#2323](https://github.com/weechat/weechat/issues/2323))
|
||||
|
||||
|
||||
@@ -1006,6 +1006,14 @@ relay_http_recv (struct t_relay_client *client, const char *data, int size)
|
||||
|
||||
if (client->partial_message)
|
||||
{
|
||||
/*
|
||||
* limit the size of the partial message: once the maximum is reached,
|
||||
* ignore the extra data (protection against a client sending a huge
|
||||
* amount of data without any end-of-line and dribbling it, which would
|
||||
* consume all the memory)
|
||||
*/
|
||||
if (strlen (client->partial_message) >= RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH)
|
||||
return;
|
||||
new_partial = realloc (client->partial_message,
|
||||
strlen (client->partial_message) +
|
||||
strlen (data) + 1);
|
||||
|
||||
@@ -64,6 +64,15 @@ enum t_relay_client_http_status
|
||||
*/
|
||||
#define RELAY_HTTP_BODY_MAX_LENGTH (8 * 1024 * 1024)
|
||||
|
||||
/*
|
||||
* maximum length of the partial message accumulated while reading an HTTP
|
||||
* request: once this limit is reached, the extra data is ignored; this
|
||||
* protects against a client sending a huge amount of data without any
|
||||
* end-of-line (an unterminated method or header line), which would consume
|
||||
* all the memory
|
||||
*/
|
||||
#define RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH (8 * 1024 * 1024)
|
||||
|
||||
struct t_relay_http_request
|
||||
{
|
||||
enum t_relay_client_http_status status; /* HTTP status */
|
||||
|
||||
@@ -41,6 +41,7 @@ extern "C"
|
||||
#include "src/plugins/relay/relay-client.h"
|
||||
#include "src/plugins/relay/relay-config.h"
|
||||
#include "src/plugins/relay/relay-http.h"
|
||||
#include "src/plugins/relay/relay-server.h"
|
||||
#include "src/plugins/relay/relay-websocket.h"
|
||||
#include "src/plugins/weechat-plugin.h"
|
||||
|
||||
@@ -1021,6 +1022,69 @@ TEST(RelayHttp, Recv)
|
||||
/* TODO: write tests */
|
||||
}
|
||||
|
||||
/*
|
||||
* Test functions:
|
||||
* relay_http_recv (partial message accumulated is bounded)
|
||||
*
|
||||
* Check that data received without any end-of-line does not grow the partial
|
||||
* message buffer without limit.
|
||||
*/
|
||||
|
||||
TEST(RelayHttp, RecvLimit)
|
||||
{
|
||||
struct t_relay_server *server;
|
||||
struct t_relay_client *client;
|
||||
char *chunk;
|
||||
int chunk_size, i;
|
||||
size_t length1, length2;
|
||||
|
||||
/* disable auto-open of relay buffer (it would pollute other tests) */
|
||||
config_file_option_set (relay_config_look_auto_open_buffer, "off", 1);
|
||||
|
||||
server = relay_server_new ("weechat", RELAY_PROTOCOL_WEECHAT, NULL,
|
||||
9000,
|
||||
NULL, /* path */
|
||||
1, /* ipv4 */
|
||||
0, /* ipv6 */
|
||||
0, /* tls */
|
||||
0); /* unix_socket */
|
||||
CHECK(server);
|
||||
client = relay_client_new (-1, "test", server);
|
||||
CHECK(client);
|
||||
|
||||
chunk_size = 1024 * 1024;
|
||||
chunk = (char *)malloc (chunk_size + 1);
|
||||
CHECK(chunk);
|
||||
memset (chunk, 'a', chunk_size);
|
||||
chunk[chunk_size] = '\0';
|
||||
|
||||
/* feed more than the maximum, with no end-of-line (16 MB) */
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
relay_http_recv (client, chunk, chunk_size);
|
||||
}
|
||||
CHECK(client->partial_message);
|
||||
length1 = strlen (client->partial_message);
|
||||
|
||||
/* the partial message must be bounded (not ~16 MB) */
|
||||
CHECK(length1 <= RELAY_HTTP_PARTIAL_MESSAGE_MAX_LENGTH + (size_t)chunk_size);
|
||||
|
||||
/* feeding more data must not grow it any further */
|
||||
for (i = 0; i < 16; i++)
|
||||
{
|
||||
relay_http_recv (client, chunk, chunk_size);
|
||||
}
|
||||
length2 = strlen (client->partial_message);
|
||||
LONGS_EQUAL(length1, length2);
|
||||
|
||||
free (chunk);
|
||||
relay_client_free (client);
|
||||
relay_server_free (server);
|
||||
|
||||
/* restore auto-open of relay buffer */
|
||||
config_file_option_reset (relay_config_look_auto_open_buffer, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test functions:
|
||||
* relay_http_compress
|
||||
|
||||
Reference in New Issue
Block a user