Skip to content

mime: Curl_mime_prepare_headers() recurses once per nesting level and can exhaust the stack #22971

Description

@Fengxiaoxx

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

  1. Build curl from source with AddressSanitizer (the attached reproduce.sh
    does this; SRC/BUILD can point at an existing checkout).
  2. 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().
  3. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions