✓ProveIt
Examples / Report #1045844 Saved analysis

CVE-2020-8285: FTP wildcard stack overflow

curl · 2020-11-2710.0s analysis · $0.022 in model calls

What the report claims

FTP wildcard stack overflow

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2020-8285 FTP wildcard stack overflow

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?
  • wc_statemach uses recursion instead of iteration for state transitions, causing stack depth linear in the number of files processed
  • An attacker controlling the FTP server can create a directory with tens of thousands of files, each triggering a recursive call
  • The stack trace confirms the recursion between CURLWC_DOWNLOADING (line 3856) and CURLWC_SKIP (line 3894) cases
  • This is a denial-of-service vulnerability reachable via server-controlled input
How this analysis ran 5 claims · 9 citations verified

Checked against curl master on 2020-11-27 (c7b02c5d6800). 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 · 20 references checked out, 2 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2543 tokens · $0.0002
3 · Evidence
8 code snippets
real curl source at c7b02c5d6800
4 · Judgment
Nemotron-3-Ultra-550b-a55b
16761 tokens · 8.6s · $0.0214
5 · Receipts
9 of 9 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

The report is valid. The wc_statemach function in lib/ftp.c uses unbounded recursion (lines 3820, 3856, 3864, 3894) for state transitions during wildcard FTP directory processing. An attacker controlling the FTP server can create a directory with many files (40,000 as shown), causing stack overflow. The fix is to convert the recursion to a while loop. This affects all versions including the latest release.

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

Report claims

C1flaw supported

FTP wildcard stack overflow

Read the reasoning

The wc_statemach function uses unbounded recursion for state transitions. Each file processed in the wildcard matching can cause a recursive call, leading to stack depth proportional to the number of files. With 40,000 files as demonstrated, this overflows the stack.

C2location supported

wc_statemach

Read the reasoning

The flaw is in the wc_statemach function in ftp.c, confirmed by the stack trace and source code.

C3trigger supported

adding/crafting files on the server

Read the reasoning

The file list is populated from the FTP server's directory listing (LIST command response). An attacker controlling the server can create arbitrarily many files, each triggering a recursive call in wc_statemach.

C4impact supported

stack overflow

Read the reasoning

The stack trace shows alternating calls at lines 3856 and 3894 (DOWNLOADING->SKIP->DOWNLOADING...), confirming unbounded recursion depth. The crash in curl_dbg_log via dynbuf allocation is a consequence of stack exhaustion.

C5reproduction not verifiable

build 6255.c and run it with a debugger

Read the reasoning

Reproduction steps reference an external test file (6255.c) not provided. The code analysis alone confirms the recursive pattern that would cause stack overflow with many files.

Deterministic checks that failed

  • line none of curl_fnmatch, fnmatch, wc_statemach, curl_dbg_log appear near line 846 at c7b02c5d6800; it reads: 'OUTCHAR(*str++);'
  • line none of curl_fnmatch, fnmatch, wc_statemach, curl_dbg_log appear near line 3836 at c7b02c5d6800; it reads: 'char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);'
Read the original report (excerpt)

Full report on HackerOne ↗

## Summary: User 'xnynx' on github filed [PR 6255](https://github.com/curl/curl/issues/6255) highlighting this problem. **Filed publicly** My first gut reaction was that this had to be a problem with `curl_fnmatch` as that has caused us grief in the past (and on most platforms we use the native `fnmatch()` now, but not on Windows IIRC and this is a reported to happen on Windows), but I then built a test program and I made it crash in what seems like potential stack overflow due to recursive calls to `wc_statemach` from within itself. ## Steps To Reproduce: 1. build 6255.c (attached) 1. run it (with a debugger) 1. inspect the crash The example app lists a directory with 40,000 files on funet.fi. ## Crash stack trace ~~~ Program received signal SIGSEGV, Segmentation fault. 0x0000555555582039 in curl_dbg_log ( format=<error reading variable: Cannot access memory at address 0x7fffff7fefb8>) at memdebug.c:437 437 { (gdb) bt #0 0x0000555555582039 in curl_dbg_log ( format=<error reading variable: Cannot access memory at address 0x7fffff7fefb8>) at memdebug.c:437 #1 0x0000555555581b57 in curl_dbg_realloc (ptr=0x0, wantedsize=32, line=98, source=0x55555563d738 "dynbuf.c") at memdebug.c:247 #2 0x00005555555c01d3 in dyn_nappend (s=0x7fffff800820, mem=0x7fffff7ff187 "rP\b\200\377\377\177", len=1) at dynbuf.c:98 #3 0x00005555555c0409 in Curl_dyn_addn (s=0x7fffff800820, mem=0x7fffff7ff187, len=1) at dynbuf.c:164 #4 0x0000555555587a20 in alloc_addbyter (output=114, data=0x7fffff800850) at mprintf.c:1045 #5 0x000055555558710f in dprintf_formatf (data=0x7fffff800850, stream=0x5555555879e8 <alloc_addbyter>, format=0x555555642498 "%s%s", ap_save=0x7fffff800890) at mprintf.c:846 #6 0x0000555555587aea in curl_mvaprintf (format=0x555555642498 "%s%s", ap_save=0x7fffff800890) at mprintf.c:1080 #7 0x0000555555587be6 in curl_maprintf (format=0x555555642498 "%s%s") at mprintf.c:1095 #8 0x00005555555eae21 in wc_statemach (conn=0x5555556b9068) at ftp.c:3836 #9 0x00005555555eb0a3 in wc_statemach (conn=0x5555556b9068) at ftp.c:3894 #10 0x00005555555eaf44 in wc_statemach (conn=0x5555556b9068) at ftp.c:3856 #11 0x00005555555eb0a3 in wc_statemach (conn=0x5555556b9068) at ftp.c:3894 #12 0x00005555555eaf44 in wc_statemach (conn=0x5555556b9068) at ftp.c:3856 #13 0x00005555555eb0a3 in wc_statemach (conn=0x5555556b906 …

Source code

lib/ftp.clines 3793–3919 · lines 3894-3894 cited by the report; lines 3856-3856 cited by the report; definition of wc_statemach
3793static CURLcode wc_statemach(struct connectdata *conn)
C2: Definition of wc_statemach function
3794{
3795 struct WildcardData * const wildcard = &(conn->data->wildcard);
3796 CURLcode result = CURLE_OK;
3797
3798 switch(wildcard->state) {
3799 case CURLWC_INIT:
3800 result = init_wc_data(conn);
3801 if(wildcard->state == CURLWC_CLEAN)
3802 /* only listing! */
3803 break;
3804 wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
3805 break;
3806
3807 case CURLWC_MATCHING: {
3808 /* In this state is LIST response successfully parsed, so lets restore
3809 previous WRITEFUNCTION callback and WRITEDATA pointer */
3810 struct ftp_wc *ftpwc = wildcard->protdata;
3811 conn->data->set.fwrite_func = ftpwc->backup.write_function;
3812 conn->data->set.out = ftpwc->backup.file_descriptor;
3813 ftpwc->backup.write_function = ZERO_NULL;
3814 ftpwc->backup.file_descriptor = NULL;
3815 wildcard->state = CURLWC_DOWNLOADING;
3816
3817 if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
3818 /* error found in LIST parsing */
3819 wildcard->state = CURLWC_CLEAN;
3820 return wc_statemach(conn);
C1: CURLWC_MATCHING case recursively calls wc_statemach
3821 }
3822 if(wildcard->filelist.size == 0) {
3823 /* no corresponding file */
3824 wildcard->state = CURLWC_CLEAN;
3825 return CURLE_REMOTE_FILE_NOT_FOUND;
3826 }
3827 return wc_statemach(conn);
3828 }
3829
3830 case CURLWC_DOWNLOADING: {
3831 /* filelist has at least one file, lets get first one */
3832 struct ftp_conn *ftpc = &conn->proto.ftpc;
3833 struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
C3: finfo comes from wildcard->filelist.head->ptr, populated from server directory listing
3834 struct FTP *ftp = conn->data->req.p.ftp;
3835
3836 char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
3837 if(!tmp_path)
3838 return CURLE_OUT_OF_MEMORY;
3839
3840 /* switch default ftp->path and tmp_path */
3841 free(ftp->pathalloc);
3842 ftp->pathalloc = ftp->path = tmp_path;
3843
3844 infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3845 if(conn->data->set.chunk_bgn) {
3846 long userresponse;
3847 Curl_set_in_callback(conn->data, true);
3848 userresponse = conn->data->set.chunk_bgn(
3849 finfo, wildcard->customptr, (int)wildcard->filelist.size);
3850 Curl_set_in_callback(conn->data, false);
3851 switch(userresponse) {
3852 case CURL_CHUNK_BGN_FUNC_SKIP:
3853 infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3854 finfo->filename);
3855 wildcard->state = CURLWC_SKIP;
3856 return wc_statemach(conn);
C1: CURLWC_DOWNLOADING case (SKIP) recursively calls wc_statemach
C4: Recursive call from DOWNLOADING to SKIP
3857 case CURL_CHUNK_BGN_FUNC_FAIL:
3858 return CURLE_CHUNK_FAILED;
3859 }
3860 }
3861
3862 if(finfo->filetype != CURLFILETYPE_FILE) {
3863 wildcard->state = CURLWC_SKIP;
3864 return wc_statemach(conn);
C1: CURLWC_DOWNLOADING case (non-file) recursively calls wc_statemach
3865 }
3866
3867 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3868 ftpc->known_filesize = finfo->size;
3869
3870 result = ftp_parse_url_path(conn);
3871 if(result)
3872 return result;
3873
3874 /* we don't need the Curl_fileinfo of first file anymore */
3875 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
3876
3877 if(wildcard->filelist.size == 0) { /* remains only one file to down. */
3878 wildcard->state = CURLWC_CLEAN;
3879 /* after that will be ftp_do called once again and no transfer
3880 will be done because of CURLWC_CLEAN state */
3881 return CURLE_OK;
3882 }
3883 } break;
3884
3885 case CURLWC_SKIP: {
3886 if(conn->data->set.chunk_end) {
3887 Curl_set_in_callback(conn->data, true);
3888 conn->data->set.chunk_end(conn->data->wildcard.customptr);
3889 Curl_set_in_callback(conn->data, false);
3890 }
3891 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
C3: Curl_llist_remove processes each file in the server-provided list
3892 wildcard->state = (wildcard->filelist.size == 0) ?
3893 CURLWC_CLEAN : CURLWC_DOWNLOADING;
3894 return wc_statemach(conn);
C1: CURLWC_SKIP case recursively calls wc_statemach
C4: Recursive call from SKIP back to DOWNLOADING (if list not empty)
3895 }
3896
3897 case CURLWC_CLEAN: {
3898 struct ftp_wc *ftpwc = wildcard->protdata;
3899 result = CURLE_OK;
3900 if(ftpwc)
3901 result = Curl_ftp_parselist_geterror(ftpwc->parser);
3902
3903 wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
3904 } break;
3905
3906 case CURLWC_DONE:
3907 case CURLWC_ERROR:
3908 case CURLWC_CLEAR:
3909 if(wildcard->dtor)
3910 wildcard->dtor(wildcard->protdata);
3911 break;
3912 }
3913
3914 return result;
3915}
3916
3917/***********************************************************************
3918 *
3919 * ftp_do()
lib/memdebug.clines 412–462 · lines 437-437 cited by the report
412 if(source)
413 curl_dbg_log("FILE %s:%d fdopen(\"%d\",\"%s\") = %p\n",
414 source, line, filedes, mode, (void *)res);
415 return res;
416}
417
418int curl_dbg_fclose(FILE *file, int line, const char *source)
419{
420 int res;
421
422 DEBUGASSERT(file != NULL);
423
424 if(source)
425 curl_dbg_log("FILE %s:%d fclose(%p)\n",
426 source, line, (void *)file);
427
428 res = fclose(file);
429
430 return res;
431}
432
433#define LOGLINE_BUFSIZE 1024
434
435/* this does the writing to the memory tracking log file */
436void curl_dbg_log(const char *format, ...)
437{
438 char *buf;
439 int nchars;
440 va_list ap;
441
442 if(!curl_dbg_logfile)
443 return;
444
445 buf = (Curl_cmalloc)(LOGLINE_BUFSIZE);
446 if(!buf)
447 return;
448
449 va_start(ap, format);
450 nchars = mvsnprintf(buf, LOGLINE_BUFSIZE, format, ap);
451 va_end(ap);
452
453 if(nchars > LOGLINE_BUFSIZE - 1)
454 nchars = LOGLINE_BUFSIZE - 1;
455
456 if(nchars > 0)
457 fwrite(buf, 1, (size_t)nchars, curl_dbg_logfile);
458
459 (Curl_cfree)(buf);
460}
461
462#endif /* CURLDEBUG */
lib/memdebug.clines 220–272 · lines 247-247 cited by the report; definition of curl_dbg_realloc
Show 53 lines (not cited by the judge)
220void *curl_dbg_realloc(void *ptr, size_t wantedsize,
221 int line, const char *source)
222{
223 struct memdebug *mem = NULL;
224
225 size_t size = sizeof(struct memdebug) + wantedsize;
226
227 DEBUGASSERT(wantedsize != 0);
228
229 if(countcheck("realloc", line, source))
230 return NULL;
231
232#ifdef __INTEL_COMPILER
233# pragma warning(push)
234# pragma warning(disable:1684)
235 /* 1684: conversion from pointer to same-sized integral type */
236#endif
237
238 if(ptr)
239 mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
240
241#ifdef __INTEL_COMPILER
242# pragma warning(pop)
243#endif
244
245 mem = (Curl_crealloc)(mem, size);
246 if(source)
247 curl_dbg_log("MEM %s:%d realloc(%p, %zu) = %p\n",
248 source, line, (void *)ptr, wantedsize,
249 mem ? (void *)mem->mem : (void *)0);
250
251 if(mem) {
252 mem->size = wantedsize;
253 return mem->mem;
254 }
255
256 return NULL;
257}
258
259void curl_dbg_free(void *ptr, int line, const char *source)
260{
261 if(ptr) {
262 struct memdebug *mem;
263
264#ifdef __INTEL_COMPILER
265# pragma warning(push)
266# pragma warning(disable:1684)
267 /* 1684: conversion from pointer to same-sized integral type */
268#endif
269
270 mem = (void *)((char *)ptr - offsetof(struct memdebug, mem));
271
272#ifdef __INTEL_COMPILER
lib/dynbuf.clines 65–123 · lines 98-98 cited by the report; definition of dyn_nappend
Show 59 lines (not cited by the judge)
65static CURLcode dyn_nappend(struct dynbuf *s,
66 const unsigned char *mem, size_t len)
67{
68 size_t indx = s->leng;
69 size_t a = s->allc;
70 size_t fit = len + indx + 1; /* new string + old string + zero byte */
71
72 /* try to detect if there's rubbish in the struct */
73 DEBUGASSERT(s->init == DYNINIT);
74 DEBUGASSERT(s->toobig);
75 DEBUGASSERT(indx < s->toobig);
76 DEBUGASSERT(!s->leng || s->bufr);
77
78 if(fit > s->toobig) {
79 Curl_dyn_free(s);
80 return CURLE_OUT_OF_MEMORY;
81 }
82 else if(!a) {
83 DEBUGASSERT(!indx);
84 /* first invoke */
85 if(fit < MIN_FIRST_ALLOC)
86 a = MIN_FIRST_ALLOC;
87 else
88 a = fit;
89 }
90 else {
91 while(a < fit)
92 a *= 2;
93 }
94
95 if(a != s->allc) {
96 /* this logic is not using Curl_saferealloc() to make the tool not have to
97 include that as well when it uses this code */
98 void *p = realloc(s->bufr, a);
99 if(!p) {
100 Curl_safefree(s->bufr);
101 s->leng = s->allc = 0;
102 return CURLE_OUT_OF_MEMORY;
103 }
104 s->bufr = p;
105 s->allc = a;
106 }
107
108 if(len)
109 memcpy(&s->bufr[indx], mem, len);
110 s->leng = indx + len;
111 s->bufr[s->leng] = 0;
112 return CURLE_OK;
113}
114
115/*
116 * Clears the string, keeps the allocation. This can also be called on a
117 * buffer that already was freed.
118 */
119void Curl_dyn_reset(struct dynbuf *s)
120{
121 DEBUGASSERT(s);
122 DEBUGASSERT(s->init == DYNINIT);
123 DEBUGASSERT(!s->leng || s->bufr);
lib/dynbuf.clines 139–189 · lines 164-164 cited by the report
Show 51 lines (not cited by the judge)
139 if(trail > s->leng)
140 return CURLE_BAD_FUNCTION_ARGUMENT;
141 else if(trail == s->leng)
142 return CURLE_OK;
143 else if(!trail) {
144 Curl_dyn_reset(s);
145 }
146 else {
147 memmove(&s->bufr[0], &s->bufr[s->leng - trail], trail);
148 s->leng = trail;
149 s->bufr[s->leng] = 0;
150 }
151 return CURLE_OK;
152
153}
154#endif
155
156/*
157 * Appends a buffer with length.
158 */
159CURLcode Curl_dyn_addn(struct dynbuf *s, const void *mem, size_t len)
160{
161 DEBUGASSERT(s);
162 DEBUGASSERT(s->init == DYNINIT);
163 DEBUGASSERT(!s->leng || s->bufr);
164 return dyn_nappend(s, mem, len);
165}
166
167/*
168 * Append a null-terminated string at the end.
169 */
170CURLcode Curl_dyn_add(struct dynbuf *s, const char *str)
171{
172 size_t n = strlen(str);
173 DEBUGASSERT(s);
174 DEBUGASSERT(s->init == DYNINIT);
175 DEBUGASSERT(!s->leng || s->bufr);
176 return dyn_nappend(s, (unsigned char *)str, n);
177}
178
179/*
180 * Append a string vprintf()-style
181 */
182CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap)
183{
184#ifdef BUILDING_LIBCURL
185 int rc;
186 DEBUGASSERT(s);
187 DEBUGASSERT(s->init == DYNINIT);
188 DEBUGASSERT(!s->leng || s->bufr);
189 rc = Curl_dyn_vprintf(s, fmt, ap);
lib/mprintf.clines 1020–1120 · lines 1045-1045 cited by the report; lines 1080-1080 cited by the report; lines 1095-1095 cited by the report
Show 101 lines (not cited by the judge)
1020 if(info.max == info.length)
1021 /* we're at maximum, scrap the last letter */
1022 info.buffer[-1] = 0;
1023 else
1024 info.buffer[0] = 0;
1025 }
1026 return retcode;
1027}
1028
1029int curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
1030{
1031 int retcode;
1032 va_list ap_save; /* argument pointer */
1033 va_start(ap_save, format);
1034 retcode = curl_mvsnprintf(buffer, maxlength, format, ap_save);
1035 va_end(ap_save);
1036 return retcode;
1037}
1038
1039/* fputc() look-alike */
1040static int alloc_addbyter(int output, FILE *data)
1041{
1042 struct asprintf *infop = (struct asprintf *)data;
1043 unsigned char outc = (unsigned char)output;
1044
1045 if(Curl_dyn_addn(infop->b, &outc, 1)) {
1046 infop->fail = 1;
1047 return -1; /* fail */
1048 }
1049 return outc; /* fputc() returns like this on success */
1050}
1051
1052extern int Curl_dyn_vprintf(struct dynbuf *dyn,
1053 const char *format, va_list ap_save);
1054
1055/* appends the formatted string, returns 0 on success, 1 on error */
1056int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save)
1057{
1058 int retcode;
1059 struct asprintf info;
1060 info.b = dyn;
1061 info.fail = 0;
1062
1063 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1064 if((-1 == retcode) || info.fail) {
1065 Curl_dyn_free(info.b);
1066 return 1;
1067 }
1068 return 0;
1069}
1070
1071char *curl_mvaprintf(const char *format, va_list ap_save)
1072{
1073 int retcode;
1074 struct asprintf info;
1075 struct dynbuf dyn;
1076 info.b = &dyn;
1077 Curl_dyn_init(info.b, DYN_APRINTF);
1078 info.fail = 0;
1079
1080 retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save);
1081 if((-1 == retcode) || info.fail) {
1082 Curl_dyn_free(info.b);
1083 return NULL;
1084 }
1085 if(Curl_dyn_len(info.b))
1086 return Curl_dyn_ptr(info.b);
1087 return strdup("");
1088}
1089
1090char *curl_maprintf(const char *format, ...)
1091{
1092 va_list ap_save;
1093 char *s;
1094 va_start(ap_save, format);
1095 s = curl_mvaprintf(format, ap_save);
1096 va_end(ap_save);
1097 return s;
1098}
1099
1100static int storebuffer(int output, FILE *data)
1101{
1102 char **buffer = (char **)data;
1103 unsigned char outc = (unsigned char)output;
1104 **buffer = outc;
1105 (*buffer)++;
1106 return outc; /* act like fputc() ! */
1107}
1108
1109int curl_msprintf(char *buffer, const char *format, ...)
1110{
1111 va_list ap_save; /* argument pointer */
1112 int retcode;
1113 va_start(ap_save, format);
1114 retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
1115 va_end(ap_save);
1116 *buffer = 0; /* we terminate this with a zero byte */
1117 return retcode;
1118}
1119
1120int curl_mprintf(const char *format, ...)
lib/mprintf.clines 563–723 · definition of dprintf_formatf (truncated)
Show 161 lines (not cited by the judge)
563static int dprintf_formatf(
564 void *data, /* untouched by format(), just sent to the stream() function in
565 the second argument */
566 /* function pointer called for each output character */
567 int (*stream)(int, FILE *),
568 const char *format, /* %-formatted string */
569 va_list ap_save) /* list of parameters */
570{
571 /* Base-36 digits for numbers. */
572 const char *digits = lower_digits;
573
574 /* Pointer into the format string. */
575 char *f;
576
577 /* Number of characters written. */
578 int done = 0;
579
580 long param; /* current parameter to read */
581 long param_num = 0; /* parameter counter */
582
583 struct va_stack vto[MAX_PARAMETERS];
584 char *endpos[MAX_PARAMETERS];
585 char **end;
586 char work[BUFFSIZE];
587 struct va_stack *p;
588
589 /* 'workend' points to the final buffer byte position, but with an extra
590 byte as margin to avoid the (false?) warning Coverity gives us
591 otherwise */
592 char *workend = &work[sizeof(work) - 2];
593
594 /* Do the actual %-code parsing */
595 if(dprintf_Pass1(format, vto, endpos, ap_save))
596 return -1;
597
598 end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
599 created for us */
600
601 f = (char *)format;
602 while(*f != '\0') {
603 /* Format spec modifiers. */
604 int is_alt;
605
606 /* Width of a field. */
607 long width;
608
609 /* Precision of a field. */
610 long prec;
611
612 /* Decimal integer is negative. */
613 int is_neg;
614
615 /* Base of a number to be written. */
616 unsigned long base;
617
618 /* Integral values to be written. */
619 mp_uintmax_t num;
620
621 /* Used to convert negative in positive. */
622 mp_intmax_t signed_num;
623
624 char *w;
625
626 if(*f != '%') {
627 /* This isn't a format spec, so write everything out until the next one
628 OR end of string is reached. */
629 do {
630 OUTCHAR(*f);
631 } while(*++f && ('%' != *f));
632 continue;
633 }
634
635 ++f;
636
637 /* Check for "%%". Note that although the ANSI standard lists
638 '%' as a conversion specifier, it says "The complete format
639 specification shall be `%%'," so we can avoid all the width
640 and precision processing. */
641 if(*f == '%') {
642 ++f;
643 OUTCHAR('%');
644 continue;
645 }
646
647 /* If this is a positional parameter, the position must follow immediately
648 after the %, thus create a %<num>$ sequence */
649 param = dprintf_DollarString(f, &f);
650
651 if(!param)
652 param = param_num;
653 else
654 --param;
655
656 param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
657 third %s will pick the 3rd argument */
658
659 p = &vto[param];
660
661 /* pick up the specified width */
662 if(p->flags & FLAGS_WIDTHPARAM) {
663 width = (long)vto[p->width].data.num.as_signed;
664 param_num++; /* since the width is extracted from a parameter, we
665 must skip that to get to the next one properly */
666 if(width < 0) {
667 /* "A negative field width is taken as a '-' flag followed by a
668 positive field width." */
669 width = -width;
670 p->flags |= FLAGS_LEFT;
671 p->flags &= ~FLAGS_PAD_NIL;
672 }
673 }
674 else
675 width = p->width;
676
677 /* pick up the specified precision */
678 if(p->flags & FLAGS_PRECPARAM) {
679 prec = (long)vto[p->precision].data.num.as_signed;
680 param_num++; /* since the precision is extracted from a parameter, we
681 must skip that to get to the next one properly */
682 if(prec < 0)
683 /* "A negative precision is taken as if the precision were
684 omitted." */
685 prec = -1;
686 }
687 else if(p->flags & FLAGS_PREC)
688 prec = p->precision;
689 else
690 prec = -1;
691
692 is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
693
694 switch(p->type) {
695 case FORMAT_INT:
696 num = p->data.num.as_unsigned;
697 if(p->flags & FLAGS_CHAR) {
698 /* Character. */
699 if(!(p->flags & FLAGS_LEFT))
700 while(--width > 0)
701 OUTCHAR(' ');
702 OUTCHAR((char) num);
703 if(p->flags & FLAGS_LEFT)
704 while(--width > 0)
705 OUTCHAR(' ');
706 break;
707 }
708 if(p->flags & FLAGS_OCTAL) {
709 /* Octal unsigned integer. */
710 base = 8;
711 goto unsigned_number;
712 }
713 else if(p->flags & FLAGS_HEX) {
714 /* Hexadecimal unsigned integer. */
715
716 digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
717 base = 16;
718 goto unsigned_number;
719 }
720 else if(p->flags & FLAGS_UNSIGNED) {
721 /* Decimal unsigned integer. */
722 base = 10;
723 goto unsigned_number;
lib/ftp.c@curl-7_73_0lines 3794–3916 · definition of wc_statemach as in release curl-7_73_0; differs from master
Show 123 lines (not cited by the judge)
3794static CURLcode wc_statemach(struct connectdata *conn)
3795{
3796 struct WildcardData * const wildcard = &(conn->data->wildcard);
3797 CURLcode result = CURLE_OK;
3798
3799 switch(wildcard->state) {
3800 case CURLWC_INIT:
3801 result = init_wc_data(conn);
3802 if(wildcard->state == CURLWC_CLEAN)
3803 /* only listing! */
3804 break;
3805 wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
3806 break;
3807
3808 case CURLWC_MATCHING: {
3809 /* In this state is LIST response successfully parsed, so lets restore
3810 previous WRITEFUNCTION callback and WRITEDATA pointer */
3811 struct ftp_wc *ftpwc = wildcard->protdata;
3812 conn->data->set.fwrite_func = ftpwc->backup.write_function;
3813 conn->data->set.out = ftpwc->backup.file_descriptor;
3814 ftpwc->backup.write_function = ZERO_NULL;
3815 ftpwc->backup.file_descriptor = NULL;
3816 wildcard->state = CURLWC_DOWNLOADING;
3817
3818 if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
3819 /* error found in LIST parsing */
3820 wildcard->state = CURLWC_CLEAN;
3821 return wc_statemach(conn);
3822 }
3823 if(wildcard->filelist.size == 0) {
3824 /* no corresponding file */
3825 wildcard->state = CURLWC_CLEAN;
3826 return CURLE_REMOTE_FILE_NOT_FOUND;
3827 }
3828 return wc_statemach(conn);
3829 }
3830
3831 case CURLWC_DOWNLOADING: {
3832 /* filelist has at least one file, lets get first one */
3833 struct ftp_conn *ftpc = &conn->proto.ftpc;
3834 struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
3835 struct FTP *ftp = conn->data->req.protop;
3836
3837 char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
3838 if(!tmp_path)
3839 return CURLE_OUT_OF_MEMORY;
3840
3841 /* switch default ftp->path and tmp_path */
3842 free(ftp->pathalloc);
3843 ftp->pathalloc = ftp->path = tmp_path;
3844
3845 infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3846 if(conn->data->set.chunk_bgn) {
3847 long userresponse;
3848 Curl_set_in_callback(conn->data, true);
3849 userresponse = conn->data->set.chunk_bgn(
3850 finfo, wildcard->customptr, (int)wildcard->filelist.size);
3851 Curl_set_in_callback(conn->data, false);
3852 switch(userresponse) {
3853 case CURL_CHUNK_BGN_FUNC_SKIP:
3854 infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3855 finfo->filename);
3856 wildcard->state = CURLWC_SKIP;
3857 return wc_statemach(conn);
3858 case CURL_CHUNK_BGN_FUNC_FAIL:
3859 return CURLE_CHUNK_FAILED;
3860 }
3861 }
3862
3863 if(finfo->filetype != CURLFILETYPE_FILE) {
3864 wildcard->state = CURLWC_SKIP;
3865 return wc_statemach(conn);
3866 }
3867
3868 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3869 ftpc->known_filesize = finfo->size;
3870
3871 result = ftp_parse_url_path(conn);
3872 if(result)
3873 return result;
3874
3875 /* we don't need the Curl_fileinfo of first file anymore */
3876 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
3877
3878 if(wildcard->filelist.size == 0) { /* remains only one file to down. */
3879 wildcard->state = CURLWC_CLEAN;
3880 /* after that will be ftp_do called once again and no transfer
3881 will be done because of CURLWC_CLEAN state */
3882 return CURLE_OK;
3883 }
3884 } break;
3885
3886 case CURLWC_SKIP: {
3887 if(conn->data->set.chunk_end) {
3888 Curl_set_in_callback(conn->data, true);
3889 conn->data->set.chunk_end(conn->data->wildcard.customptr);
3890 Curl_set_in_callback(conn->data, false);
3891 }
3892 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
3893 wildcard->state = (wildcard->filelist.size == 0) ?
3894 CURLWC_CLEAN : CURLWC_DOWNLOADING;
3895 return wc_statemach(conn);
3896 }
3897
3898 case CURLWC_CLEAN: {
3899 struct ftp_wc *ftpwc = wildcard->protdata;
3900 result = CURLE_OK;
3901 if(ftpwc)
3902 result = Curl_ftp_parselist_geterror(ftpwc->parser);
3903
3904 wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
3905 } break;
3906
3907 case CURLWC_DONE:
3908 case CURLWC_ERROR:
3909 case CURLWC_CLEAR:
3910 if(wildcard->dtor)
3911 wildcard->dtor(wildcard->protdata);
3912 break;
3913 }
3914
3915 return result;
3916}