Summary
Curl_mime_prepare_headers() calls itself for every subpart while generating
the Content-Type headers (lib/mime.c:1877). MAX_MIME_LEVELS (40) only
guards the read paths, so a deeply nested mime tree can exhaust the stack
inside curl_easy_perform(). The release path was recently made
non-recursive (d754680, "mime: do curl_mime_free() non-recursively"); this
request-preparation path still recurses.
Steps to reproduce
- Build curl from source with AddressSanitizer (the attached
reproduce.sh
does this; SRC/BUILD can point at an existing checkout).
- Build the attached harness (
poc.c) and run ./poc. It builds 200000
nested multipart parts bottom-up, starts a tiny local HTTP server and calls
curl_easy_perform().
- Observed:
AddressSanitizer: stack-overflow, with the stack frames
repeating Curl_mime_prepare_headers (lib/mime.c:1877).
Control: ./poc 100 returns CURLE_READ_ERROR without crashing.
Affected version
curl 8.22.0 (official release tarball, Release-Date 2026-09-02) and current
git master; x86_64 Linux, clang 14 static build with -fsanitize=address.
lib/mime.c has not changed since 8.22.0 except for the non-recursive free
fix.
Expected
Deep nesting should either be bounded (like the read paths with
MAX_MIME_LEVELS) or prepared iteratively, so that request preparation cannot
run the stack out.
Attached
curl_03_prepare_headers_recursion_asan_poc.zip contains the harness
(poc.c), reproduce.sh and build.md.
Summary
Curl_mime_prepare_headers()calls itself for every subpart while generatingthe
Content-Typeheaders (lib/mime.c:1877).MAX_MIME_LEVELS(40) onlyguards the read paths, so a deeply nested mime tree can exhaust the stack
inside
curl_easy_perform(). The release path was recently madenon-recursive (d754680, "mime: do curl_mime_free() non-recursively"); this
request-preparation path still recurses.
Steps to reproduce
reproduce.shdoes this;
SRC/BUILDcan point at an existing checkout).poc.c) and run./poc. It builds 200000nested multipart parts bottom-up, starts a tiny local HTTP server and calls
curl_easy_perform().AddressSanitizer: stack-overflow, with the stack framesrepeating
Curl_mime_prepare_headers(lib/mime.c:1877).Control:
./poc 100returnsCURLE_READ_ERRORwithout crashing.Affected version
curl 8.22.0 (official release tarball, Release-Date 2026-09-02) and current
git master; x86_64 Linux, clang 14 static build with
-fsanitize=address.lib/mime.chas not changed since 8.22.0 except for the non-recursive freefix.
Expected
Deep nesting should either be bounded (like the read paths with
MAX_MIME_LEVELS) or prepared iteratively, so that request preparation cannotrun the stack out.
Attached
curl_03_prepare_headers_recursion_asan_poc.zipcontains the harness(
poc.c),reproduce.shandbuild.md.