✓ProveIt
Examples / Report #3452725 Saved analysis

Title: Use-After-Free in cURL Test Suite via Improper Cleanup of Global Handle

curl · 2025-12-059.9s analysis · $0.019 in model calls

What the report claims

The test suite contains a use-after-free bug in the cleanup of a global CURL handle due to missing NULL assignment after curl_easy_cleanup.

Compare with curl's recorded outcome

Labeled AI slop

curl's maintainer lists this report as AI slop

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Needs human review

The available evidence does not settle the report. A maintainer should investigate.

Judgment withdrawn. The answer failed the evidence check.
See the withdrawn model reasoning

The following reasoning failed the citation check and does not support the final finding.

  • The test file lib1555.c and its contents (test_lib1555 function, t1555_curl global, test_cleanup label) do not exist in curl at the checked revision (master 68a44edd500a on 2025-12-05) or in any release since 7.50, per deterministic checks.
  • Deterministic checks show 0/4 quoted lines from the report appear anywhere in curl at the checked revision.
  • The report describes a vulnerability in code that is not part of the curl codebase.
How this analysis ran 5 claims · 0 citations verified

Evidence check: judge said likely_invalid without a code citation contradicting a central claim

Checked against curl master on 2025-12-05 (68a44edd500a). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
partially grounded
no model · 9 references checked out, 6 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 5716 tokens · $0.0004
3 · Evidence
10 code snippets
real curl source at 68a44edd500a
4 · Judgment
Nemotron-3-Ultra-550b-a55b
13982 tokens · 7.7s · $0.0182
5 · Receipts
0 of 5 citations verified
every citation must point at code the judge was shown
Inspect the withdrawn draft reply

This draft belongs to the withdrawn judgment. It may contain unsupported claims.

The test file lib1555.c and the function test_lib1555 with global t1555_curl do not exist in curl's repository at the revision you reference (master 68a44edd500a, 2025-12-05) or in any release since 7.50. Deterministic symbol checks confirm 0/4 quoted lines from your report appear in curl. Please verify the file path and revision, or provide a patch against the actual curl source tree.

Inspect all claims and source code 5 claims · 10 code excerpts

This model judgment was withdrawn. The claim assessments below are preserved for inspection and may be wrong.

Report claims

C1flaw contradicted

curl_easy_cleanup(t1555_curl) is called without checking if t1555_curl is NULL or valid, leading to use-after-free or double-free

Read the reasoning

The report describes a test file lib1555.c with function test_lib1555, global t1555_curl, and test_cleanup label. Deterministic checks confirm these symbols are ungrounded — they do not exist in curl at the checked revision (master 68a44edd500a on 2025-12-05) or in any release since 7.50. The quoted code from the report does not appear in the curl codebase.

deterministic check
C2location contradicted

test_cleanup label in test_lib1555 function at line 83 in lib1555.c

Read the reasoning

The location referenced (test_cleanup label in test_lib1555 at line 83 of lib1555.c) does not exist in the curl repository at the revision checked. The deterministic checks show 0/4 quoted lines from the report appear anywhere in curl.

deterministic check
C3trigger contradicted

Repeated calls to test_lib1555 or test_cleanup causing the dangling pointer to be reused

Read the reasoning

The trigger described (repeated calls to test_lib1555 or test_cleanup) cannot occur because these functions do not exist in the curl codebase at the checked revision.

deterministic check
C4impact contradicted

Memory corruption, potential arbitrary code execution, or denial of service in test environments

Read the reasoning

The claimed impact (memory corruption, arbitrary code execution, denial of service) is predicated on the existence of the vulnerable test code, which deterministic checks confirm is not present in curl.

deterministic check
C5reproduction contradicted

Call test_lib1555 multiple times without reinitializing t1555_curl to demonstrate use-after-free

Read the reasoning

The reproduction steps require calling test_lib1555 multiple times, but this function does not exist in curl at the checked revision.

deterministic check

Deterministic checks that failed

  • symbol progressCallback() does not exist in curl at 68a44edd500a or in any release since 7.50
  • symbol easy_init exists in other curl releases but not at 68a44edd500a
  • symbol easy_setopt exists in other curl releases but not at 68a44edd500a
  • symbol test_cleanup() does not exist in curl at 68a44edd500a or in any release since 7.50
  • quote only 0/4 quoted lines appear anywhere in curl at 68a44edd500a
  • quote only 0/2 quoted lines appear anywhere in curl at 68a44edd500a
Read the original report (excerpt)

Full report on HackerOne ↗

**Title: Use-After-Free in cURL Test Suite via Improper Cleanup of Global Handle** ```c /*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * SPDX-License-Identifier: curl * ***************************************************************************/ /* * Verify that some API functions are locked from being called inside callback */ #include "first.h" static CURL *t1555_curl; static int progressCallback(void *arg, double dltotal, double dlnow, double ultotal, double ulnow) { CURLcode res = CURLE_OK; char buffer[256]; size_t n = 0; (void)arg; (void)dltotal; (void)dlnow; (void)ultotal; (void)ulnow; res = curl_easy_recv(t1555_curl, buffer, 256, &n); curl_mprintf("curl_easy_recv returned %d\n", res); res = curl_easy_send(t1555_curl, buffer, n, &n); curl_mprintf("curl_easy_send returned %d\n", res); return 1; } static CURLcode test_lib1555(const char *URL) { CURLcode res = CURLE_OK; global_init(CURL_GLOBAL_ALL); easy_init(t1555_curl); easy_setopt(t1555_curl, CURLOPT_URL, URL); easy_setopt(t1555_curl, CURLOPT_TIMEOUT, 7L); easy_setopt(t1555_curl, CURLOPT_NOSIGNAL, 1L); easy_setopt(t1555_curl, CURLOPT_PROGRESSFUNCTION, progressCallback); easy_setopt(t1555_curl, CURLOPT_PROGRESSDATA, NULL); easy_setopt(t1555_curl, CURLOPT_NOPROGRESS, 0L); res = curl_easy_perform(t1555_curl); test_cleanup: /* undocumented cleanup sequence - type UA * …

Source code

lib/easy.clines 855–864 · definition of curl_easy_cleanup
855void curl_easy_cleanup(CURL *ptr)
856{
857 struct Curl_easy *data = ptr;
858 if(GOOD_EASY_HANDLE(data)) {
859 SIGPIPE_VARIABLE(pipe_st);
860 sigpipe_ignore(data, &pipe_st);
861 Curl_close(&data);
862 sigpipe_restore(&pipe_st);
863 }
864}
include/README.mdlines 1–20 · definition of Copyright
1<!--
2Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
3
4SPDX-License-Identifier: curl
5-->
6
7# include
8
9Public include files for libcurl, external users.
10
11They are all placed in the curl subdirectory here for better fit in any kind of
12environment. You must include files from here using...
13
14 #include <curl/curl.h>
15
16... style and point the compiler's include path to the directory holding the
17curl subdirectory. It makes it more likely to survive future modifications.
18
19The public curl include files can be shared freely between different platforms
20and different architectures.
lib/easy.clines 835–838 · definition of curl_easy_perform
Show 4 lines (not cited by the judge)
835CURLcode curl_easy_perform(CURL *data)
836{
837 return easy_perform(data, FALSE);
838}
lib/easy.clines 1230–1252 · definition of curl_easy_recv
Show 23 lines (not cited by the judge)
1230CURLcode curl_easy_recv(CURL *d, void *buffer, size_t buflen, size_t *n)
1231{
1232 CURLcode result;
1233 struct connectdata *c;
1234 struct Curl_easy *data = d;
1235
1236 if(!GOOD_EASY_HANDLE(data))
1237 return CURLE_BAD_FUNCTION_ARGUMENT;
1238 if(Curl_is_in_callback(data))
1239 return CURLE_RECURSIVE_API_CALL;
1240
1241 result = easy_connection(data, &c);
1242 if(result)
1243 return result;
1244
1245 if(!data->conn)
1246 /* on first invoke, the transfer has been detached from the connection and
1247 needs to be reattached */
1248 Curl_attach_connection(data, c);
1249
1250 *n = 0;
1251 return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n);
1252}
lib/easy.clines 1308–1321 · definition of curl_easy_send
Show 14 lines (not cited by the judge)
1308CURLcode curl_easy_send(CURL *d, const void *buffer, size_t buflen, size_t *n)
1309{
1310 size_t written = 0;
1311 CURLcode result;
1312 struct Curl_easy *data = d;
1313 if(!GOOD_EASY_HANDLE(data))
1314 return CURLE_BAD_FUNCTION_ARGUMENT;
1315 if(Curl_is_in_callback(data))
1316 return CURLE_RECURSIVE_API_CALL;
1317
1318 result = Curl_senddata(data, buffer, buflen, &written);
1319 *n = written;
1320 return result;
1321}
lib/easy.clines 274–306 · definition of curl_global_cleanup
Show 33 lines (not cited by the judge)
274void curl_global_cleanup(void)
275{
276 global_init_lock();
277
278 if(!initialized) {
279 global_init_unlock();
280 return;
281 }
282
283 if(--initialized) {
284 global_init_unlock();
285 return;
286 }
287
288 Curl_ssl_cleanup();
289 Curl_async_global_cleanup();
290
291#ifdef _WIN32
292 Curl_win32_cleanup(easy_init_flags);
293#endif
294
295 Curl_amiga_cleanup();
296
297 Curl_ssh_cleanup();
298
299#ifdef DEBUGBUILD
300 curlx_free(leakpointer);
301#endif
302
303 easy_init_flags = 0;
304
305 global_init_unlock();
306}
lib/easy.c@curl-8_17_0lines 276–308 · definition of curl_global_cleanup as in release curl-8_17_0; differs from master; commits that changed it since: 193cb00ce9 build: stop overriding standard memory allocation functions
Show 33 lines (not cited by the judge)
276void curl_global_cleanup(void)
277{
278 global_init_lock();
279
280 if(!initialized) {
281 global_init_unlock();
282 return;
283 }
284
285 if(--initialized) {
286 global_init_unlock();
287 return;
288 }
289
290 Curl_ssl_cleanup();
291 Curl_async_global_cleanup();
292
293#ifdef _WIN32
294 Curl_win32_cleanup(easy_init_flags);
295#endif
296
297 Curl_amiga_cleanup();
298
299 Curl_ssh_cleanup();
300
301#ifdef DEBUGBUILD
302 free(leakpointer);
303#endif
304
305 easy_init_flags = 0;
306
307 global_init_unlock();
308}
lib/mprintf.clines 1222–1230 · definition of curl_mprintf
Show 9 lines (not cited by the judge)
1222int curl_mprintf(const char *format, ...)
1223{
1224 int retcode;
1225 va_list ap_save; /* argument pointer */
1226 va_start(ap_save, format);
1227 retcode = formatf(stdout, fputc_wrapper, format, ap_save);
1228 va_end(ap_save);
1229 return retcode;
1230}
lib/easy.clines 143–210 · definition of global_init
Show 68 lines (not cited by the judge)
143static CURLcode global_init(long flags, bool memoryfuncs)
144{
145 if(initialized++)
146 return CURLE_OK;
147
148 if(memoryfuncs) {
149 /* Setup the default memory functions here (again) */
150 Curl_cmalloc = (curl_malloc_callback)malloc;
151 Curl_cfree = (curl_free_callback)free;
152 Curl_crealloc = (curl_realloc_callback)realloc;
153 Curl_cstrdup = (curl_strdup_callback)system_strdup;
154 Curl_ccalloc = (curl_calloc_callback)calloc;
155 }
156
157 if(Curl_trc_init()) {
158 DEBUGF(curl_mfprintf(stderr, "Error: Curl_trc_init failed\n"));
159 goto fail;
160 }
161
162 if(!Curl_ssl_init()) {
163 DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssl_init failed\n"));
164 goto fail;
165 }
166
167 if(!Curl_vquic_init()) {
168 DEBUGF(curl_mfprintf(stderr, "Error: Curl_vquic_init failed\n"));
169 goto fail;
170 }
171
172 if(Curl_win32_init(flags)) {
173 DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n"));
174 goto fail;
175 }
176
177 if(Curl_amiga_init()) {
178 DEBUGF(curl_mfprintf(stderr, "Error: Curl_amiga_init failed\n"));
179 goto fail;
180 }
181
182 if(Curl_macos_init()) {
183 DEBUGF(curl_mfprintf(stderr, "Error: Curl_macos_init failed\n"));
184 goto fail;
185 }
186
187 if(Curl_async_global_init()) {
188 DEBUGF(curl_mfprintf(stderr, "Error: resolver_global_init failed\n"));
189 goto fail;
190 }
191
192 if(Curl_ssh_init()) {
193 DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssh_init failed\n"));
194 goto fail;
195 }
196
197 easy_init_flags = flags;
198
199#ifdef DEBUGBUILD
200 if(getenv("CURL_GLOBAL_INIT"))
201 /* alloc data that will leak if *cleanup() is not called! */
202 leakpointer = curlx_malloc(1);
203#endif
204
205 return CURLE_OK;
206
207fail:
208 initialized--; /* undo the increase */
209 return CURLE_FAILED_INIT;
210}
lib/easy.c@curl-8_17_0lines 144–211 · definition of global_init as in release curl-8_17_0; differs from master; commits that changed it since: 193cb00ce9 build: stop overriding standard memory allocation functions
Show 68 lines (not cited by the judge)
144static CURLcode global_init(long flags, bool memoryfuncs)
145{
146 if(initialized++)
147 return CURLE_OK;
148
149 if(memoryfuncs) {
150 /* Setup the default memory functions here (again) */
151 Curl_cmalloc = (curl_malloc_callback)malloc;
152 Curl_cfree = (curl_free_callback)free;
153 Curl_crealloc = (curl_realloc_callback)realloc;
154 Curl_cstrdup = (curl_strdup_callback)system_strdup;
155 Curl_ccalloc = (curl_calloc_callback)calloc;
156 }
157
158 if(Curl_trc_init()) {
159 DEBUGF(curl_mfprintf(stderr, "Error: Curl_trc_init failed\n"));
160 goto fail;
161 }
162
163 if(!Curl_ssl_init()) {
164 DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssl_init failed\n"));
165 goto fail;
166 }
167
168 if(!Curl_vquic_init()) {
169 DEBUGF(curl_mfprintf(stderr, "Error: Curl_vquic_init failed\n"));
170 goto fail;
171 }
172
173 if(Curl_win32_init(flags)) {
174 DEBUGF(curl_mfprintf(stderr, "Error: win32_init failed\n"));
175 goto fail;
176 }
177
178 if(Curl_amiga_init()) {
179 DEBUGF(curl_mfprintf(stderr, "Error: Curl_amiga_init failed\n"));
180 goto fail;
181 }
182
183 if(Curl_macos_init()) {
184 DEBUGF(curl_mfprintf(stderr, "Error: Curl_macos_init failed\n"));
185 goto fail;
186 }
187
188 if(Curl_async_global_init()) {
189 DEBUGF(curl_mfprintf(stderr, "Error: resolver_global_init failed\n"));
190 goto fail;
191 }
192
193 if(Curl_ssh_init()) {
194 DEBUGF(curl_mfprintf(stderr, "Error: Curl_ssh_init failed\n"));
195 goto fail;
196 }
197
198 easy_init_flags = flags;
199
200#ifdef DEBUGBUILD
201 if(getenv("CURL_GLOBAL_INIT"))
202 /* alloc data that will leak if *cleanup() is not called! */
203 leakpointer = malloc(1);
204#endif
205
206 return CURLE_OK;
207
208fail:
209 initialized--; /* undo the increase */
210 return CURLE_FAILED_INIT;
211}