From 1791a087079e90769d52e7a797a59ecaf2d1bd6d Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 26 May 2026 15:59:09 +0200 Subject: [PATCH] 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 --- lib/content_encoding.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib/content_encoding.c b/lib/content_encoding.c index aaef92a7d9..b106fc813b 100644 --- a/lib/content_encoding.c +++ b/lib/content_encoding.c @@ -46,6 +46,7 @@ #include #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;