-
Notifications
You must be signed in to change notification settings - Fork 8k
Description
Description
Hi everyone, I think I may have found a bug around memory management in ext/curl.
Summary
When using curl_multi_* functions with HTTP/3 (CURLOPT_HTTP_VERSION = 3
), PHP exhibits use-after-free memory errors detected by Valgrind. The issue appears to be specific to HTTP/3 and does not occur with HTTP/1.1 or HTTP/2.
Environment
- PHP Version: 8.4.12 (cli) (built: Aug 28 2025 18:17:51) (NTS)
- Docker Image: 8.4-cli
- curl Version: 8.14.1 with HTTP3 support (nghttp3/1.8.0)
- OS: Linux (arm64)
Description
The bug manifests as invalid memory access when using HTTP/3 with curl multi handles. Valgrind reports use-after-free errors where memory freed by libcurl is subsequently accessed, suggesting a timing or reference counting issue between PHP's curl implementation and libcurl's HTTP/3 handling.
The issue is particularly reproducible:
- After
curl_reset()
calls - With multiple handles
- During connection cleanup phase
Valgrind Output
Extract:
(...)
==17150== Invalid read of size 8
==17150== at 0x5546C1C: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x5505503: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x55478A7: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x55498A3: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x35201B: ??? (in /usr/local/bin/php)
==17150== by 0x6BBD17: execute_ex (in /usr/local/bin/php)
==17150== by 0x6B76A7: zend_execute (in /usr/local/bin/php)
==17150== by 0x723D47: zend_execute_script (in /usr/local/bin/php)
==17150== by 0x5AB557: php_execute_script_ex (in /usr/local/bin/php)
==17150== by 0x725A37: ??? (in /usr/local/bin/php)
==17150== by 0x27A73F: ??? (in /usr/local/bin/php)
==17150== by 0x56F229B: (below main) (libc_start_call_main.h:58)
==17150== Address 0x2a4cbd50 is 976 bytes inside a block of size 1,464 free'd
==17150== at 0x48884F8: free (vg_replace_malloc.c:989)
==17150== by 0x550D547: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x55056FB: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x55063A3: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x556E723: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x5549E83: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x554B94F: curl_multi_perform (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x3521FF: ??? (in /usr/local/bin/php)
==17150== by 0x6BCBC7: execute_ex (in /usr/local/bin/php)
==17150== by 0x6B76A7: zend_execute (in /usr/local/bin/php)
==17150== by 0x723D47: zend_execute_script (in /usr/local/bin/php)
==17150== by 0x5AB557: php_execute_script_ex (in /usr/local/bin/php)
==17150== Block was alloc'd at
==17150== at 0x488C5C4: calloc (vg_replace_malloc.c:1675)
==17150== by 0x556DFB7: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x5549E83: ??? (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x554B94F: curl_multi_perform (in /usr/lib/aarch64-linux-gnu/libcurl.so.4.8.0)
==17150== by 0x3521FF: ??? (in /usr/local/bin/php)
==17150== by 0x6BCBC7: execute_ex (in /usr/local/bin/php)
==17150== by 0x6B76A7: zend_execute (in /usr/local/bin/php)
==17150== by 0x723D47: zend_execute_script (in /usr/local/bin/php)
==17150== by 0x5AB557: php_execute_script_ex (in /usr/local/bin/php)
==17150== by 0x725A37: ??? (in /usr/local/bin/php)
==17150== by 0x27A73F: ??? (in /usr/local/bin/php)
==17150== by 0x56F229B: (below main) (libc_start_call_main.h:58)
(...)
Full valgrind output: available on github.
Reproduction
Script available on github.
To reproduce:
- Run with docker:
docker run --rm -v $(pwd):/app -w /app php:8.4-cli php bug-http3.php
- Run with Valgrind:
valgrind --tool=memcheck --track-origins=yes php bug-http3.php
Note:
If you patch my reproducer on lines 13-17 with the following code (bypassing curl_reset
) there is no issue: $ch = curl_init();
.
I could reproduce this issue with curl 8.14.1 and 8.15.0 (on macos).
Full curl version (provided by official docker php:8.4-cli):
curl 8.14.1 (aarch64-unknown-linux-gnu) libcurl/8.14.1 OpenSSL/3.5.1 zlib/1.3.1 brotli/1.1.0 zstd/1.5.7 libidn2/2.3.8 libpsl/0.21.2 libssh2/1.11.1 nghttp2/1.64.0 nghttp3/1.8.0 librtmp/2.3 OpenLDAP/2.6.10
Release-Date: 2025-06-04, security patched: 8.14.1-2
Protocols: dict file ftp ftps gopher gophers http https imap imaps ipfs ipns ldap ldaps mqtt pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp ws wss
Features: alt-svc AsynchDNS brotli GSS-API HSTS HTTP2 HTTP3 HTTPS-proxy IDN IPv6 Kerberos Largefile libz NTLM PSL SPNEGO SSL threadsafe TLS-SRP UnixSockets zstd
PHP Version
PHP 8.4.12 (cli) (built: Aug 28 2025 18:17:51) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Zend Engine v4.4.12, Copyright (c) Zend Technologies
with Zend OPcache v8.4.12, Copyright (c), by Zend Technologies
Operating System
No response