content_encoding: timeout during slow decoding

Check during transfer/content decoding for every MB or so, if the
transfer has reached its overall time limit. Error out if so.

This is mainly a protectin against compression bombs using way more time
than the transfer is allowed to. Normal compression ratios are unlikely
to benefit as they need more upstream data where the timeout handling is
already in place.

Fixes #21603
Reported-by: Joshua Rogers
Closes #21758
This commit is contained in:
Stefan Eissing 2026-05-26 15:59:09 +02:00 committed by Daniel Stenberg
parent 049ec8a363
commit 1791a08707
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2

View file

@ -46,6 +46,7 @@
#include <zstd.h>
#endif
#include "connect.h"
#include "sendf.h"
#include "curl_trc.h"
#include "content_encoding.h"
@ -154,6 +155,7 @@ static CURLcode inflate_stream(struct Curl_easy *data,
z_const Bytef *orig_in = z->next_in;
bool done = FALSE;
CURLcode result = CURLE_OK; /* Curl_client_write status */
int i = 0;
/* Check state. */
if(zp->zlib_init != ZLIB_INIT &&
@ -167,6 +169,15 @@ static CURLcode inflate_stream(struct Curl_easy *data,
int status; /* zlib status */
done = TRUE;
if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) {
/* check every MB of output if we are not exceeding time limit */
i = 0;
if(Curl_timeleft_ms(data) < 0) {
failf(data, "Operation timed out while decoding payload");
return exit_zlib(data, z, &zp->zlib_init, CURLE_OPERATION_TIMEDOUT);
}
}
/* (re)set buffer for decompressed output for every iteration */
z->next_out = (Bytef *)zp->buffer;
z->avail_out = DECOMPRESS_BUFFER_SIZE;
@ -412,6 +423,7 @@ static CURLcode brotli_do_write(struct Curl_easy *data,
size_t dstleft;
CURLcode result = CURLE_OK;
BrotliDecoderResult r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
int i = 0;
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
@ -421,6 +433,16 @@ static CURLcode brotli_do_write(struct Curl_easy *data,
while((nbytes || r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) &&
result == CURLE_OK) {
if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) {
/* check every MB of output if we are not exceeding time limit */
i = 0;
if(Curl_timeleft_ms(data) < 0) {
failf(data, "Operation timed out while decoding payload");
return CURLE_OPERATION_TIMEDOUT;
}
}
dst = (uint8_t *)bp->buffer;
dstleft = DECOMPRESS_BUFFER_SIZE;
r = BrotliDecoderDecompressStream(bp->br,
@ -520,6 +542,7 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
ZSTD_inBuffer in;
ZSTD_outBuffer out;
size_t errorCode;
int i = 0;
if(!(type & CLIENTWRITE_BODY) || !nbytes)
return Curl_cwriter_write(data, writer->next, type, buf, nbytes);
@ -529,6 +552,15 @@ static CURLcode zstd_do_write(struct Curl_easy *data,
in.size = nbytes;
for(;;) {
if(++i > (1024 * 1024 / DECOMPRESS_BUFFER_SIZE)) {
/* check every MB of output if we are not exceeding time limit */
i = 0;
if(Curl_timeleft_ms(data) < 0) {
failf(data, "Operation timed out while decoding payload");
return CURLE_OPERATION_TIMEDOUT;
}
}
out.pos = 0;
out.dst = zp->buffer;
out.size = DECOMPRESS_BUFFER_SIZE;