]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
mime: only allow 40 levels of calls
authorDaniel Stenberg <daniel@haxx.se>
Mon, 20 Apr 2026 08:28:28 +0000 (10:28 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 20 Apr 2026 12:28:43 +0000 (14:28 +0200)
To avoid problems when doing insane things.

Closes #21384

lib/mime.c

index 27a2a85c8bee593c67952b952d7c3627f0e2ad28..b7e51aae4e96fd0095d9fd51cb5e0baefde5690a 100644 (file)
@@ -44,11 +44,14 @@ struct Curl_easy;
 #include "slist.h"
 #include "curlx/dynbuf.h"
 
+#define MAX_MIME_LEVELS 40 /* avoid deep nesting */
+
 #define READ_ERROR   ((size_t)-1)
 #define STOP_FILLING ((size_t)-2)
 
 static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
-                                 void *instream, bool *hasread);
+                                 void *instream, bool *hasread,
+                                 size_t call_depth);
 static curl_off_t mime_size(curl_mimepart *part);
 
 /* Quoted-printable character class table.
@@ -687,11 +690,16 @@ static size_t readback_bytes(struct mime_state *state,
 }
 
 /* Read a non-encoded part content. */
-static size_t read_part_content(curl_mimepart *part,
-                                char *buffer, size_t bufsize, bool *hasread)
+
+static size_t read_part_content(curl_mimepart *part, char *buffer,
+                                size_t bufsize, bool *hasread,
+                                size_t call_depth)
 {
   size_t sz = 0;
 
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
+
   switch(part->lastreadstatus) {
   case 0:
   case CURL_READFUNC_ABORT:
@@ -714,7 +722,8 @@ static size_t read_part_content(curl_mimepart *part,
        * Cannot be processed as other kinds since read function requires
        * an additional parameter and is highly recursive.
        */
-      sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread);
+      sz = mime_subparts_read(buffer, 1, bufsize, part->arg, hasread,
+                              call_depth);
       break;
     case MIMEKIND_FILE:
       if(part->fp && feof(part->fp))
@@ -753,13 +762,17 @@ static size_t read_part_content(curl_mimepart *part,
 
 /* Read and encode part content. */
 static size_t read_encoded_part_content(curl_mimepart *part, char *buffer,
-                                        size_t bufsize, bool *hasread)
+                                        size_t bufsize, bool *hasread,
+                                        size_t call_depth)
 {
   struct mime_encoder_state *st = &part->encstate;
   size_t cursize = 0;
   size_t sz;
   bool ateof = FALSE;
 
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
+
   for(;;) {
     if(st->bufbeg < st->bufend || ateof) {
       /* Encode buffered data. */
@@ -792,7 +805,7 @@ static size_t read_encoded_part_content(curl_mimepart *part, char *buffer,
     if(st->bufend >= sizeof(st->buf))
       return cursize ? cursize : READ_ERROR;    /* Buffer full. */
     sz = read_part_content(part, st->buf + st->bufend,
-                           sizeof(st->buf) - st->bufend, hasread);
+                           sizeof(st->buf) - st->bufend, hasread, call_depth);
     switch(sz) {
     case 0:
       ateof = TRUE;
@@ -813,12 +826,15 @@ static size_t read_encoded_part_content(curl_mimepart *part, char *buffer,
 
 /* Readback a mime part. */
 static size_t readback_part(curl_mimepart *part,
-                            char *buffer, size_t bufsize, bool *hasread)
+                            char *buffer, size_t bufsize, bool *hasread,
+                            size_t call_depth)
 {
   size_t cursize = 0;
 
-  /* Readback from part. */
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
 
+  /* Readback from part. */
   while(bufsize) {
     size_t sz = 0;
     struct curl_slist *hdr = (struct curl_slist *)part->state.ptr;
@@ -861,9 +877,10 @@ static size_t readback_part(curl_mimepart *part,
       break;
     case MIMESTATE_CONTENT:
       if(part->encoder)
-        sz = read_encoded_part_content(part, buffer, bufsize, hasread);
+        sz = read_encoded_part_content(part, buffer, bufsize, hasread,
+                                       call_depth);
       else
-        sz = read_part_content(part, buffer, bufsize, hasread);
+        sz = read_part_content(part, buffer, bufsize, hasread, call_depth);
       switch(sz) {
       case 0:
         mimesetstate(&part->state, MIMESTATE_END, NULL);
@@ -897,12 +914,16 @@ static size_t readback_part(curl_mimepart *part,
 
 /* Readback from mime. Warning: not a read callback function. */
 static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
-                                 void *instream, bool *hasread)
+                                 void *instream, bool *hasread,
+                                 size_t call_depth)
 {
   curl_mime *mime = (curl_mime *)instream;
   size_t cursize = 0;
   (void)size;  /* Always 1 */
 
+  if(++call_depth > MAX_MIME_LEVELS)
+    return READ_ERROR;
+
   while(nitems) {
     size_t sz = 0;
     curl_mimepart *part = mime->state.ptr;
@@ -937,7 +958,7 @@ static size_t mime_subparts_read(char *buffer, size_t size, size_t nitems,
         mimesetstate(&mime->state, MIMESTATE_END, NULL);
         break;
       }
-      sz = readback_part(part, buffer, nitems, hasread);
+      sz = readback_part(part, buffer, nitems, hasread, call_depth);
       switch(sz) {
       case CURL_READFUNC_ABORT:
       case CURL_READFUNC_PAUSE:
@@ -1506,7 +1527,7 @@ size_t Curl_mime_read(char *buffer, size_t size, size_t nitems, void *instream)
    * adding any data and this loops infinitely. */
   do {
     hasread = FALSE;
-    ret = readback_part(part, buffer, nitems, &hasread);
+    ret = readback_part(part, buffer, nitems, &hasread, 0);
     /*
      * If this is not possible to get some data without calling more than
      * one read callback (probably because a content encoder is not able to