curl · Report #3279804
Stack use-after-scope in HTTP/3 POST request processing via CURLOPT_POSTFIELDS
Summary
Audit every libcurl POST in your stack. Treat CURLOPT_POSTFIELDS like explosives. Either switch to CURLOPT_COPYPOSTFIELDS or prove your pointer lives forever. No exceptions.
How it was found
Summary
A stack use-after-scope vulnerability exists in libcurl's HTTP/3 request processing when using CURLOPT_POSTFIELDS with stack-allocated buffers. libcurl retains a pointer to user-provided POST data but accesses it after the original stack frame has been destroyed, leading to memory corruption and potential denial of service.
The vulnerability occurs in Curl_pretransfer() at transfer.c:569 when libcurl calls strlen() on the previously stored POST data pointer that now points to invalid stack memory.
Steps to Reproduce / Proof of Concept
Environment
- libcurl version: 8.16.0-DEV (master branch)
- Compiler: Clang 20.1.8 with AddressSanitizer
- Platform: macOS (ARM64)
- Configuration: HTTP/3 enabled with ngtcp2/nghttp3
Reproduction Steps
- Build libcurl with ASAN:
export CC=clang
export CFLAGS="-O1 -g -fsanitize=address,undefined"
./configure --with-openssl --with-nghttp2 --with-nghttp3 --with-ngtcp2
make
- Compile the reproducer:
// http3_crash_poc.c
#include <curl/curl.h>
#include <string.h>
int main() {
CURL *curl = curl_easy_init();
// Stack-allocated buffer that goes out of scope
{
char body_data[257];
memset(body_data, 'A', 256);
body_data[256] = '\0';
curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/");
curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_3);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body_data); // Vulnerable call
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, 50L);
} // body_data goes out of scope here
// libcurl accesses invalid memory during transfer
curl_easy_perform(curl);
curl_easy_cleanup(curl);
return 0;
}
- Compile and run:
clang -fsanitize=address http3_crash_poc.c -lcurl -o poc
./poc
Crash Output
==3720==ERROR: AddressSanitizer: stack-use-after-scope on address 0x00016fa21470
READ of size 45 at 0x00016fa21470 thread T0
#0 strlen
#1 Curl_pretransfer transfer.c:569
#2 multi_runsingle multi.c:2376
#3 curl_multi_perform multi.c:2756
#4 easy_transfer easy.c:705
#5 easy_perform easy.c:813
SUMMARY: AddressSanitizer: stack-use-after-scope transfer.c:569 in Curl_pretransfer
Fuzzer Discovery
This vulnerability was discovered using a custom libFuzzer fuzzing harness.
Technical Analysis
Root Cause
The vulnerability stems from libcurl's CURLOPT_POSTFIELDS behavior:
- libcurl stores the pointer but doesn't copy the data
- The application's stack buffer becomes invalid after scope exit
- libcurl later dereferences the invalid pointer in
Curl_pretransfer()
Affected Code Path
curl_easy_setopt(CURLOPT_POSTFIELDS) → curl_easy_perform() → Curl_pretransfer() → strlen(invalid_pointer) → CRASH
Recommended Fix
- Documentation: Clarify that
CURLOPT_POSTFIELDSdata must remain valid until transfer completion - API Enhancement: Consider adding bounds checking or automatic copying for stack-detected pointers
- Alternative API: Promote
CURLOPT_COPYPOSTFIELDSfor safer usage patterns
Impact
Security Impact
- Denial of Service: Guaranteed crash leading to application termination
- Memory Corruption: Use-after-scope can lead to unpredictable behavior
- Potential Code Execution: In specific circumstances, memory corruption could be leveraged for control flow hijacking
Affected Scenarios
- Applications using libcurl for HTTP/3 requests with POST data
- Any code pattern where CURLOPT_POSTFIELDS points to stack-allocated memory
- Particularly affects:
- HTTP/3 client applications
- API clients using stack buffers for request bodies
- Embedded systems with limited heap usage
Real-World Exposure
- Language bindings: Many curl bindings may inadvertently create this pattern
- Example applications: CLI tools, web scrapers, API clients
- Severity: High due to HTTP/3 adoption growth and remote exploitability