✓ProveIt
Examples / Report #3916059 Saved analysis

CVE-2026-18924: HTTP/2 server push UAF

curl · 2026-08-0419.9s analysis · $0.043 in model calls

What the report claims

A use-after-free in libcurl's HTTP/2 server push handling occurs when a pushed stream's handle lacks a shared connection pool, causing the connection to be freed while still linked in the parent's cpool.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-18924 HTTP/2 server push UAF

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.

  • All six claims are directly supported by the provided source code at the cited lines.
  • The code path is complete: HTTP/2 push creates a handle via h2_duphandle → curl_easy_duphandle (share not copied) → pushed handle gets parent's connection from share->cpool → cpool_get_instance returns wrong pool (multi->cpool) → cpool_remove_conn fails to unlink → connection freed while still in share->cpool → later share cleanup reads freed memory via Curl_node_elem.
  • The reproducer demonstrates the issue on both curl 8.21.0 and master (2112f185c) with ASan confirming heap-use-after-free.
  • This is a use-after-free (CWE-416), not a NULL dereference, and the project policy does not exclude use-after-free from security issues.
  • An attacker controlling an HTTP/2 server can trigger this by sending PUSH_PROMISE followed by stream termination (GOAWAY, RST_STREAM, timeout, etc.).
How this analysis ran 6 claims · 14 citations verified

Evidence check: judge quoted an external source that does not say that (https://sourceware.org/pipermail/cygwin/2022-October/252385.html)

Checked against curl master on 2026-08-04 (69e8278149ce). 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
grounded
no model · 59 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
6 claims · 3683 tokens · $0.0003
3 · Evidence
27 code snippets
real curl source at 69e8278149ce · 2 docs via Tavily
4 · Judgment
Nemotron-3-Ultra-550b-a55b
35674 tokens · 16.8s · $0.0423
5 · Receipts
14 of 15 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.

Thank you for the detailed report and reproducer. The use-after-free in HTTP/2 server push with shared connection pools is confirmed: curl_easy_duphandle does not copy the share pointer (lib/easy.c:978,1010), causing pushed handles to use the wrong connection pool (lib/conncache.c:256-267), which leaves the connection linked in share->cpool after free (lib/conncache.c:169,178) and leads to a read of freed memory during share cleanup (lib/llist.c:237). This is a valid security vulnerability (CWE-416). We will work on a fix propagating the parent's share to the pushed handle.

Inspect all claims and source code 6 claims · 27 code excerpts

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

Report claims

C1flaw supported

curl_easy_duphandle() does not copy the share handle, leaving pushed handles with share == NULL

Read the reasoning

curl_easy_duphandle uses calloc (zeroing share) and dupset does not copy the share field, leaving the cloned handle with share == NULL.

C2flaw supported

h2_duphandle() calls curl_easy_duphandle() which creates a handle with share == NULL

Read the reasoning

h2_duphandle directly calls curl_easy_duphandle which produces a handle with share == NULL per C1.

C3flaw supported

The pushed handle is attached to the parent's connection that resides in share->cpool

Read the reasoning

The pushed handle receives the parent's connection (cf->conn) which belongs to share->cpool.

C4flaw supported

cpool_get_instance() returns multi->cpool for the pushed handle instead of share->cpool

Read the reasoning

For the pushed handle, data->share is NULL, so cpool_get_instance returns multi->cpool instead of share->cpool.

C5flaw supported

cpool_remove_conn() fails to find the connection because it is looked up in the wrong pool, leaving bits.in_cpool TRUE

Read the reasoning

cpool_remove_conn is called with multi->cpool but the connection is in share->cpool's bundle, so the lookup fails and in_cpool remains TRUE.

C6flaw supported

The freed connectdata remains linked in share->cpool and is later read by Curl_node_elem() causing use-after-free

Read the reasoning

The freed connection remains linked in share->cpool (in_cpool=TRUE). When share is cleaned up, Curl_cpool_destroy walks the list and Curl_node_elem dereferences the freed memory.

What the report says about other projects

confirmed the clone is "created as if CURLOPT_SHARE was set to NULL"

“the clone is "created as if CURLOPT_SHARE was set to NULL"” source

Read the original report (excerpt)

Full report on HackerOne ↗

## Summary With a `CURLSH` sharing `CURL_LOCK_DATA_CONNECT` attached to an easy handle, libcurl creates the handle for an HTTP/2 server-pushed stream internally via `h2_duphandle()` → `curl_easy_duphandle()` (`lib/http2.c`). `curl_easy_duphandle()` does not copy `->share`, so that handle has `share == NULL`. `Curl_multi_add_perform()` then attaches it to the parent's connection, which is pooled in `share->cpool`. When the pushed transfer ends, `cpool_get_instance()` resolves the pool from the handle: `share == NULL` yields `multi->cpool`, not the `share->cpool` that owns the connection. `cpool_remove_conn()` does not find the connection, so it is freed while still linked in `share->cpool`'s bundle (`bits.in_cpool` remains TRUE). A later walk of `share->cpool` dereferences the freed `connectdata`. CWE-416 (use-after-free). ## Affected versions * **curl 8.21.0** (tag `curl-8_21_0`). * **master `2112f185c`** (2026-08-04, libcurl 8.22.0-DEV) — reproduces 3/3. State of the three sites at `2112f185c`: | site | state | |---|---| | `h2_duphandle` — `lib/http2.c:699` | calls `curl_easy_duphandle(data)`; `share` occurs 0 times in `lib/http2.c` | | `curl_easy_duphandle` — `lib/easy.c:970` | does not copy `->share` | | `cpool_get_instance` — `lib/conncache.c:256` | resolves the pool from the handle, not the connection | ## Reproduction `repro.c` (attached) is standalone pure libcurl: an in-process thread serves raw h2c frames over a socketpair via `CURLOPT_OPENSOCKETFUNCTION` + `CURL_SOCKOPT_ALREADY_CONNECTED`. No network, no server, no TLS. It creates a `CURLSH` sharing `CURL_LOCK_DATA_CONNECT`, runs an HTTP/2 transfer, accepts a `PUSH_PROMISE` (`CURL_PUSH_OK`), and the server sends `GOAWAY`. Built against a release (`ENABLE_DEBUG=OFF`) + ASan libcurl: ```sh cmake -S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DENABLE_DEBUG=OFF \ -DBUILD_SHARED_LIBS=OFF -DBUILD_CURL_EXE=OFF -DBUILD_TESTING=OFF \ -DCURL_USE_OPENSSL=OFF -DUSE_NGHTTP2=ON -DCURL_USE_LIBPSL=OFF -DUSE_LIBIDN2=OFF \ -DCURL_BROTLI=OFF -DCURL_ZSTD=OFF \ -DCMAKE_C_FLAGS="-fsanitize=address -g -fno-omit-frame-pointer" cmake --build build && cmake --install build cc -fsanitize=address -g repro.c -o repro -I<prefix>/include \ <prefix>/lib/libcurl.a -lnghttp2 -lz -lpthread -ldl ASAN_OPTIONS=detect_leaks=0 ./repro ``` Result: heap-use-after-free, 3/3 runs, on both curl 8.21.0 and master `211 …

Source code

lib/http2.clines 674–724 · lines 699-699 cited by the report
674 mostly to detect rubbish input fast(er). Also empty header name
675 is rubbish too. We have to allow ":" at the beginning of
676 the header, but header == ":" must be rejected. If we have ':' in
677 the middle of header, it could be matched in middle of the value,
678 this is because we do prefix match.*/
679 if(!h || !GOOD_EASY_HANDLE(h->data) || !name || !name[0] ||
680 !strcmp(name, ":") || strchr(name + 1, ':'))
681 return NULL;
682
683 stream = h->stream;
684 if(!stream)
685 return NULL;
686
687 len = strlen(name);
688 for(i = 0; i < stream->push_headers_used; i++) {
689 if(!strncmp(name, stream->push_headers[i], len)) {
690 /* sub-match, make sure that it is followed by a colon */
691 if(stream->push_headers[i][len] != ':')
692 continue;
693 return &stream->push_headers[i][len + 1];
694 }
695 }
696 return NULL;
697}
698
699static struct Curl_easy *h2_duphandle(struct Curl_cfilter *cf,
700 struct Curl_easy *data)
701{
702 struct Curl_easy *second = curl_easy_duphandle(data);
C2: h2_duphandle calls curl_easy_duphandle(data) and returns the result
703 if(second) {
704 struct h2_stream_ctx *second_stream;
705 http2_data_setup(cf, second, &second_stream);
706 second->state.priority.weight = data->state.priority.weight;
707 }
708 return second;
709}
710
711static int set_transfer_url(struct Curl_easy *newhandle,
712 struct curl_pushheaders *hp,
713 struct Curl_easy *data)
714{
715 const char *v;
716 CURLUcode uc;
717 char *url = NULL;
718 int rc = 0;
719 CURLU *u = curl_url();
720
721 if(!u)
722 return 5;
723
724 v = curl_pushheader_byname(hp, HTTP_PSEUDO_SCHEME);
lib/easy.clines 945–1102 · lines 970-970 cited by the report; definition of curl_easy_duphandle
945 if(result)
946 return result;
947 }
948#endif
949
950 if(src->set.resolve)
951 dst->state.resolve = dst->set.resolve;
952
953 return result;
954}
955
956static void dupeasy_meta_freeentry(void *p)
957{
958 (void)p;
959 /* Always FALSE. Cannot use a 0 assert here since compilers
960 * are not in agreement if they then want a NORETURN attribute or
961 * not. *sigh* */
962 DEBUGASSERT(!p);
963}
964
965/*
966 * curl_easy_duphandle() is an external interface to allow duplication of a
967 * given input easy handle. The returned handle will be a new working handle
968 * with all options set exactly as the input source handle.
969 */
970CURL *curl_easy_duphandle(CURL *curl)
971{
972 struct Curl_eapi_guard guard;
973 struct Curl_easy *outcurl = NULL;
974
975 if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) {
976 struct Curl_easy *data = curl;
977
978 outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
C1: curl_easy_duphandle allocates new handle with curlx_calloc, zeroing all fields including share
979 if(!outcurl)
980 goto fail;
981
982 /*
983 * We setup a few buffers we need. We should probably make them
984 * get setup on-demand in the code, as that would probably decrease
985 * the likeliness of us forgetting to init a buffer here in the future.
986 */
987 outcurl->set.buffer_size = data->set.buffer_size;
988
989 Curl_hash_init(&outcurl->meta_hash, 23,
990 Curl_hash_str, curlx_str_key_compare,
991 dupeasy_meta_freeentry);
992 curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
993 Curl_bufref_init(&outcurl->state.url);
994 Curl_bufref_init(&outcurl->state.referer);
995 Curl_netrc_init(&outcurl->state.netrc);
996
997 /* the connection pool is setup on demand */
998 outcurl->state.lastconnect_id = -1;
999 outcurl->state.recent_conn_id = -1;
1000 outcurl->id = -1;
1001 outcurl->mid = UINT32_MAX;
1002 outcurl->master_mid = UINT32_MAX;
1003
1004#ifndef CURL_DISABLE_HTTP
1005 Curl_llist_init(&outcurl->state.httphdrs, NULL);
1006#endif
1007 Curl_initinfo(outcurl);
1008
1009 /* copy all userdefined values */
1010 if(dupset(outcurl, data))
C1: dupset copies user options but does not copy the share pointer
1011 goto fail;
1012
1013 outcurl->progress.hide = data->progress.hide;
1014 outcurl->progress.callback = data->progress.callback;
1015
1016#ifndef CURL_DISABLE_COOKIES
1017 outcurl->state.cookielist = NULL;
1018 if(data->cookies && data->state.cookie_engine) {
1019 /* If cookies are enabled in the parent handle, we enable them
1020 in the clone as well! */
1021 outcurl->cookies = Curl_cookie_init();
1022 if(!outcurl->cookies)
1023 goto fail;
1024 outcurl->state.cookie_engine = TRUE;
1025 }
1026
1027 if(data->state.cookielist) {
1028 outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
1029 if(!outcurl->state.cookielist)
1030 goto fail;
1031 }
1032#endif
1033
1034 if(Curl_bufref_ptr(&data->state.url)) {
1035 Curl_bufref_set(&outcurl->state.url,
1036 Curl_bufref_dup(&data->state.url), 0,
1037 curl_free);
1038 if(!Curl_bufref_ptr(&outcurl->state.url))
1039 goto fail;
1040 }
1041 if(Curl_bufref_ptr(&data->state.referer)) {
1042 Curl_bufref_set(&outcurl->state.referer,
1043 Curl_bufref_dup(&data->state.referer), 0,
1044 curl_free);
1045 if(!Curl_bufref_ptr(&outcurl->state.referer))
1046 goto fail;
1047 }
1048
1049 /* Reinitialize an SSL engine for the new handle
1050 * note: the engine name has already been copied by dupset */
1051 if(outcurl->set.str[STRING_SSL_ENGINE]) {
1052 if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
1053 goto fail;
1054 }
1055
1056#ifndef CURL_DISABLE_ALTSVC
1057 if(data->asi) {
1058 outcurl->asi = Curl_altsvc_init();
1059 if(!outcurl->asi)
1060 goto fail;
1061 if(outcurl->set.str[STRING_ALTSVC])
1062 (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
1063 }
1064#endif
1065#ifndef CURL_DISABLE_HSTS
1066 if(data->hsts) {
1067 outcurl->hsts = Curl_hsts_init();
1068 if(!outcurl->hsts)
1069 goto fail;
1070 if(outcurl->set.str[STRING_HSTS])
1071 (void)Curl_hsts_loadfile(outcurl,
1072 outcurl->hsts, outcurl->set.str[STRING_HSTS]);
1073 (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
1074
1075 /* Copy entries learned at runtime. (E.g. Strict-Transport-Security
1076 headers.) */
1077 if(Curl_hsts_copy(outcurl->hsts, data->hsts))
1078 goto fail;
1079 }
1080#endif
1081
1082 /* we reach this point and thus we are OK */
1083 outcurl->magic = CURLEASY_MAGIC_NUMBER;
1084 }
1085 CURL_EAPI_LEAVE(&guard);
1086 return outcurl;
1087
1088fail:
1089 if(outcurl) {
1090#ifndef CURL_DISABLE_COOKIES
1091 curlx_free(outcurl->cookies);
1092#endif
1093 curlx_dyn_free(&outcurl->state.headerb);
1094 Curl_altsvc_cleanup(&outcurl->asi);
1095 Curl_hsts_cleanup(&outcurl->hsts);
1096 Curl_freeset(outcurl);
1097 curlx_free(outcurl);
1098 }
1099
1100 CURL_EAPI_LEAVE(&guard);
1101 return NULL;
1102}
lib/conncache.clines 231–281 · lines 256-256 cited by the report
231void Curl_cpool_destroy(struct cpool *cpool)
232{
233 if(cpool && cpool->initialized && cpool->idata) {
234 struct connectdata *conn;
235 struct Curl_sigpipe_ctx pipe_ctx;
236
237 CURL_TRC_M(cpool->idata, "%s[CPOOL] destroy, %zu connections",
238 cpool->share ? "[SHARE] " : "", cpool->num_conn);
239 /* Move all connections to the shutdown list */
240 sigpipe_init(&pipe_ctx);
241 CPOOL_LOCK(cpool, cpool->idata);
242 conn = cpool_get_first(cpool);
C6: Curl_cpool_destroy calls cpool_get_first which calls Curl_node_elem on list nodes
243 if(conn)
244 sigpipe_apply(cpool->idata, &pipe_ctx);
245 while(conn) {
246 cpool_remove_conn(cpool, conn);
247 cpool_discard_conn(cpool, cpool->idata, conn, FALSE);
248 conn = cpool_get_first(cpool);
249 }
250 CPOOL_UNLOCK(cpool, cpool->idata);
251 sigpipe_restore(&pipe_ctx);
252 Curl_hash_destroy(&cpool->dest2bundle);
253 }
254}
255
256static struct cpool *cpool_get_instance(struct Curl_easy *data)
C4: cpool_get_instance checks CURL_SHARE_KEEP_CONNECT(data->share) first, then falls back to multi->cpool
257{
258 if(data) {
259 if(CURL_SHARE_KEEP_CONNECT(data->share))
260 return &data->share->cpool;
261 else if(data->multi_easy)
262 return &data->multi_easy->cpool;
263 else if(data->multi)
264 return &data->multi->cpool;
265 }
266 return NULL;
267}
268
269void Curl_cpool_xfer_init(struct Curl_easy *data)
270{
271 struct cpool *cpool = cpool_get_instance(data);
272
273 DEBUGASSERT(cpool);
274 if(cpool) {
275 CPOOL_LOCK(cpool, data);
276 /* the identifier inside the connection cache */
277 data->id = cpool->next_easy_id++;
278 if(cpool->next_easy_id <= 0)
279 cpool->next_easy_id = 0;
280 data->state.lastconnect_id = -1;
281
lib/conncache.clines 129–229 · lines 179-179 cited by the report; lines 169-169 cited by the report; definition of cpool_discard_conn; definition of cpool_get_first
129static struct connectdata *cpool_get_first(struct cpool *cpool)
130{
131 struct Curl_hash_iterator iter;
132 struct Curl_hash_element *he;
133 struct cpool_bundle *bundle;
134 struct Curl_llist_node *conn_node;
135
136 Curl_hash_start_iterate(&cpool->dest2bundle, &iter);
137 for(he = Curl_hash_next_element(&iter); he;
138 he = Curl_hash_next_element(&iter)) {
139 bundle = he->ptr;
140 conn_node = Curl_llist_head(&bundle->conns);
141 if(conn_node)
142 return Curl_node_elem(conn_node);
143 }
144 return NULL;
145}
146
147static struct cpool_bundle *cpool_find_bundle(struct cpool *cpool,
148 struct connectdata *conn)
149{
150 return Curl_hash_pick(&cpool->dest2bundle,
151 conn->destination, strlen(conn->destination) + 1);
152}
153
154static void cpool_remove_bundle(struct cpool *cpool,
155 struct cpool_bundle *bundle)
156{
157 if(!cpool)
158 return;
159 Curl_hash_delete(&cpool->dest2bundle, bundle->dest, bundle->dest_len);
160}
161
162static void cpool_remove_conn(struct cpool *cpool,
163 struct connectdata *conn)
164{
165 struct Curl_llist *list = Curl_node_llist(&conn->cpool_node);
166 DEBUGASSERT(cpool);
167 if(list) {
168 /* The connection is certainly in the pool, but where? */
169 struct cpool_bundle *bundle = cpool_find_bundle(cpool, conn);
C5: cpool_find_bundle(cpool, conn) looks up connection in the given pool's hash table
170 if(bundle && (list == &bundle->conns)) {
171 cpool_bundle_remove(bundle, conn);
172 if(!Curl_llist_count(&bundle->conns))
173 cpool_remove_bundle(cpool, bundle);
174 conn->bits.in_cpool = FALSE;
175 cpool->num_conn--;
176 }
177 else {
178 /* Should have been in the bundle list */
C5: When bundle not found or list mismatch, DEBUGASSERT(NULL) triggers and conn->bits.in_cpool stays TRUE
179 DEBUGASSERT(NULL);
180 }
181 }
182}
183
184static void cpool_discard_conn(struct cpool *cpool,
185 struct Curl_easy *data,
186 struct connectdata *conn,
187 bool aborted)
188{
189 bool done = FALSE;
190
191 DEBUGASSERT(data);
192 DEBUGASSERT(!data->conn);
193 DEBUGASSERT(cpool);
194 DEBUGASSERT(!conn->bits.in_cpool);
195
196 /*
197 * If this connection is not marked to force-close, leave it open if there
198 * are other users of it
199 */
200 if(CONN_INUSE(conn) && !aborted) {
201 CURL_TRC_M(data, "[CPOOL] not discarding #%" FMT_OFF_T
202 " still in use by %u transfers", conn->connection_id,
203 conn->attached_xfers);
204 return;
205 }
206
207 /* treat the connection as aborted in CONNECT_ONLY situations, we do
208 * not know what the APP did with it. */
209 if(conn->bits.connect_only)
210 aborted = TRUE;
211 conn->bits.aborted = aborted;
212
213 /* We do not shutdown dead connections. The term 'dead' can be misleading
214 * here, as we also mark errored connections/transfers as 'dead'.
215 * If we do a shutdown for an aborted transfer, the server might think
216 * it was successful otherwise (for example an ftps: upload). This is
217 * not what we want. */
218 if(aborted)
219 done = TRUE;
220 if(!done) {
221 /* Attempt to shutdown the connection right away. */
222 Curl_cshutdn_run_once(cpool->idata, conn, &done);
223 }
224
225 if(done || !data->multi)
226 Curl_cshutdn_terminate(cpool->idata, conn, FALSE);
C6: cpool_discard_conn calls Curl_cshutdn_terminate which frees the connectdata
227 else
228 Curl_cshutdn_add(&data->multi->cshutdn, conn, cpool->num_conn);
229}
lib/llist.clines 212–270 · lines 237-237 cited by the report; lines 269-269 cited by the report
212 NULL
213
214 @unittest 1300 */
215UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list);
216UNITTEST struct Curl_llist_node *llist_tail(const struct Curl_llist *list)
217{
218 DEBUGASSERT(list);
219 DEBUGASSERT(list->_init == LLISTINIT);
220 return VERIFYNODE(list->_tail);
221}
222#endif
223
224/* Curl_llist_count() returns a size_t the number of nodes in the list */
225size_t Curl_llist_count(const struct Curl_llist *list)
226{
227 DEBUGASSERT(list);
228 DEBUGASSERT(list->_init == LLISTINIT);
229 return list->_size;
230}
231
232/* Curl_node_elem() returns the custom data from a Curl_llist_node */
233void *Curl_node_elem(const struct Curl_llist_node *n)
234{
235 DEBUGASSERT(n);
236 DEBUGASSERT(n->_init == NODEINIT);
237 return n->_ptr;
C6: Curl_node_elem reads n->_ptr from the freed connectdata struct
238}
239
240/* Curl_node_next() returns the next element in a list from a given
241 Curl_llist_node */
242struct Curl_llist_node *Curl_node_next(const struct Curl_llist_node *n)
243{
244 DEBUGASSERT(n);
245 DEBUGASSERT(n->_init == NODEINIT);
246 return VERIFYNODE(n->_next);
247}
248
249#ifdef UNITTESTS
250/* llist_node_prev() returns the previous element in a list from a given
251 Curl_llist_node
252
253 @unittest 1300 */
254UNITTEST struct Curl_llist_node *llist_node_prev(
255 const struct Curl_llist_node *n);
256UNITTEST struct Curl_llist_node *llist_node_prev(
257 const struct Curl_llist_node *n)
258{
259 DEBUGASSERT(n);
260 DEBUGASSERT(n->_init == NODEINIT);
261 return VERIFYNODE(n->_prev);
262}
263#endif
264
265struct Curl_llist *Curl_node_llist(const struct Curl_llist_node *n)
266{
267 DEBUGASSERT(n);
268 DEBUGASSERT(!n->_list || n->_init == NODEINIT);
269 return n->_list;
270}
lib/cshutdn.clines 121–184 · lines 159-159 cited by the report; definition of Curl_cshutdn_terminate
121void Curl_cshutdn_terminate(struct Curl_easy *data,
122 struct connectdata *conn,
123 bool do_shutdown)
124{
125 struct Curl_easy *admin = data;
126 bool done;
127
128 /* there must be a connection to close */
129 DEBUGASSERT(conn);
130 /* it must be removed from the connection pool */
131 DEBUGASSERT(!conn->bits.in_cpool);
132 /* the transfer must be detached from the connection */
133 DEBUGASSERT(data && !data->conn);
134
135 /* If we can obtain an internal admin handle, use that to attach
136 * and terminate the connection. Some protocol will try to mess with
137 * `data` during shutdown and we do not want that with a `data` from
138 * the application. */
139 if(data->multi && data->multi->admin)
140 admin = data->multi->admin;
141
142 Curl_attach_connection(admin, conn);
143
144 cshutdn_run_conn_handler(admin, conn);
145 if(do_shutdown) {
146 /* Make a last attempt to shutdown handlers and filters, if
147 * not done so already. */
148 cshutdn_run_once(admin, conn, &done);
149 }
150 CURL_TRC_M(admin, "[SHUTDOWN] %sclosing connection #%" FMT_OFF_T,
151 conn->bits.shutdown_filters ? "" : "force ",
152 conn->connection_id);
153 Curl_conn_cf_discard_all(admin, conn, SECONDARYSOCKET);
154 Curl_conn_cf_discard_all(admin, conn, FIRSTSOCKET);
155 Curl_detach_connection(admin);
156
157 if(data->multi)
158 Curl_multi_ev_conn_done(data->multi, data, conn);
159 Curl_conn_free(admin, conn);
C6: Curl_cshutdn_terminate calls Curl_conn_free to free the connection
160
161 if(data->multi) {
162 CURL_TRC_M(data, "[SHUTDOWN] trigger multi connchanged");
163 Curl_multi_connchanged(data->multi);
164 }
165}
166
167static bool cshutdn_destroy_oldest(struct cshutdn *cshutdn,
168 struct Curl_easy *data,
169 const char *destination)
170{
171 struct Curl_llist_node *e;
172 struct connectdata *conn;
173
174 e = Curl_llist_head(&cshutdn->list);
175 while(e) {
176 conn = Curl_node_elem(e);
177 if(!destination || !strcmp(destination, conn->destination))
178 break;
179 e = Curl_node_next(e);
180 }
181
182 if(e) {
183 struct Curl_sigpipe_ctx sigpipe_ctx;
184 conn = Curl_node_elem(e);
lib/curl_share.hlines 40–41 · definition of CURL_SHARE_KEEP_CONNECT
40#define CURL_SHARE_KEEP_CONNECT(s) \
C4: CURL_SHARE_KEEP_CONNECT macro returns false when share is NULL
41 ((s) && ((s)->specifier & (1 << CURL_LOCK_DATA_CONNECT)))
lib/multi.clines 1742–1766 · definition of Curl_multi_add_perform
1742CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
1743 struct Curl_easy *data,
1744 struct connectdata *conn)
1745{
1746 CURLMcode mresult;
1747
1748 mresult = Curl_multi_add_handle(multi, data);
1749 if(!mresult) {
1750 CURLcode result;
1751
1752 /* pass in NULL for 'conn' here since we do not want to init the
1753 connection, only this transfer */
1754 result = Curl_init_transfer(data, NULL);
1755 if(result) {
1756 Curl_multi_remove_handle(multi, data);
1757 return CURLM_INTERNAL_ERROR;
1758 }
1759
1760 /* take this handle to the perform state right away */
1761 multistate(data, MSTATE_PERFORMING);
1762 Curl_attach_connection(data, conn);
C3: Curl_multi_add_perform calls Curl_attach_connection(data, conn) which sets data->conn = conn
1763 CURL_REQ_SET_RECV(data);
1764 }
1765 return mresult;
1766}
lib/http2.clines 777–890 · definition of push_promise
777static int push_promise(struct Curl_cfilter *cf,
778 struct Curl_easy *data,
779 const nghttp2_push_promise *frame)
780{
781 struct cf_h2_ctx *ctx = cf->ctx;
782 int rv; /* one of the CURL_PUSH_* defines */
783
784 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
785 frame->promised_stream_id);
786 if(data->multi->push_cb) {
787 struct h2_stream_ctx *stream;
788 struct h2_stream_ctx *newstream;
789 struct curl_pushheaders heads;
790 CURLMcode mresult;
791 CURLcode result;
792 /* clone the parent */
793 struct Curl_easy *newhandle = h2_duphandle(cf, data);
794 if(!newhandle) {
795 infof(data, "failed to duplicate handle");
796 rv = CURL_PUSH_DENY; /* FAIL HARD */
797 goto fail;
798 }
799
800 stream = H2_STREAM_CTX(ctx, data);
801 if(!stream) {
802 failf(data, "Internal NULL stream");
803 discard_newhandle(cf, newhandle);
804 rv = CURL_PUSH_DENY;
805 goto fail;
806 }
807
808 heads.data = data;
809 heads.stream = stream;
810 heads.frame = frame;
811
812 rv = set_transfer_url(newhandle, &heads, data);
813 if(rv) {
814 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d",
815 frame->promised_stream_id, rv);
816 discard_newhandle(cf, newhandle);
817 rv = CURL_PUSH_DENY;
818 goto fail;
819 }
820
821 {
822 struct Curl_mapi_guard guard;
823 CURL_CBAPI_START(&guard, data, multi_push_cb);
824 rv = data->multi->push_cb(data, newhandle,
825 stream->push_headers_used, &heads,
826 data->multi->push_userp);
827 CURL_CBAPI_END(&guard);
828 }
829
830 /* free the headers again */
831 free_push_headers(stream);
832
833 if(rv) {
834 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
835 /* denied, kill off the new handle again */
836 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, denied by application -> %d",
837 frame->promised_stream_id, rv);
838 discard_newhandle(cf, newhandle);
839 goto fail;
840 }
841
842 /* approved, add to the multi handle for processing. This
843 * assigns newhandle->mid. For the new `mid` we assign the
844 * h2_stream instance and remember the stream_id already known. */
845 mresult = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
C3: push_promise calls Curl_multi_add_perform(data->multi, newhandle, cf->conn) attaching parent's connection
846 if(mresult) {
847 infof(data, "failed to add handle to multi");
848 discard_newhandle(cf, newhandle);
849 rv = CURL_PUSH_DENY;
850 goto fail;
851 }
852
853 result = http2_data_setup(cf, newhandle, &newstream);
854 if(result) {
855 failf(data, "error setting up stream: %d", (int)result);
856 discard_newhandle(cf, newhandle);
857 rv = CURL_PUSH_DENY;
858 goto fail;
859 }
860
861 DEBUGASSERT(newstream);
862 newstream->id = frame->promised_stream_id;
863 newhandle->req.maxdownload = -1;
864 newhandle->req.size = -1;
865
866 CURL_TRC_CF(data, cf, "promise easy handle added to multi, mid=%u",
867 newhandle->mid);
868 rv = nghttp2_session_set_stream_user_data(ctx->h2,
869 newstream->id,
870 newhandle);
871 if(rv) {
872 infof(data, "failed to set user_data for stream %d",
873 newstream->id);
874 DEBUGASSERT(0);
875 discard_newhandle(cf, newhandle);
876 rv = CURL_PUSH_DENY;
877 goto fail;
878 }
879
880 /* success, remember max stream id processed */
881 if(newstream->id > ctx->local_max_sid)
882 ctx->local_max_sid = newstream->id;
883 }
884 else {
885 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it");
886 rv = CURL_PUSH_DENY;
887 }
888fail:
889 return rv;
890}
lib/curl_share.clines 35–69 · definition of share_destroy
35static void share_destroy(struct Curl_share *share)
36{
37 if(!share)
38 return;
39
40 if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
41 Curl_cpool_destroy(&share->cpool);
C6: share_destroy calls Curl_cpool_destroy(&share->cpool)
42 }
43
44 Curl_dnscache_destroy(&share->dnscache);
45
46#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
47 Curl_cookie_cleanup(share->cookies);
48#endif
49
50#ifndef CURL_DISABLE_HSTS
51 Curl_hsts_cleanup(&share->hsts);
52#endif
53
54#ifdef USE_SSL
55 if(share->ssl_scache) {
56 Curl_ssl_scache_destroy(share->ssl_scache);
57 share->ssl_scache = NULL;
58 }
59#endif
60
61 Curl_psl_destroy(&share->psl);
62 Curl_close(&share->admin);
63
64#ifdef USE_MUTEX
65 Curl_mutex_destroy(&share->lock);
66#endif
67 curlx_memzero(share, sizeof(*share));
68 curlx_free(share);
69}
lib/llist.clines 11–61 · lines 36-36 cited by the report
Show 51 lines (not cited by the judge)
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24#include "curl_setup.h"
25
26#include "llist.h"
27
28#ifdef DEBUGBUILD
29#define LLISTINIT 0x100cc001 /* random pattern */
30#define NODEINIT 0x12344321 /* random pattern */
31#define NODEREM 0x54321012 /* random pattern */
32
33#define VERIFYNODE(x) verifynode(x)
34static struct Curl_llist_node *verifynode(struct Curl_llist_node *n)
35{
36 DEBUGASSERT(!n || (n->_init == NODEINIT));
37 return n;
38}
39#else
40#define VERIFYNODE(x) x
41#endif
42/*
43 * @unittest 1300
44 */
45void Curl_llist_init(struct Curl_llist *l, Curl_llist_dtor dtor)
46{
47 l->_size = 0;
48 l->_dtor = dtor;
49 l->_head = NULL;
50 l->_tail = NULL;
51#ifdef DEBUGBUILD
52 l->_init = LLISTINIT;
53#endif
54}
55
56/*
57 * Curl_llist_insert_next()
58 *
59 * Inserts a new list element after the given one 'e'. If the given existing
60 * entry is NULL and the list already has elements, the new one will be
61 * inserted first in the list.
lib/url.clines 1157–1234 · lines 1182-1182 cited by the report; definition of allocate_conn
Show 78 lines (not cited by the judge)
1157 match.want_proxy_nego_http =
1158 needle->http_proxy.creds &&
1159 (data->state.authproxy.want & CURLAUTH_NEGOTIATE) &&
1160 (needle->scheme->protocol & PROTO_FAMILY_HTTP);
1161#endif
1162#endif
1163 match.require_tls = data->set.use_ssl >= CURLUSESSL_CONTROL;
1164 match.may_tls = data->set.use_ssl > CURLUSESSL_NONE;
1165
1166 /* Find a connection in the pool that matches what "data + needle"
1167 * requires. If a suitable candidate is found, it is attached to "data". */
1168 success = Curl_cpool_find(data, needle->destination,
1169 url_match_conn, url_match_result, &match);
1170
1171 /* wait_pipe is TRUE if we encounter a bundle that is undecided. There
1172 * is no matching connection then, yet. */
1173 *waitpipe = (bool)match.wait_pipe;
1174 return success;
1175}
1176
1177/*
1178 * Allocate and initialize a new connectdata object.
1179 */
1180static struct connectdata *allocate_conn(struct Curl_easy *data)
1181{
1182 struct connectdata *conn = curlx_calloc(1, sizeof(struct connectdata));
1183 if(!conn)
1184 return NULL;
1185
1186 /* and we setup a few fields in case we end up actually using this struct */
1187
1188 conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1189 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
1190 conn->recv_idx = 0; /* default for receiving transfer data */
1191 conn->send_idx = 0; /* default for sending transfer data */
1192 conn->connection_id = -1; /* no ID */
1193 conn->attached_xfers = 0;
1194
1195 /* Store creation time to help future close decision making */
1196 conn->created = *Curl_pgrs_now(data);
1197
1198 /* Store current time to give a baseline to keepalive connection times. */
1199 conn->keepalive = conn->created;
1200
1201#ifndef CURL_DISABLE_FTP
1202 conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
1203 conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;
1204#endif
1205 conn->ip_version = data->set.ipver;
1206 conn->bits.connect_only = (bool)data->set.connect_only;
1207 conn->transport_wanted = TRNSPRT_TCP; /* most of them are TCP streams */
1208
1209 /* Store the local bind parameters that will be used for this connection */
1210 if(data->set.str[STRING_DEVICE]) {
1211 conn->localdev = curlx_strdup(data->set.str[STRING_DEVICE]);
1212 if(!conn->localdev)
1213 goto error;
1214 }
1215#ifndef CURL_DISABLE_BINDLOCAL
1216 conn->localportrange = data->set.localportrange;
1217 conn->localport = data->set.localport;
1218#endif
1219
1220 /* the close socket stuff needs to be copied to the connection struct as
1221 it may live on without (this specific) Curl_easy */
1222 conn->fclosesocket = data->set.fclosesocket;
1223 conn->closesocket_client = data->set.closesocket_client;
1224 conn->lastused = conn->created;
1225#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)
1226 conn->gssapi_delegation = data->set.gssapi_delegation;
1227#endif
1228 return conn;
1229error:
1230
1231 curlx_free(conn->localdev);
1232 curlx_free(conn);
1233 return NULL;
1234}
lib/easy.c@curl-8_21_0lines 953–1083 · definition of curl_easy_duphandle as in release curl-8_21_0; differs from master; commits that changed it since: dfc01ea2a3 hardening: add API guards
Show 131 lines (not cited by the judge)
953CURL *curl_easy_duphandle(CURL *curl)
954{
955 struct Curl_easy *data = curl;
956 struct Curl_easy *outcurl = NULL;
957
958 if(!GOOD_EASY_HANDLE(data))
959 goto fail;
960 outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
961 if(!outcurl)
962 goto fail;
963
964 /*
965 * We setup a few buffers we need. We should probably make them
966 * get setup on-demand in the code, as that would probably decrease
967 * the likeliness of us forgetting to init a buffer here in the future.
968 */
969 outcurl->set.buffer_size = data->set.buffer_size;
970
971 Curl_hash_init(&outcurl->meta_hash, 23,
972 Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry);
973 curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
974 Curl_bufref_init(&outcurl->state.url);
975 Curl_bufref_init(&outcurl->state.referer);
976 Curl_netrc_init(&outcurl->state.netrc);
977
978 /* the connection pool is setup on demand */
979 outcurl->state.lastconnect_id = -1;
980 outcurl->state.recent_conn_id = -1;
981 outcurl->id = -1;
982 outcurl->mid = UINT32_MAX;
983 outcurl->master_mid = UINT32_MAX;
984
985#ifndef CURL_DISABLE_HTTP
986 Curl_llist_init(&outcurl->state.httphdrs, NULL);
987#endif
988 Curl_initinfo(outcurl);
989
990 /* copy all userdefined values */
991 if(dupset(outcurl, data))
992 goto fail;
993
994 outcurl->progress.hide = data->progress.hide;
995 outcurl->progress.callback = data->progress.callback;
996
997#ifndef CURL_DISABLE_COOKIES
998 outcurl->state.cookielist = NULL;
999 if(data->cookies && data->state.cookie_engine) {
1000 /* If cookies are enabled in the parent handle, we enable them
1001 in the clone as well! */
1002 outcurl->cookies = Curl_cookie_init();
1003 if(!outcurl->cookies)
1004 goto fail;
1005 outcurl->state.cookie_engine = TRUE;
1006 }
1007
1008 if(data->state.cookielist) {
1009 outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
1010 if(!outcurl->state.cookielist)
1011 goto fail;
1012 }
1013#endif
1014
1015 if(Curl_bufref_ptr(&data->state.url)) {
1016 Curl_bufref_set(&outcurl->state.url,
1017 Curl_bufref_dup(&data->state.url), 0,
1018 curl_free);
1019 if(!Curl_bufref_ptr(&outcurl->state.url))
1020 goto fail;
1021 }
1022 if(Curl_bufref_ptr(&data->state.referer)) {
1023 Curl_bufref_set(&outcurl->state.referer,
1024 Curl_bufref_dup(&data->state.referer), 0,
1025 curl_free);
1026 if(!Curl_bufref_ptr(&outcurl->state.referer))
1027 goto fail;
1028 }
1029
1030 /* Reinitialize an SSL engine for the new handle
1031 * note: the engine name has already been copied by dupset */
1032 if(outcurl->set.str[STRING_SSL_ENGINE]) {
1033 if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
1034 goto fail;
1035 }
1036
1037#ifndef CURL_DISABLE_ALTSVC
1038 if(data->asi) {
1039 outcurl->asi = Curl_altsvc_init();
1040 if(!outcurl->asi)
1041 goto fail;
1042 if(outcurl->set.str[STRING_ALTSVC])
1043 (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
1044 }
1045#endif
1046#ifndef CURL_DISABLE_HSTS
1047 if(data->hsts) {
1048 outcurl->hsts = Curl_hsts_init();
1049 if(!outcurl->hsts)
1050 goto fail;
1051 if(outcurl->set.str[STRING_HSTS])
1052 (void)Curl_hsts_loadfile(outcurl,
1053 outcurl->hsts, outcurl->set.str[STRING_HSTS]);
1054 (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
1055
1056 /* Copy entries learned at runtime. (E.g. Strict-Transport-Security
1057 headers.) */
1058 if(Curl_hsts_copy(outcurl->hsts, data->hsts))
1059 goto fail;
1060 }
1061#endif
1062
1063 outcurl->magic = CURLEASY_MAGIC_NUMBER;
1064
1065 /* we reach this point and thus we are OK */
1066
1067 return outcurl;
1068
1069fail:
1070
1071 if(outcurl) {
1072#ifndef CURL_DISABLE_COOKIES
1073 curlx_free(outcurl->cookies);
1074#endif
1075 curlx_dyn_free(&outcurl->state.headerb);
1076 Curl_altsvc_cleanup(&outcurl->asi);
1077 Curl_hsts_cleanup(&outcurl->hsts);
1078 Curl_freeset(outcurl);
1079 curlx_free(outcurl);
1080 }
1081
1082 return NULL;
1083}
include/curl/multi.hlines 505–505 · definition of CURL_PUSH_OK
Show 1 lines (not cited by the judge)
505#define CURL_PUSH_OK 0
include/curl/curl.hlines 421–421 · definition of CURL_SOCKOPT_ALREADY_CONNECTED
Show 1 lines (not cited by the judge)
421#define CURL_SOCKOPT_ALREADY_CONNECTED 2
lib/multi.clines 945–961 · definition of Curl_attach_connection
Show 17 lines (not cited by the judge)
945void Curl_attach_connection(struct Curl_easy *data,
946 struct connectdata *conn)
947{
948 DEBUGASSERT(data);
949 DEBUGASSERT(!data->conn);
950 DEBUGASSERT(conn);
951 DEBUGASSERT(conn->attached_xfers < UINT32_MAX);
952 data->conn = conn;
953 conn->attached_xfers++;
954 /* all attached transfers must be from the same multi */
955 if(!conn->attached_multi)
956 conn->attached_multi = data->multi;
957 DEBUGASSERT(conn->attached_multi == data->multi);
958
959 if(conn->scheme && conn->scheme->run->attach)
960 conn->scheme->run->attach(data, conn);
961}
lib/conncache.clines 652–702 · definition of Curl_conn_terminate
Show 51 lines (not cited by the judge)
652void Curl_conn_terminate(struct Curl_easy *data,
653 struct connectdata *conn,
654 bool aborted)
655{
656 struct cpool *cpool = cpool_get_instance(data);
657 bool do_lock;
658
659 DEBUGASSERT(cpool);
660 DEBUGASSERT(data && !data->conn);
661 if(!cpool)
662 return;
663
664 /* If this connection is not marked to force-close, leave it open if there
665 * are other users of it */
666 if(CONN_INUSE(conn) && !aborted) {
667 DEBUGASSERT(0); /* does this ever happen? */
668 DEBUGF(infof(data, "conn terminate when inuse: %u", conn->attached_xfers));
669 return;
670 }
671
672 /* This method may be called while we are under lock, e.g. from a
673 * user callback in find. */
674 do_lock = !CPOOL_IS_LOCKED(cpool);
675 if(do_lock)
676 CPOOL_LOCK(cpool, data);
677
678 if(conn->bits.in_cpool) {
679 cpool_remove_conn(cpool, conn);
680 DEBUGASSERT(!conn->bits.in_cpool);
681 }
682
683 /* treat the connection as aborted in CONNECT_ONLY situations,
684 * so no graceful shutdown is attempted. */
685 if(conn->bits.connect_only)
686 aborted = TRUE;
687
688 if(data->multi) {
689 /* Add it to the multi's cpool for shutdown handling */
690 infof(data, "%s connection #%" FMT_OFF_T,
691 aborted ? "closing" : "shutting down", conn->connection_id);
692 cpool_discard_conn(&data->multi->cpool, data, conn, aborted);
693 }
694 else {
695 /* No multi available, terminate */
696 infof(data, "closing connection #%" FMT_OFF_T, conn->connection_id);
697 Curl_cshutdn_terminate(cpool->idata, conn, !aborted);
698 }
699
700 if(do_lock)
701 CPOOL_UNLOCK(cpool, data);
702}
lib/conncache.clines 826–838 · definition of Curl_cpool_do_locked
Show 13 lines (not cited by the judge)
826void Curl_cpool_do_locked(struct Curl_easy *data,
827 struct connectdata *conn,
828 Curl_cpool_conn_do_cb *cb, void *cbdata)
829{
830 struct cpool *cpool = cpool_get_instance(data);
831 if(cpool) {
832 CPOOL_LOCK(cpool, data);
833 cb(conn, data, cbdata);
834 CPOOL_UNLOCK(cpool, data);
835 }
836 else
837 cb(conn, data, cbdata);
838}
lib/multi.c@curl-8_21_0lines 1749–1776 · definition of Curl_multi_add_perform as in release curl-8_21_0; differs from master; commits that changed it since: 7acf124614 url: rename Curl_init_do => Curl_init_transfer | dfc01ea2a3 hardening: add API guards
Show 28 lines (not cited by the judge)
1749CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
1750 struct Curl_easy *data,
1751 struct connectdata *conn)
1752{
1753 CURLMcode mresult;
1754
1755 if(multi->in_callback)
1756 return CURLM_RECURSIVE_API_CALL;
1757
1758 mresult = curl_multi_add_handle(multi, data);
1759 if(!mresult) {
1760 CURLcode result;
1761
1762 /* pass in NULL for 'conn' here since we do not want to init the
1763 connection, only this transfer */
1764 result = Curl_init_do(data, NULL);
1765 if(result) {
1766 curl_multi_remove_handle(multi, data);
1767 return CURLM_INTERNAL_ERROR;
1768 }
1769
1770 /* take this handle to the perform state right away */
1771 multistate(data, MSTATE_PERFORMING);
1772 Curl_attach_connection(data, conn);
1773 CURL_REQ_SET_RECV(data);
1774 }
1775 return mresult;
1776}
lib/curl_setup.hlines 1077–1077 · definition of DEBUGASSERT
Show 1 lines (not cited by the judge)
1077#define DEBUGASSERT(x) CURL_DEBUGASSERT(x)
lib/multi.clines 897–911 · definition of curl_multi_remove_handle
Show 15 lines (not cited by the judge)
897CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl)
898{
899 struct Curl_mapi_guard guard;
900 CURLMcode mresult;
901
902 if(CURL_MAPI_ENTER(&guard, m, multi_remove_handle, &mresult)) {
903 struct Curl_easy *data = curl;
904 if(!GOOD_EASY_HANDLE(data))
905 mresult = CURLM_BAD_EASY_HANDLE;
906 else
907 mresult = Curl_multi_remove_handle(m, data);
908 }
909 CURL_MAPI_LEAVE(&guard);
910 return mresult;
911}
lib/multi.c@curl-8_21_0lines 784–938 · definition of curl_multi_remove_handle as in release curl-8_21_0; differs from master; commits that changed it since: dfc01ea2a3 hardening: add API guards
Show 155 lines (not cited by the judge)
784CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl)
785{
786 struct Curl_multi *multi = m;
787 struct Curl_easy *data = curl;
788 bool premature;
789 struct Curl_llist_node *e;
790 CURLMcode mresult;
791 uint32_t mid;
792
793 /* First, make some basic checks that the CURLM handle is a good handle */
794 if(!GOOD_MULTI_HANDLE(multi))
795 return CURLM_BAD_HANDLE;
796
797 /* Verify that we got a somewhat good easy handle too */
798 if(!GOOD_EASY_HANDLE(data))
799 return CURLM_BAD_EASY_HANDLE;
800
801 /* Prevent users from trying to remove same easy handle more than once */
802 if(!data->multi)
803 return CURLM_OK; /* it is already removed so let's say it is fine! */
804
805 /* Prevent users from trying to remove an easy handle from the wrong multi */
806 if(data->multi != multi)
807 return CURLM_BAD_EASY_HANDLE;
808
809 if(data->mid == UINT32_MAX) {
810 DEBUGASSERT(0);
811 return CURLM_INTERNAL_ERROR;
812 }
813 if(Curl_uint32_tbl_get(&multi->xfers, data->mid) != data) {
814 DEBUGASSERT(0);
815 return CURLM_INTERNAL_ERROR;
816 }
817
818 if(multi->in_callback)
819 return CURLM_RECURSIVE_API_CALL;
820
821 premature = (data->mstate < MSTATE_COMPLETED);
822
823 /* If the 'state' is not INIT or COMPLETED, we might need to do something
824 nice to put the easy_handle in a good known state when this returns. */
825 if(data->conn &&
826 data->mstate > MSTATE_DO &&
827 data->mstate < MSTATE_COMPLETED) {
828 /* Set connection owner so that the DONE function closes it. We can
829 safely do this here since connection is killed. */
830 streamclose(data->conn, "Removed with partial response");
831 }
832
833 if(data->conn) {
834 /* multi_done() clears the association between the easy handle and the
835 connection.
836
837 Note that this ignores the return code because there is
838 nothing really useful to do with it anyway! */
839 (void)multi_done(data, data->result, premature);
840 }
841
842 /* The timer must be shut down before data->multi is set to NULL, else the
843 timenode will remain in the splay tree after curl_easy_cleanup is
844 called. Do it after multi_done() in case that sets another time! */
845 Curl_expire_clear(data);
846
847 /* If in `msgsent`, it was deducted from `multi->xfers_alive` already. */
848 if(!Curl_uint32_bset_contains(&multi->msgsent, data->mid))
849 --multi->xfers_alive;
850 if(data->state.really_alive) {
851 data->state.really_alive = FALSE;
852 --multi->xfers_really_alive;
853 if(!multi->xfers_really_alive)
854 (void)multi_assess_wakeup(multi);
855 }
856
857 Curl_wildcard_dtor(&data->wildcard);
858
859 data->mstate = MSTATE_COMPLETED;
860
861 /* Remove the association between the connection and the handle */
862 Curl_detach_connection(data);
863
864 /* Tell event handling that this transfer is definitely going away */
865 Curl_multi_ev_xfer_done(multi, data);
866
867 if(data->set.connect_only && !data->multi_easy) {
868 /* This removes a handle that was part the multi interface that used
869 CONNECT_ONLY, that connection is now left alive but since this handle
870 has bits.close set nothing can use that transfer anymore and it is
871 forbidden from reuse. This easy handle cannot find the connection
872 anymore once removed from the multi handle
873
874 Better close the connection here, at once.
875 */
876 struct connectdata *c;
877 curl_socket_t s;
878 s = Curl_getconnectinfo(data, &c);
879 if((s != CURL_SOCKET_BAD) && c) {
880 Curl_conn_terminate(data, c, TRUE);
881 }
882 }
883
884 if(data->state.lastconnect_id != -1) {
885 /* Mark any connect-only connection for closure */
886 Curl_cpool_do_by_id(data, data->state.lastconnect_id,
887 close_connect_only, NULL);
888 }
889
890#ifdef USE_LIBPSL
891 /* Remove the PSL association. */
892 if(data->psl == &multi->psl)
893 data->psl = NULL;
894#endif
895
896 /* make sure there is no pending message in the queue sent from this easy
897 handle */
898 for(e = Curl_llist_head(&multi->msglist); e; e = Curl_node_next(e)) {
899 struct Curl_message *msg = Curl_node_elem(e);
900
901 if(msg->extmsg.easy_handle == data) {
902 Curl_node_remove(e);
903 /* there can only be one from this specific handle */
904 break;
905 }
906 }
907
908 /* clear the association to this multi handle */
909 mid = data->mid;
910 DEBUGASSERT(Curl_uint32_tbl_contains(&multi->xfers, mid));
911 Curl_uint32_tbl_remove(&multi->xfers, mid);
912 Curl_uint32_bset_remove(&multi->process, mid);
913 Curl_uint32_bset_remove(&multi->dirty, mid);
914 Curl_uint32_bset_remove(&multi->pending, mid);
915 Curl_uint32_bset_remove(&multi->msgsent, mid);
916 data->multi = NULL;
917 data->mid = UINT32_MAX;
918 data->master_mid = UINT32_MAX;
919
920 /* NOTE NOTE NOTE
921 We do not touch the easy handle here! */
922 process_pending_handles(multi);
923
924 mresult = Curl_update_timer(multi);
925 if(mresult)
926 return mresult;
927
928 mresult = multi_assess_wakeup(multi);
929 if(mresult) {
930 failf(data, "error enabling wakeup listening: %d", mresult);
931 return mresult;
932 }
933
934 CURL_TRC_M(data, "removed from multi, mid=%u, running=%u, total=%u",
935 mid, Curl_multi_xfers_running(multi),
936 Curl_uint32_tbl_count(&multi->xfers));
937 return CURLM_OK;
938}
lib/curl_share.clines 359–372 · definition of curl_share_cleanup
Show 14 lines (not cited by the judge)
359CURLSHcode curl_share_cleanup(CURLSH *sh)
360{
361 struct Curl_share *share = sh;
362 bool locked;
363 if(!GOOD_SHARE_HANDLE(share))
364 return CURLSHE_INVALID;
365
366 if(share_in_use(share))
367 return CURLSHE_IN_USE;
368
369 locked = share_lock_acquire(share, NULL);
370 share_unlink(&share, NULL, locked);
371 return CURLSHE_OK;
372}
lib/multi.clines 678–756 · definition of multi_done
Show 79 lines (not cited by the judge)
678static CURLcode multi_done(struct Curl_easy *data,
679 CURLcode status, /* an error if this is called
680 after an error was detected */
681 bool premature)
682{
683 CURLcode result;
684 struct connectdata *conn = data->conn;
685
686 CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d",
687 (int)status, (int)premature, data->state.done);
688
689 if(data->state.done)
690 /* Stop if multi_done() has already been called */
691 return CURLE_OK;
692
693 /* Shut down any ongoing async resolver operation. */
694 Curl_resolv_shutdown_all(data);
695
696 /* Cleanup possible redirect junk */
697 curlx_safefree(data->req.newurl);
698 curlx_safefree(data->req.location);
699
700 switch(status) {
701 case CURLE_ABORTED_BY_CALLBACK:
702 case CURLE_READ_ERROR:
703 case CURLE_WRITE_ERROR:
704 /* When we are aborted due to a callback return code it has to be counted
705 as premature as there is trouble ahead if we do not. We have many
706 callbacks and protocols work differently, we could potentially do this
707 more fine-grained in the future. */
708 premature = TRUE;
709 FALLTHROUGH();
710 default:
711 break;
712 }
713
714 /* this calls the protocol-specific function pointer previously set */
715 if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT))
716 result = conn->scheme->run->done(data, status, premature);
717 else
718 result = status;
719
720 if(data->mstate > MSTATE_CONNECTING &&
721 (result != CURLE_ABORTED_BY_CALLBACK)) {
722 /* avoid this if
723 * - the transfer has not connected
724 * - we already aborted by callback to avoid this calling another callback
725 */
726 int rc = Curl_pgrsDone(data);
727 if(!result && rc)
728 result = CURLE_ABORTED_BY_CALLBACK;
729 }
730
731 /* Make sure that transfer client writes are really done now. */
732 result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature));
733
734 /* Inform connection filters that this transfer is done */
735 if(conn)
736 Curl_conn_ev_data_done(data, premature);
737
738 multi_schedule_pending(data->multi); /* connection / multiplex */
739
740 if(!result)
741 result = Curl_req_done(&data->req, data, premature);
742
743 if(conn) {
744 /* Under the potential connection pool's share lock, decide what to
745 * do with the transfer's connection. */
746 struct multi_done_ctx mdctx;
747
748 memset(&mdctx, 0, sizeof(mdctx));
749 mdctx.premature = premature;
750 Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
751 }
752
753 /* flush the netrc cache */
754 Curl_netrc_cleanup(&data->state.netrc);
755 return result;
756}
lib/multi.c@curl-8_21_0lines 694–772 · definition of multi_done as in release curl-8_21_0; differs from master
Show 79 lines (not cited by the judge)
694static CURLcode multi_done(struct Curl_easy *data,
695 CURLcode status, /* an error if this is called
696 after an error was detected */
697 bool premature)
698{
699 CURLcode result;
700 struct connectdata *conn = data->conn;
701
702 CURL_TRC_M(data, "multi_done: status: %d prem: %d done: %d",
703 (int)status, (int)premature, data->state.done);
704
705 if(data->state.done)
706 /* Stop if multi_done() has already been called */
707 return CURLE_OK;
708
709 /* Shut down any ongoing async resolver operation. */
710 Curl_resolv_shutdown_all(data);
711
712 /* Cleanup possible redirect junk */
713 curlx_safefree(data->req.newurl);
714 curlx_safefree(data->req.location);
715
716 switch(status) {
717 case CURLE_ABORTED_BY_CALLBACK:
718 case CURLE_READ_ERROR:
719 case CURLE_WRITE_ERROR:
720 /* When we are aborted due to a callback return code it has to be counted
721 as premature as there is trouble ahead if we do not. We have many
722 callbacks and protocols work differently, we could potentially do this
723 more fine-grained in the future. */
724 premature = TRUE;
725 FALLTHROUGH();
726 default:
727 break;
728 }
729
730 /* this calls the protocol-specific function pointer previously set */
731 if(conn && conn->scheme->run->done && (data->mstate >= MSTATE_PROTOCONNECT))
732 result = conn->scheme->run->done(data, status, premature);
733 else
734 result = status;
735
736 if(data->mstate > MSTATE_CONNECTING &&
737 (result != CURLE_ABORTED_BY_CALLBACK)) {
738 /* avoid this if
739 * - the transfer has not connected
740 * - we already aborted by callback to avoid this calling another callback
741 */
742 int rc = Curl_pgrsDone(data);
743 if(!result && rc)
744 result = CURLE_ABORTED_BY_CALLBACK;
745 }
746
747 /* Make sure that transfer client writes are really done now. */
748 result = Curl_1st_fatal(result, Curl_xfer_write_done(data, premature));
749
750 /* Inform connection filters that this transfer is done */
751 if(conn)
752 Curl_conn_ev_data_done(data, premature);
753
754 process_pending_handles(data->multi); /* connection / multiplex */
755
756 if(!result)
757 result = Curl_req_done(&data->req, data, premature);
758
759 if(conn) {
760 /* Under the potential connection pool's share lock, decide what to
761 * do with the transfer's connection. */
762 struct multi_done_ctx mdctx;
763
764 memset(&mdctx, 0, sizeof(mdctx));
765 mdctx.premature = premature;
766 Curl_cpool_do_locked(data, data->conn, multi_done_locked, &mdctx);
767 }
768
769 /* flush the netrc cache */
770 Curl_netrc_cleanup(&data->state.netrc);
771 return result;
772}
lib/http2.c@curl-8_21_0lines 777–887 · definition of push_promise as in release curl-8_21_0; differs from master; commits that changed it since: dfc01ea2a3 hardening: add API guards
Show 111 lines (not cited by the judge)
777static int push_promise(struct Curl_cfilter *cf,
778 struct Curl_easy *data,
779 const nghttp2_push_promise *frame)
780{
781 struct cf_h2_ctx *ctx = cf->ctx;
782 int rv; /* one of the CURL_PUSH_* defines */
783
784 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE received",
785 frame->promised_stream_id);
786 if(data->multi->push_cb) {
787 struct h2_stream_ctx *stream;
788 struct h2_stream_ctx *newstream;
789 struct curl_pushheaders heads;
790 CURLMcode mresult;
791 CURLcode result;
792 /* clone the parent */
793 struct Curl_easy *newhandle = h2_duphandle(cf, data);
794 if(!newhandle) {
795 infof(data, "failed to duplicate handle");
796 rv = CURL_PUSH_DENY; /* FAIL HARD */
797 goto fail;
798 }
799
800 stream = H2_STREAM_CTX(ctx, data);
801 if(!stream) {
802 failf(data, "Internal NULL stream");
803 discard_newhandle(cf, newhandle);
804 rv = CURL_PUSH_DENY;
805 goto fail;
806 }
807
808 heads.data = data;
809 heads.stream = stream;
810 heads.frame = frame;
811
812 rv = set_transfer_url(newhandle, &heads, data);
813 if(rv) {
814 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, failed to set URL -> %d",
815 frame->promised_stream_id, rv);
816 discard_newhandle(cf, newhandle);
817 rv = CURL_PUSH_DENY;
818 goto fail;
819 }
820
821 Curl_set_in_callback(data, TRUE);
822 rv = data->multi->push_cb(data, newhandle,
823 stream->push_headers_used, &heads,
824 data->multi->push_userp);
825 Curl_set_in_callback(data, FALSE);
826
827 /* free the headers again */
828 free_push_headers(stream);
829
830 if(rv) {
831 DEBUGASSERT((rv > CURL_PUSH_OK) && (rv <= CURL_PUSH_ERROROUT));
832 /* denied, kill off the new handle again */
833 CURL_TRC_CF(data, cf, "[%d] PUSH_PROMISE, denied by application -> %d",
834 frame->promised_stream_id, rv);
835 discard_newhandle(cf, newhandle);
836 goto fail;
837 }
838
839 /* approved, add to the multi handle for processing. This
840 * assigns newhandle->mid. For the new `mid` we assign the
841 * h2_stream instance and remember the stream_id already known. */
842 mresult = Curl_multi_add_perform(data->multi, newhandle, cf->conn);
843 if(mresult) {
844 infof(data, "failed to add handle to multi");
845 discard_newhandle(cf, newhandle);
846 rv = CURL_PUSH_DENY;
847 goto fail;
848 }
849
850 result = http2_data_setup(cf, newhandle, &newstream);
851 if(result) {
852 failf(data, "error setting up stream: %d", (int)result);
853 discard_newhandle(cf, newhandle);
854 rv = CURL_PUSH_DENY;
855 goto fail;
856 }
857
858 DEBUGASSERT(newstream);
859 newstream->id = frame->promised_stream_id;
860 newhandle->req.maxdownload = -1;
861 newhandle->req.size = -1;
862
863 CURL_TRC_CF(data, cf, "promise easy handle added to multi, mid=%u",
864 newhandle->mid);
865 rv = nghttp2_session_set_stream_user_data(ctx->h2,
866 newstream->id,
867 newhandle);
868 if(rv) {
869 infof(data, "failed to set user_data for stream %d",
870 newstream->id);
871 DEBUGASSERT(0);
872 discard_newhandle(cf, newhandle);
873 rv = CURL_PUSH_DENY;
874 goto fail;
875 }
876
877 /* success, remember max stream id processed */
878 if(newstream->id > ctx->local_max_sid)
879 ctx->local_max_sid = newstream->id;
880 }
881 else {
882 CURL_TRC_CF(data, cf, "Got PUSH_PROMISE, ignore it");
883 rv = CURL_PUSH_DENY;
884 }
885fail:
886 return rv;
887}
lib/curl_share.c@curl-8_21_0lines 35–66 · definition of share_destroy as in release curl-8_21_0; differs from master; commits that changed it since: 112a8b5adf src: safely clear certain buffers
Show 32 lines (not cited by the judge)
35static void share_destroy(struct Curl_share *share)
36{
37 if(share->specifier & (1 << CURL_LOCK_DATA_CONNECT)) {
38 Curl_cpool_destroy(&share->cpool);
39 }
40
41 Curl_dnscache_destroy(&share->dnscache);
42
43#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
44 Curl_cookie_cleanup(share->cookies);
45#endif
46
47#ifndef CURL_DISABLE_HSTS
48 Curl_hsts_cleanup(&share->hsts);
49#endif
50
51#ifdef USE_SSL
52 if(share->ssl_scache) {
53 Curl_ssl_scache_destroy(share->ssl_scache);
54 share->ssl_scache = NULL;
55 }
56#endif
57
58 Curl_psl_destroy(&share->psl);
59 Curl_close(&share->admin);
60
61#ifdef USE_MUTEX
62 Curl_mutex_destroy(&share->lock);
63#endif
64 share->magic = 0;
65 curlx_free(share);
66}