CVE-2026-92388: FTP wildcard write callback mixup
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
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.
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
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.
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.
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.
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.
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)
Source code
| 3948 | static 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 | |
| 4031 | fail: |
| 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 | |
| 4042 | static 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); |
| 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 | |
| 1015 | size_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 | |
| 1082 | fail: |
| 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 | } |
| 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 | |
| 925 | static 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 | } |
| 188 | void 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 | } |
| 1004 | CURL *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 | |
| 1123 | fail: |
| 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 | } |
Show 135 lines (not cited by the judge)
| 974 | CURL *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 | |
| 1094 | fail: |
| 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 | } |
Show 37 lines (not cited by the judge)
| 1143 | void 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 | } |
Show 64 lines (not cited by the judge)
| 895 | static 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 | } |
Show 9 lines (not cited by the judge)
| 273 | int 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 | } |
Show 7 lines (not cited by the judge)
| 3940 | static 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 | } |