✓ProveIt
Examples / Report #4022070 Saved analysis

CVE-2026-92388: FTP wildcard write callback mixup

curl · 2026-09-1118.5s analysis · $0.033 in model calls

What the report claims

A failed FTP wildcard LIST leaves data->set.fwrite_func pointing at libcurl's internal listing parser and data->set.out pointing at the easy handle, and this poisoned state can be duplicated or reused to cause a heap use-after-free read or a controlled write primitive on subsequent transfers.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-92388 FTP wildcard write callback mixup

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

ProveIt's finding

Code supports the claim

The code supports the reported flaw. A maintainer should review its impact and decide what to do next.

Why this finding?
  • The code confirms that failed FTP wildcard LIST leaves data->set.fwrite_func=Curl_ftp_parselist and data->set.out=data, with restoration only in CURLWC_MATCHING (successful parse).
  • Teardown paths (fail label in init_wc_data and Curl_wildcard_dtor) free wildcard/parser but never restore data->set, leaving the handle poisoned.
  • curl_easy_duphandle copies the poisoned set struct verbatim via dupset (dst->set = src->set), so the clone inherits fwrite_func=Curl_ftp_parselist and out pointing to the source handle. Freeing the source creates a use-after-free.
  • Reusing the handle with only CURLOPT_WRITEDATA changed (common documented pattern) makes Curl_ftp_parselist treat the application's WRITEDATA object as a Curl_easy*, following attacker-controlled pointers to achieve a controlled-write primitive.
  • The PoC demonstrates both escalation paths on curl-8_22_0 and current master. This is not merely a NULL dereference; the craft-write mode shows a write primitive reachable from a remote malicious FTP server (550 denial) followed by a benign HTTP transfer.
  • The project's 'NULL dereferences and crashes' policy exclusion does not apply because the impact exceeds a crash — it yields a controlled-write primitive.
How this analysis ran 5 claims · 20 citations verified

Checked against curl master on 2026-09-11 (73d15fcb1a7d). 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 · 43 references checked out, 1 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 6275 tokens · $0.0005
3 · Evidence
10 code snippets
real curl source at 73d15fcb1a7d
4 · Judgment
Nemotron-3-Ultra-550b-a55b
24728 tokens · 15.5s · $0.0325
5 · Receipts
20 of 20 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the detailed report and reproducible PoC. The code confirms the flaw: failed FTP wildcard LIST leaves fwrite_func/out mutated, restoration only happens on success, and teardown never restores them. duphandle copies the poisoned state, and WRITEDATA-only reuse enables a type-confusion write primitive. This is a valid security issue (controlled write, not just a crash). We'll prioritize a fix that stops mutating data->set for the parser, or makes restoration idempotent on all teardown paths with guards in Curl_ftp_parselist.

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

Report claims

C1flaw supported

A failed FTP wildcard LIST leaves data->set.fwrite_func pointing at libcurl's internal listing parser and data->set.out pointing at the easy handle, because the code path that restores them is only reached on a successful listing.

Read the reasoning

The code at lib/ftp.c:4019-4026 saves the original write function and output pointer, then replaces them with Curl_ftp_parselist and the easy handle itself. The restoration at lib/ftp.c:4059-4066 is explicitly gated behind 'LIST response successfully parsed' (CURLWC_MATCHING state). A failed listing never reaches this state.

C2location supported

The restoration of fwrite_func and out occurs only in the CURLWC_MATCHING state (lib/ftp.c:4059-4066).

Read the reasoning

The restoration of fwrite_func and out occurs only in the CURLWC_MATCHING case block, and the comment confirms this state is only reached after successful LIST parsing.

C3trigger supported

Anything that ends the listing before that point (e.g., a 550 denial, dropped data connection, or application timeout) skips the restoration.

Read the reasoning

Both the fail path in init_wc_data (lib/ftp.c:4031-4039) and Curl_wildcard_dtor (lib/ftplistparser.c:188-207) free the wildcard/parser structures but never restore data->set.fwrite_func or data->set.out. Any error before CURLWC_MATCHING (550 denial, timeout, connection drop) skips restoration.

C4impact supported

The handle ends up with fwrite_func == Curl_ftp_parselist and out == data, leading to a NULL dereference on reuse or a type confusion that parses response bytes into a pointer taken from the application's own memory.

Read the reasoning

After a failed listing, the handle has fwrite_func=Curl_ftp_parselist and out=data. On reuse, Curl_ftp_parselist is called with connptr=out (which is either the freed original handle or the application's WRITEDATA object). It unconditionally casts connptr to Curl_easy* and dereferences data->wildcard->ftpwc->parser. Since data->wildcard was nulled by Curl_wildcard_dtor, this is a NULL dereference. If out points to application memory (WRITEDATA-only reuse), the parser follows attacker-controlled pointers, enabling a type confusion write primitive.

C5reproduction supported

A failed wildcard listing followed by handle duplication (curl_easy_duphandle) or reuse with only WRITEDATA changed reproduces the bug.

Read the reasoning

curl_easy_duphandle calls dupset which copies the entire set struct (including fwrite_func and out) by value. The clone inherits fwrite_func=Curl_ftp_parselist and out pointing to the source handle. Freeing the source makes the clone's out a dangling pointer. The PoC demonstrates both duphandle (use-after-free) and craft-write (controlled write) modes reproducing the bug, while control (resetting both options) survives.

Deterministic checks that failed

  • quote 1 of 2 quoted lines appear nowhere in curl, e.g. 'struct ftp_wc *ftpwc = data->wildcard->ftpwc; /* data->wildc'
Read the original report (excerpt)

Full report on HackerOne ↗

Hi all, A failed FTP wildcard `LIST` leaves `data->set.fwrite_func` pointing at libcurl's internal listing parser and `data->set.out` pointing at the easy handle itself, because the code path that restores them is only reached on a *successful* listing. That alone is a crash on reuse. However, `curl_easy_duphandle()` copies this poisoned state verbatim into a clone, so freeing the original handle (an explicitly documented, independent thing to do) turns the clone's next transfer into a heap use-after-free read. A second, adjacent pattern (reusing a handle by only ever changing `CURLOPT_WRITEDATA`, never `CURLOPT_WRITEFUNCTION`) turns it into a type confusion that parses response bytes into a pointer taken from the application's own memory. ## The bug Wildcard setup saves the application's write callback and output pointer, then substitutes its own: ```c // lib/ftp.c:4019-4026 /* backup old write_function */ ftpwc->backup.write_function = data->set.fwrite_func; data->set.fwrite_func = Curl_ftp_parselist; /* backup old file descriptor */ ftpwc->backup.file_descriptor = data->set.out; data->set.out = data; ``` The only place that puts them back is the state the machine reaches once the `LIST` response has already parsed successfully: ```c // lib/ftp.c:4059-4066 case CURLWC_MATCHING: { /* In this state is LIST response successfully parsed, so lets restore previous WRITEFUNCTION callback and WRITEDATA pointer */ struct ftp_wc *ftpwc = wildcard->ftpwc; data->set.fwrite_func = ftpwc->backup.write_function; data->set.out = ftpwc->backup.file_descriptor; ``` Anything that ends the listing before that point (a `550` denial, a dropped data connection, the application's own `CURLOPT_TIMEOUT` firing on a server that just stays silent) skips it. Every teardown path instead runs `Curl_wildcard_dtor()`, called on every transfer completion from `Curl_multi_done()`, and it never sees the `Curl_easy` it would need to restore into: ```c // lib/ftplistparser.c:188-198 void Curl_wildcard_dtor(struct WildcardData **wcp) { struct WildcardData *wc = *wcp; if(!wc) return; if(wc->dtor) { wc->dtor(wc->ftpwc); /* frees ftpwc + its parser */ wc->dtor = ZERO_NULL; wc->ftpwc = NULL; } ... curlx_free(wc); *wcp = NULL; /* data->wildcard is now NULL */ ``` The handle now has `fwrite_func == Curl_ftp_parselist` and `out == data`, wi …

Source code

lib/ftp.clines 3948–4091 · lines 4019-4026 cited by the report; lines 4059-4066 cited by the report; definition of init_wc_data
3948static CURLcode init_wc_data(struct Curl_easy *data,
3949 struct ftp_conn *ftpc,
3950 struct FTP *ftp)
3951{
3952 char *last_slash;
3953 char *path = ftp->path;
3954 struct WildcardData *wildcard = data->wildcard;
3955 CURLcode result = CURLE_OK;
3956 struct ftp_wc *ftpwc = NULL;
3957
3958 last_slash = strrchr(ftp->path, '/');
3959 if(last_slash) {
3960 last_slash++;
3961 if(last_slash[0] == '\0') {
3962 wildcard->state = CURLWC_CLEAN;
3963 return ftp_parse_url_path(data, ftpc, ftp);
3964 }
3965 wildcard->pattern = curlx_strdup(last_slash);
3966 if(!wildcard->pattern)
3967 return CURLE_OUT_OF_MEMORY;
3968 last_slash[0] = '\0'; /* cut file from path */
3969 }
3970 else { /* there is only 'wildcard pattern' or nothing */
3971 if(path[0]) {
3972 wildcard->pattern = curlx_strdup(path);
3973 if(!wildcard->pattern)
3974 return CURLE_OUT_OF_MEMORY;
3975 path[0] = '\0';
3976 }
3977 else { /* only list */
3978 wildcard->state = CURLWC_CLEAN;
3979 return ftp_parse_url_path(data, ftpc, ftp);
3980 }
3981 }
3982
3983 /* program continues only if URL is not ending with slash, allocate needed
3984 resources for wildcard transfer */
3985
3986 /* allocate ftp protocol specific wildcard data */
3987 ftpwc = curlx_calloc(1, sizeof(struct ftp_wc));
3988 if(!ftpwc) {
3989 result = CURLE_OUT_OF_MEMORY;
3990 goto fail;
3991 }
3992
3993 /* INITIALIZE parselist structure */
3994 ftpwc->parser = Curl_ftp_parselist_data_alloc();
3995 if(!ftpwc->parser) {
3996 result = CURLE_OUT_OF_MEMORY;
3997 goto fail;
3998 }
3999
4000 wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */
4001 wildcard->dtor = wc_data_dtor;
4002
4003 /* wildcard does not support NOCWD option (assert it?) */
4004 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
4005 data->set.ftp_filemethod = FTPFILE_MULTICWD;
4006
4007 /* try to parse ftp URL */
4008 result = ftp_parse_url_path(data, ftpc, ftp);
4009 if(result) {
4010 goto fail;
4011 }
4012
4013 wildcard->path = curlx_strdup(ftp->path);
4014 if(!wildcard->path) {
4015 result = CURLE_OUT_OF_MEMORY;
4016 goto fail;
4017 }
4018
4019 /* backup old write_function */
C1: Backup and overwrite of fwrite_func and out with internal parser and easy handle
4020 ftpwc->backup.write_function = data->set.fwrite_func;
C1: data->set.fwrite_func = Curl_ftp_parselist
4021 /* parsing write function */
4022 data->set.fwrite_func = Curl_ftp_parselist;
C4: data->set.fwrite_func = Curl_ftp_parselist remains after failure
4023 /* backup old file descriptor */
4024 ftpwc->backup.file_descriptor = data->set.out;
C1: data->set.out = data
4025 /* let the writefunc callback know the transfer */
C4: data->set.out = data remains after failure
4026 data->set.out = data;
4027
4028 infof(data, "Wildcard - Parsing started");
4029 return CURLE_OK;
4030
4031fail:
C3: fail: label - error path does not restore fwrite_func/out
4032 if(ftpwc) {
C3: Frees ftpwc and parser but leaves data->set mutated
4033 Curl_ftp_parselist_data_free(&ftpwc->parser);
4034 curlx_free(ftpwc);
4035 }
4036 curlx_safefree(wildcard->pattern);
4037 wildcard->dtor = ZERO_NULL;
4038 wildcard->ftpwc = NULL;
4039 return result;
4040}
4041
4042static CURLcode wc_statemach(struct Curl_easy *data,
4043 struct ftp_conn *ftpc,
4044 struct FTP *ftp)
4045{
4046 struct WildcardData * const wildcard = data->wildcard;
4047 CURLcode result = CURLE_OK;
4048
4049 for(;;) {
4050 switch(wildcard->state) {
4051 case CURLWC_INIT:
4052 result = init_wc_data(data, ftpc, ftp);
4053 if(wildcard->state == CURLWC_CLEAN)
4054 /* only listing! */
4055 return result;
4056 wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
4057 return result;
4058
4059 case CURLWC_MATCHING: {
C1: Restoration only in CURLWC_MATCHING state, which requires successful LIST parsing
C2: case CURLWC_MATCHING: { /* In this state is LIST response successfully parsed */
4060 /* In this state is LIST response successfully parsed, so lets restore
4061 previous WRITEFUNCTION callback and WRITEDATA pointer */
4062 struct ftp_wc *ftpwc = wildcard->ftpwc;
C2: data->set.fwrite_func = ftpwc->backup.write_function
4063 data->set.fwrite_func = ftpwc->backup.write_function;
C2: data->set.out = ftpwc->backup.file_descriptor
4064 data->set.out = ftpwc->backup.file_descriptor;
4065 ftpwc->backup.write_function = ZERO_NULL;
4066 ftpwc->backup.file_descriptor = NULL;
4067 wildcard->state = CURLWC_DOWNLOADING;
4068
4069 if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
4070 /* error found in LIST parsing */
4071 wildcard->state = CURLWC_CLEAN;
4072 continue;
4073 }
4074 if(Curl_llist_count(&wildcard->filelist) == 0) {
4075 /* no corresponding file */
4076 wildcard->state = CURLWC_CLEAN;
4077 return CURLE_REMOTE_FILE_NOT_FOUND;
4078 }
4079 continue;
4080 }
4081
4082 case CURLWC_DOWNLOADING: {
4083 /* filelist has at least one file, lets get first one */
4084 struct Curl_llist_node *head = Curl_llist_head(&wildcard->filelist);
4085 struct curl_fileinfo *finfo = Curl_node_elem(head);
4086 char *tmp_path;
4087 char *enc = curl_easy_escape(NULL, finfo->filename, 0);
4088 if(!enc)
4089 return CURLE_OUT_OF_MEMORY;
4090
4091 tmp_path = curl_maprintf("%s%s", wildcard->path, enc);
lib/ftplistparser.clines 990–1091 · lines 1015-1017 cited by the report; definition of Curl_ftp_parselist
990 parser->state.NT.main = PL_WINNT_DATE;
991 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
992 }
993 break;
994 case PL_WINNT_FILENAME_WINEOL:
995 if(c == '\n') {
996 parser->offsets.filename = parser->item_offset;
997 result = ftp_pl_insert_finfo(data, infop);
998 if(result)
999 return result;
1000
1001 parser->state.NT.main = PL_WINNT_DATE;
1002 parser->state.NT.sub.filename = PL_WINNT_FILENAME_PRESPACE;
1003 }
1004 else
1005 return CURLE_FTP_BAD_FILE_LIST;
1006
1007 break;
1008 }
1009 break;
1010 }
1011
1012 return CURLE_OK;
1013}
1014
1015size_t Curl_ftp_parselist(char *buffer, size_t size, size_t nmemb,
1016 void *connptr)
1017{
1018 size_t bufflen = size * nmemb;
1019 struct Curl_easy *data = (struct Curl_easy *)connptr;
C4: struct Curl_easy *data = (struct Curl_easy *)connptr
1020 struct ftp_wc *ftpwc = data->wildcard->ftpwc;
C4: struct ftp_wc *ftpwc = data->wildcard->ftpwc; // data->wildcard is NULL after dtor
1021 struct ftp_parselist_data *parser = ftpwc->parser;
C4: struct ftp_parselist_data *parser = ftpwc->parser
1022 size_t i = 0;
1023 CURLcode result;
1024 size_t retsize = bufflen;
1025
1026 if(parser->error) { /* error in previous call */
1027 /* scenario:
1028 * 1. call => OK..
1029 * 2. call => OUT_OF_MEMORY (or other error)
1030 * 3. (last) call => is skipped RIGHT HERE and the error is handled later
1031 * in wc_statemach()
1032 */
1033 goto fail;
1034 }
1035
1036 if(parser->os_type == OS_TYPE_UNKNOWN && bufflen > 0) {
1037 /* considering info about FILE response format */
1038 parser->os_type = ISDIGIT(buffer[0]) ? OS_TYPE_WIN_NT : OS_TYPE_UNIX;
1039 }
1040
1041 while(i < bufflen) { /* FSM */
1042 char c = buffer[i];
1043 struct fileinfo *infop;
1044 if(!parser->file_data) { /* tmp file data is not allocated yet */
1045 parser->file_data = Curl_fileinfo_alloc();
1046 if(!parser->file_data) {
1047 parser->error = CURLE_OUT_OF_MEMORY;
1048 goto fail;
1049 }
1050 parser->item_offset = 0;
1051 parser->item_length = 0;
1052 curlx_dyn_init(&parser->file_data->buf, MAX_FTPLIST_BUFFER);
1053 }
1054
1055 infop = parser->file_data;
1056
1057 if(curlx_dyn_addn(&infop->buf, &c, 1)) {
1058 parser->error = CURLE_OUT_OF_MEMORY;
1059 goto fail;
1060 }
1061
1062 switch(parser->os_type) {
1063 case OS_TYPE_UNIX:
1064 result = parse_unix(data, parser, infop, c);
1065 break;
1066 case OS_TYPE_WIN_NT:
1067 result = parse_winnt(data, parser, infop, c);
1068 break;
1069 default:
1070 retsize = bufflen + 1;
1071 goto fail;
1072 }
1073 if(result) {
1074 parser->error = result;
1075 goto fail;
1076 }
1077
1078 i++;
1079 }
1080 return retsize;
1081
1082fail:
1083
1084 /* Clean up any allocated memory. */
1085 if(parser->file_data) {
1086 Curl_fileinfo_cleanup(parser->file_data);
1087 parser->file_data = NULL;
1088 }
1089
1090 return retsize;
1091}
lib/easy.clines 907–988 · lines 932-932 cited by the report; definition of dupset
907 CURLcode result;
908
909 if(CURL_EAPI_ENTER(&guard, curl, easy_getinfo, &result)) {
910 struct Curl_easy *data = curl;
911 va_list arg;
912 void *paramp;
913
914 va_start(arg, info);
915 paramp = va_arg(arg, void *);
916
917 result = Curl_getinfo(data, info, paramp);
918
919 va_end(arg);
920 }
921 CURL_EAPI_LEAVE(&guard);
922 return result;
923}
924
925static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
926{
927 CURLcode result = CURLE_OK;
928 enum dupblob j;
929
930 /* Copy src->set into dst->set first, then deal with the strings
931 afterwards */
932 dst->set = src->set;
C5: dst->set = src->set copies fwrite_func and out verbatim
933#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
934 dst->set.mimepostp = NULL;
935#endif
936 dst->set.str_copypostfields = NULL;
937
938 Curl_u8_strset_init(&dst->set.strings);
939 /* clear all dest string and blob pointers first, in case we error out
940 mid-function */
941 memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
942
943 /* duplicate all strings */
944 result = Curl_u8_strset_copy(&dst->set.strings, &src->set.strings);
945 if(result)
946 return result;
947
948 /* duplicate all blobs */
949 for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
950 result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]);
951 if(result)
952 return result;
953 }
954
955 /* duplicate memory areas pointed to */
956 if(src->set.str_copypostfields) {
957 if(src->set.postfieldsize == -1)
958 dst->set.str_copypostfields = curlx_strdup(src->set.str_copypostfields);
959 else
960 /* postfieldsize is curl_off_t, curlx_memdup0() takes a size_t ... */
961 dst->set.str_copypostfields =
962 curlx_memdup0(src->set.str_copypostfields,
963 curlx_sotouz(src->set.postfieldsize));
964 if(!dst->set.str_copypostfields)
965 return CURLE_OUT_OF_MEMORY;
966 /* point to the new copy */
967 dst->set.postfields = dst->set.str_copypostfields;
968 }
969
970#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
971 if(src->set.mimepostp) {
972 /* Duplicate mime data. Get a mimepost struct for the clone as well */
973 dst->set.mimepostp = curlx_malloc(sizeof(*dst->set.mimepostp));
974 if(!dst->set.mimepostp)
975 return CURLE_OUT_OF_MEMORY;
976
977 Curl_mime_initpart(dst->set.mimepostp);
978 result = Curl_mime_duppart(dst, dst->set.mimepostp, src->set.mimepostp);
979 if(result)
980 return result;
981 }
982#endif
983
984 if(src->set.resolve)
985 dst->state.resolve = dst->set.resolve;
986
987 return result;
988}
lib/ftplistparser.clines 188–207 · definition of Curl_wildcard_dtor
188void Curl_wildcard_dtor(struct WildcardData **wcp)
C3: Curl_wildcard_dtor frees wildcard state without touching data->set
189{
190 struct WildcardData *wc = *wcp;
191 if(!wc)
192 return;
193
194 if(wc->dtor) {
195 wc->dtor(wc->ftpwc);
C3: wc->dtor(wc->ftpwc) frees ftpwc/parser
196 wc->dtor = ZERO_NULL;
197 wc->ftpwc = NULL;
198 }
199 DEBUGASSERT(!wc->ftpwc);
200
201 Curl_llist_destroy(&wc->filelist, NULL);
202 curlx_safefree(wc->path);
203 curlx_safefree(wc->pattern);
204 wc->state = CURLWC_INIT;
205 curlx_free(wc);
206 *wcp = NULL;
C3: *wcp = NULL sets data->wildcard to NULL
207}
lib/easy.clines 1004–1137 · definition of curl_easy_duphandle
1004CURL *curl_easy_duphandle(CURL *curl)
1005{
1006 struct Curl_eapi_guard guard;
1007 struct Curl_easy *outcurl = NULL;
1008
1009 if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) {
1010 struct Curl_easy *data = curl;
1011 const char *str;
1012
1013 outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
C5: outcurl = curlx_calloc(1, sizeof(struct Curl_easy))
1014 if(!outcurl)
1015 goto fail;
1016
1017 /*
1018 * We setup a few buffers we need. We should probably make them
1019 * get setup on-demand in the code, as that would probably decrease
1020 * the likeliness of us forgetting to init a buffer here in the future.
1021 */
1022 outcurl->set.buffer_size = data->set.buffer_size;
1023
1024 Curl_hash_init(&outcurl->meta_hash, 23, CURL_HASH_TYPE_BYTES,
1025 dupeasy_meta_freeentry);
1026 curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
1027 Curl_bufref_init(&outcurl->state.url);
1028 Curl_bufref_init(&outcurl->state.referer);
1029 Curl_netrc_init(&outcurl->state.netrc);
1030
1031 /* the connection pool is setup on demand */
1032 outcurl->state.lastconnect_id = -1;
1033 outcurl->id = -1;
1034 outcurl->mid = UINT32_MAX;
1035 outcurl->master_mid = UINT32_MAX;
1036
1037#ifndef CURL_DISABLE_HTTP
1038 Curl_llist_init(&outcurl->state.httphdrs, NULL);
1039#endif
1040 Curl_initinfo(outcurl);
1041
1042 /* copy all userdefined values */
1043 if(dupset(outcurl, data))
C5: dupset(outcurl, data) called in curl_easy_duphandle
1044 goto fail;
1045
1046 outcurl->progress.hide = data->progress.hide;
1047 outcurl->progress.callback = data->progress.callback;
1048
1049#ifndef CURL_DISABLE_COOKIES
1050 outcurl->state.cookielist = NULL;
1051 if(data->cookies && data->state.cookie_engine) {
1052 /* If cookies are enabled in the parent handle, we enable them
1053 in the clone as well! */
1054 outcurl->cookies = Curl_cookie_init();
1055 if(!outcurl->cookies)
1056 goto fail;
1057 outcurl->state.cookie_engine = TRUE;
1058 }
1059
1060 if(data->state.cookielist) {
1061 outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
1062 if(!outcurl->state.cookielist)
1063 goto fail;
1064 }
1065#endif
1066
1067 if(Curl_bufref_ptr(&data->state.url)) {
1068 Curl_bufref_set(&outcurl->state.url,
1069 Curl_bufref_dup(&data->state.url), 0,
1070 curl_free);
1071 if(!Curl_bufref_ptr(&outcurl->state.url))
1072 goto fail;
1073 }
1074 if(Curl_bufref_ptr(&data->state.referer)) {
1075 Curl_bufref_set(&outcurl->state.referer,
1076 Curl_bufref_dup(&data->state.referer), 0,
1077 curl_free);
1078 if(!Curl_bufref_ptr(&outcurl->state.referer))
1079 goto fail;
1080 }
1081
1082 /* Reinitialize an SSL engine for the new handle
1083 * note: the engine name has already been copied by dupset */
1084 str = CURL_EASY_STR(outcurl, STRING_SSL_ENGINE);
1085 if(str) {
1086 if(Curl_ssl_set_engine(outcurl, str))
1087 goto fail;
1088 }
1089
1090#ifndef CURL_DISABLE_ALTSVC
1091 if(data->asi) {
1092 outcurl->asi = Curl_altsvc_init();
1093 if(!outcurl->asi)
1094 goto fail;
1095 str = CURL_EASY_STR(outcurl, STRING_ALTSVC);
1096 if(str)
1097 (void)Curl_altsvc_load(outcurl->asi, str);
1098 }
1099#endif
1100#ifndef CURL_DISABLE_HSTS
1101 if(data->hsts) {
1102 outcurl->hsts = Curl_hsts_init();
1103 if(!outcurl->hsts)
1104 goto fail;
1105 str = CURL_EASY_STR(outcurl, STRING_HSTS);
1106 if(str)
1107 (void)Curl_hsts_loadfile(outcurl, outcurl->hsts, str);
1108 (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
1109
1110 /* Copy entries learned at runtime. (E.g. Strict-Transport-Security
1111 headers.) */
1112 if(Curl_hsts_copy(outcurl->hsts, data->hsts))
1113 goto fail;
1114 }
1115#endif
1116
1117 /* we reach this point and thus we are OK */
1118 outcurl->magic = CURLEASY_MAGIC_NUMBER;
1119 }
1120 CURL_EAPI_LEAVE(&guard);
1121 return outcurl;
1122
1123fail:
1124 if(outcurl) {
1125#ifndef CURL_DISABLE_COOKIES
1126 curlx_free(outcurl->cookies);
1127#endif
1128 curlx_dyn_free(&outcurl->state.headerb);
1129 Curl_altsvc_cleanup(&outcurl->asi);
1130 Curl_hsts_cleanup(&outcurl->hsts);
1131 Curl_freeset(outcurl);
1132 curlx_free(outcurl);
1133 }
1134
1135 CURL_EAPI_LEAVE(&guard);
1136 return NULL;
1137}
lib/easy.c@curl-8_22_0lines 974–1108 · definition of curl_easy_duphandle as in release curl-8_22_0; differs from master; commits that changed it since: 7c0c7ca57b hash: select key operations by type
Show 135 lines (not cited by the judge)
974CURL *curl_easy_duphandle(CURL *curl)
975{
976 struct Curl_eapi_guard guard;
977 struct Curl_easy *outcurl = NULL;
978
979 if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) {
980 struct Curl_easy *data = curl;
981 const char *str;
982
983 outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
984 if(!outcurl)
985 goto fail;
986
987 /*
988 * We setup a few buffers we need. We should probably make them
989 * get setup on-demand in the code, as that would probably decrease
990 * the likeliness of us forgetting to init a buffer here in the future.
991 */
992 outcurl->set.buffer_size = data->set.buffer_size;
993
994 Curl_hash_init(&outcurl->meta_hash, 23,
995 Curl_hash_str, curlx_str_key_compare,
996 dupeasy_meta_freeentry);
997 curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
998 Curl_bufref_init(&outcurl->state.url);
999 Curl_bufref_init(&outcurl->state.referer);
1000 Curl_netrc_init(&outcurl->state.netrc);
1001
1002 /* the connection pool is setup on demand */
1003 outcurl->state.lastconnect_id = -1;
1004 outcurl->id = -1;
1005 outcurl->mid = UINT32_MAX;
1006 outcurl->master_mid = UINT32_MAX;
1007
1008#ifndef CURL_DISABLE_HTTP
1009 Curl_llist_init(&outcurl->state.httphdrs, NULL);
1010#endif
1011 Curl_initinfo(outcurl);
1012
1013 /* copy all userdefined values */
1014 if(dupset(outcurl, data))
1015 goto fail;
1016
1017 outcurl->progress.hide = data->progress.hide;
1018 outcurl->progress.callback = data->progress.callback;
1019
1020#ifndef CURL_DISABLE_COOKIES
1021 outcurl->state.cookielist = NULL;
1022 if(data->cookies && data->state.cookie_engine) {
1023 /* If cookies are enabled in the parent handle, we enable them
1024 in the clone as well! */
1025 outcurl->cookies = Curl_cookie_init();
1026 if(!outcurl->cookies)
1027 goto fail;
1028 outcurl->state.cookie_engine = TRUE;
1029 }
1030
1031 if(data->state.cookielist) {
1032 outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
1033 if(!outcurl->state.cookielist)
1034 goto fail;
1035 }
1036#endif
1037
1038 if(Curl_bufref_ptr(&data->state.url)) {
1039 Curl_bufref_set(&outcurl->state.url,
1040 Curl_bufref_dup(&data->state.url), 0,
1041 curl_free);
1042 if(!Curl_bufref_ptr(&outcurl->state.url))
1043 goto fail;
1044 }
1045 if(Curl_bufref_ptr(&data->state.referer)) {
1046 Curl_bufref_set(&outcurl->state.referer,
1047 Curl_bufref_dup(&data->state.referer), 0,
1048 curl_free);
1049 if(!Curl_bufref_ptr(&outcurl->state.referer))
1050 goto fail;
1051 }
1052
1053 /* Reinitialize an SSL engine for the new handle
1054 * note: the engine name has already been copied by dupset */
1055 str = CURL_EASY_STR(outcurl, STRING_SSL_ENGINE);
1056 if(str) {
1057 if(Curl_ssl_set_engine(outcurl, str))
1058 goto fail;
1059 }
1060
1061#ifndef CURL_DISABLE_ALTSVC
1062 if(data->asi) {
1063 outcurl->asi = Curl_altsvc_init();
1064 if(!outcurl->asi)
1065 goto fail;
1066 str = CURL_EASY_STR(outcurl, STRING_ALTSVC);
1067 if(str)
1068 (void)Curl_altsvc_load(outcurl->asi, str);
1069 }
1070#endif
1071#ifndef CURL_DISABLE_HSTS
1072 if(data->hsts) {
1073 outcurl->hsts = Curl_hsts_init();
1074 if(!outcurl->hsts)
1075 goto fail;
1076 str = CURL_EASY_STR(outcurl, STRING_HSTS);
1077 if(str)
1078 (void)Curl_hsts_loadfile(outcurl, outcurl->hsts, str);
1079 (void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
1080
1081 /* Copy entries learned at runtime. (E.g. Strict-Transport-Security
1082 headers.) */
1083 if(Curl_hsts_copy(outcurl->hsts, data->hsts))
1084 goto fail;
1085 }
1086#endif
1087
1088 /* we reach this point and thus we are OK */
1089 outcurl->magic = CURLEASY_MAGIC_NUMBER;
1090 }
1091 CURL_EAPI_LEAVE(&guard);
1092 return outcurl;
1093
1094fail:
1095 if(outcurl) {
1096#ifndef CURL_DISABLE_COOKIES
1097 curlx_free(outcurl->cookies);
1098#endif
1099 curlx_dyn_free(&outcurl->state.headerb);
1100 Curl_altsvc_cleanup(&outcurl->asi);
1101 Curl_hsts_cleanup(&outcurl->hsts);
1102 Curl_freeset(outcurl);
1103 curlx_free(outcurl);
1104 }
1105
1106 CURL_EAPI_LEAVE(&guard);
1107 return NULL;
1108}
lib/easy.clines 1143–1179 · definition of curl_easy_reset
Show 37 lines (not cited by the judge)
1143void curl_easy_reset(CURL *curl)
1144{
1145 struct Curl_eapi_guard guard;
1146
1147 if(CURL_EAPI_ENTER(&guard, curl, easy_reset, NULL)) {
1148 struct Curl_easy *data = curl;
1149
1150 data->state.lastconnect_id = -1; /* clear remembered connection id */
1151 Curl_req_hard_reset(&data->req, data);
1152 Curl_hash_clean(&data->meta_hash);
1153
1154 /* clear all meta data */
1155 Curl_meta_reset(data);
1156 /* zero out UserDefined data: */
1157 Curl_freeset(data);
1158 memset(&data->set, 0, sizeof(struct UserDefined));
1159 Curl_init_userdefined(data);
1160
1161 /* zero out Progress data: */
1162 memset(&data->progress, 0, sizeof(struct Progress));
1163
1164 /* zero out PureInfo data: */
1165 Curl_initinfo(data);
1166
1167 data->progress.hide = TRUE;
1168
1169 /* zero out authentication data: */
1170 memset(&data->state.authhost, 0, sizeof(struct auth));
1171 memset(&data->state.authproxy, 0, sizeof(struct auth));
1172
1173#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
1174 Curl_http_auth_cleanup_digest(data);
1175#endif
1176 data->master_mid = UINT32_MAX;
1177 }
1178 CURL_EAPI_LEAVE(&guard);
1179}
lib/easy.c@curl-8_22_0lines 895–958 · definition of dupset as in release curl-8_22_0; differs from master; commits that changed it since: 7d7fa4d7dd easy: duplicating a zero byte memory area needs no malloc
Show 64 lines (not cited by the judge)
895static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
896{
897 CURLcode result = CURLE_OK;
898 enum dupblob j;
899
900 /* Copy src->set into dst->set first, then deal with the strings
901 afterwards */
902 dst->set = src->set;
903#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
904 dst->set.mimepostp = NULL;
905#endif
906 dst->set.str_copypostfields = NULL;
907
908 Curl_u8_strset_init(&dst->set.strings);
909 /* clear all dest string and blob pointers first, in case we error out
910 mid-function */
911 memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *));
912
913 /* duplicate all strings */
914 result = Curl_u8_strset_copy(&dst->set.strings, &src->set.strings);
915 if(result)
916 return result;
917
918 /* duplicate all blobs */
919 for(j = (enum dupblob)0; j < BLOB_LAST; j++) {
920 result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]);
921 if(result)
922 return result;
923 }
924
925 /* duplicate memory areas pointed to */
926 if(src->set.str_copypostfields) {
927 if(src->set.postfieldsize == -1)
928 dst->set.str_copypostfields = curlx_strdup(src->set.str_copypostfields);
929 else
930 /* postfieldsize is curl_off_t, curlx_memdup() takes a size_t ... */
931 dst->set.str_copypostfields =
932 curlx_memdup(src->set.str_copypostfields,
933 curlx_sotouz(src->set.postfieldsize));
934 if(!dst->set.str_copypostfields)
935 return CURLE_OUT_OF_MEMORY;
936 /* point to the new copy */
937 dst->set.postfields = dst->set.str_copypostfields;
938 }
939
940#if !defined(CURL_DISABLE_MIME) || !defined(CURL_DISABLE_FORM_API)
941 if(src->set.mimepostp) {
942 /* Duplicate mime data. Get a mimepost struct for the clone as well */
943 dst->set.mimepostp = curlx_malloc(sizeof(*dst->set.mimepostp));
944 if(!dst->set.mimepostp)
945 return CURLE_OUT_OF_MEMORY;
946
947 Curl_mime_initpart(dst->set.mimepostp);
948 result = Curl_mime_duppart(dst, dst->set.mimepostp, src->set.mimepostp);
949 if(result)
950 return result;
951 }
952#endif
953
954 if(src->set.resolve)
955 dst->state.resolve = dst->set.resolve;
956
957 return result;
958}
src/curlinfo.clines 273–281 · definition of main
Show 9 lines (not cited by the judge)
273int main(void)
274{
275 size_t i;
276
277 for(i = 0; i < CURL_ARRAYSIZE(disabled); i++)
278 puts(disabled[i]);
279
280 return 0;
281}
lib/ftp.clines 3940–3946 · definition of wc_data_dtor
Show 7 lines (not cited by the judge)
3940static void wc_data_dtor(void *ptr)
3941{
3942 struct ftp_wc *ftpwc = ptr;
3943 if(ftpwc && ftpwc->parser)
3944 Curl_ftp_parselist_data_free(&ftpwc->parser);
3945 curlx_free(ftpwc);
3946}