]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
content_encoding: timeout during slow decoding
authorStefan Eissing <stefan@eissing.org>
Tue, 26 May 2026 13:59:09 +0000 (15:59 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 27 May 2026 07:52:48 +0000 (09:52 +0200)
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

index aaef92a7d950da1a2a2269cc8abb77cc4520e0a8..b106fc813bb51d5cf394ec40442edd36b5a151af 100644 (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;