From 8dca3b0656d6c2db47f98d3773f20d77f4bacb82 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Sun, 9 Mar 2025 12:49:24 +0100 Subject: [PATCH] src: replace strto[u][ld] with curlx_str_ parsers - Better error handling (no errno mess), better limit checks. - Also removed all uses of curlx_strtoofft() Closes #16634 --- lib/strparse.h | 5 ++ lib/strtoofft.h | 1 + src/.checksrc | 2 + src/terminal.c | 8 +-- src/tool_cb_hdr.c | 11 ++-- src/tool_getparam.c | 72 ++++++++++++--------------- src/tool_main.c | 8 +-- src/tool_operate.c | 7 +-- src/tool_paramhlp.c | 119 ++++++++++++++++---------------------------- src/tool_urlglob.c | 94 ++++++++++++++-------------------- src/tool_urlglob.h | 2 +- src/var.c | 26 ++++------ 12 files changed, 150 insertions(+), 205 deletions(-) diff --git a/lib/strparse.h b/lib/strparse.h index 8b4d1b68e9..a3f9d8b082 100644 --- a/lib/strparse.h +++ b/lib/strparse.h @@ -99,4 +99,9 @@ int Curl_str_cspn(const char **linep, struct Curl_str *out, const char *cspn); void Curl_str_trimblanks(struct Curl_str *out); void Curl_str_passblanks(const char **linep); +#define curlx_str_number(x,y,z) Curl_str_number(x,y,z) +#define curlx_str_octal(x,y,z) Curl_str_octal(x,y,z) +#define curlx_str_single(x,y) Curl_str_single(x,y) +#define curlx_str_passblanks(x) Curl_str_passblanks(x) + #endif /* HEADER_CURL_STRPARSE_H */ diff --git a/lib/strtoofft.h b/lib/strtoofft.h index 051ab3064d..05b89fee69 100644 --- a/lib/strtoofft.h +++ b/lib/strtoofft.h @@ -25,6 +25,7 @@ ***************************************************************************/ #include "curl_setup.h" +#include "strparse.h" typedef enum { CURL_OFFT_OK, /* parsed fine */ diff --git a/src/.checksrc b/src/.checksrc index 272515f78f..486670ca99 100644 --- a/src/.checksrc +++ b/src/.checksrc @@ -1,3 +1,5 @@ enable STDERR banfunc strncpy banfunc sscanf +banfunc strtol +banfunc strtoul diff --git a/src/terminal.c b/src/terminal.c index cbee7ca386..de9ecb6e19 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -28,6 +28,7 @@ #endif #include "terminal.h" +#include "strtoofft.h" #include "memdebug.h" /* keep this as LAST include */ @@ -47,10 +48,9 @@ unsigned int get_terminal_columns(void) unsigned int width = 0; char *colp = curl_getenv("COLUMNS"); if(colp) { - char *endptr; - long num = strtol(colp, &endptr, 10); - if((endptr != colp) && (endptr == colp + strlen(colp)) && (num > 20) && - (num < 10000)) + curl_off_t num; + const char *p = colp; + if(!curlx_str_number(&p, &num, 10000) && (num > 20)) width = (unsigned int)num; curl_free(colp); } diff --git a/src/tool_cb_hdr.c b/src/tool_cb_hdr.c index 5696b41472..c88de3c697 100644 --- a/src/tool_cb_hdr.c +++ b/src/tool_cb_hdr.c @@ -414,13 +414,14 @@ void write_linked_location(CURL *curl, const char *location, size_t loclen, const char *loc = location; size_t llen = loclen; int space_skipped = 0; - char *vver = getenv("VTE_VERSION"); + const char *vver = getenv("VTE_VERSION"); if(vver) { - long vvn = strtol(vver, NULL, 10); - /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since some - of those versions have formatting bugs. (#10428) */ - if(0 < vvn && vvn <= 4801) + curl_off_t num; + if(curlx_str_number(&vver, &num, CURL_OFF_T_MAX) || + /* Skip formatting for old versions of VTE <= 0.48.1 (Mar 2017) since + some of those versions have formatting bugs. (#10428) */ + (num <= 4801)) goto locout; } diff --git a/src/tool_getparam.c b/src/tool_getparam.c index e42f263861..8d438bcbb9 100644 --- a/src/tool_getparam.c +++ b/src/tool_getparam.c @@ -529,18 +529,18 @@ static ParameterError GetSizeParameter(struct GlobalConfig *global, const char *which, curl_off_t *value_out) { - char *unit; + const char *unit = arg; curl_off_t value; - if(curlx_strtoofft(arg, &unit, 10, &value)) { + if(curlx_str_number(&unit, &value, CURL_OFF_T_MAX)) { warnf(global, "invalid number specified for %s", which); return PARAM_BAD_USE; } if(!*unit) - unit = (char *)"b"; + unit = "b"; else if(strlen(unit) > 1) - unit = (char *)"w"; /* unsupported */ + unit = "w"; /* unsupported */ switch(*unit) { case 'G': @@ -973,7 +973,7 @@ static ParameterError set_rate(struct GlobalConfig *global, /d == per day (24 hours) */ ParameterError err = PARAM_OK; - char *div = strchr(nextarg, '/'); + const char *div = strchr(nextarg, '/'); char number[26]; long denominator; long numerator = 60*60*1000; /* default per hour */ @@ -991,22 +991,20 @@ static ParameterError set_rate(struct GlobalConfig *global, return PARAM_BAD_USE; if(div) { - char unit = div[1]; curl_off_t numunits; - char *endp; + const char *s; + div++; + s = div; - if(curlx_strtoofft(&div[1], &endp, 10, &numunits)) { - /* if it fails, there is no legit number specified */ - if(endp == &div[1]) - /* if endp did not move, accept it as a 1 */ + if(curlx_str_number(&div, &numunits, CURL_OFF_T_MAX)) { + if(s == div) + /* if div did not move, accept it as a 1 */ numunits = 1; else return PARAM_BAD_USE; } - else - unit = *endp; - switch(unit) { + switch(*div) { case 's': /* per second */ numerator = 1000; break; @@ -1402,48 +1400,42 @@ static ParameterError parse_range(struct GlobalConfig *global, const char *nextarg) { ParameterError err = PARAM_OK; + curl_off_t value; + const char *orig = nextarg; if(config->use_resume) { errorf(global, "--continue-at is mutually exclusive with --range"); return PARAM_BAD_USE; } - /* Specifying a range WITHOUT A DASH will create an illegal HTTP range - (and will not actually be range by definition). The manpage - previously claimed that to be a good way, why this code is added to - work-around it. */ - if(ISDIGIT(*nextarg) && !strchr(nextarg, '-')) { + if(!curlx_str_number(&nextarg, &value, CURL_OFF_T_MAX) && + curlx_str_single(&nextarg, '-')) { + /* Specifying a range WITHOUT A DASH will create an illegal HTTP range + (and will not actually be range by definition). The manpage previously + claimed that to be a good way, why this code is added to work-around + it. */ char buffer[32]; - curl_off_t value; - if(curlx_strtoofft(nextarg, NULL, 10, &value)) { - warnf(global, "unsupported range point"); - err = PARAM_BAD_USE; - } - else { - warnf(global, - "A specified range MUST include at least one dash (-). " - "Appending one for you"); - msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", - value); - Curl_safefree(config->range); - config->range = strdup(buffer); - if(!config->range) - err = PARAM_NO_MEM; - } + warnf(global, "A specified range MUST include at least one dash (-). " + "Appending one for you"); + msnprintf(buffer, sizeof(buffer), "%" CURL_FORMAT_CURL_OFF_T "-", + value); + free(config->range); + config->range = strdup(buffer); + if(!config->range) + err = PARAM_NO_MEM; } else { /* byte range requested */ - const char *tmp_range = nextarg; - while(*tmp_range) { - if(!ISDIGIT(*tmp_range) && *tmp_range != '-' && *tmp_range != ',') { + while(*nextarg) { + if(!ISDIGIT(*nextarg) && *nextarg != '-' && *nextarg != ',') { warnf(global, "Invalid character is found in given range. " "A specified range MUST have only digits in " "\'start\'-\'stop\'. The server's response to this " "request is uncertain."); break; } - tmp_range++; + nextarg++; } - err = getstr(&config->range, nextarg, DENY_BLANK); + err = getstr(&config->range, orig, DENY_BLANK); } return err; } diff --git a/src/tool_main.c b/src/tool_main.c index 5910adeb3b..f75007dbb1 100644 --- a/src/tool_main.c +++ b/src/tool_main.c @@ -133,10 +133,10 @@ static void memory_tracking_init(void) /* if CURL_MEMLIMIT is set, this enables fail-on-alloc-number-N feature */ env = curl_getenv("CURL_MEMLIMIT"); if(env) { - char *endptr; - long num = strtol(env, &endptr, 10); - if((endptr != env) && (endptr == env + strlen(env)) && (num > 0)) - curl_dbg_memlimit(num); + curl_off_t num; + const char *p = env; + if(!curlx_str_number(&p, &num, LONG_MAX)) + curl_dbg_memlimit((long)num); curl_free(env); } } diff --git a/src/tool_operate.c b/src/tool_operate.c index 2d98402258..453146097b 100644 --- a/src/tool_operate.c +++ b/src/tool_operate.c @@ -930,9 +930,10 @@ static CURLcode config2setopts(struct GlobalConfig *global, #ifdef DEBUGBUILD char *env = getenv("CURL_BUFFERSIZE"); if(env) { - long size = strtol(env, NULL, 10); - if(size) - my_setopt(curl, CURLOPT_BUFFERSIZE, size); + curl_off_t num; + const char *p = env; + if(!Curl_str_number(&p, &num, LONG_MAX)) + my_setopt(curl, CURLOPT_BUFFERSIZE, (long)num); } else #endif diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c index 76792a0099..3548e509f6 100644 --- a/src/tool_paramhlp.c +++ b/src/tool_paramhlp.c @@ -24,7 +24,6 @@ #include "tool_setup.h" #include "strcase.h" - #include "curlx.h" #include "tool_cfgable.h" @@ -225,17 +224,25 @@ ParameterError file2memory(char **bufp, size_t *size, FILE *file) */ static ParameterError getnum(long *val, const char *str, int base) { + DEBUGASSERT((base == 8) || (base == 10)); if(str) { - char *endptr = NULL; - long num; - if(!str[0]) - return PARAM_BLANK_STRING; - CURL_SETERRNO(0); - num = strtol(str, &endptr, base); - if(errno == ERANGE) - return PARAM_NUMBER_TOO_LARGE; - if((endptr != str) && (*endptr == '\0')) { - *val = num; + curl_off_t num; + bool is_neg = FALSE; + if(base == 10) { + is_neg = (*str == '-'); + if(is_neg) + str++; + if(curlx_str_number(&str, &num, LONG_MAX)) + return PARAM_BAD_NUMERIC; + } + else { /* base == 8 */ + if(curlx_str_octal(&str, &num, LONG_MAX)) + return PARAM_BAD_NUMERIC; + } + if(!curlx_str_single(&str, '\0')) { + *val = (long)num; + if(is_neg) + *val = -*val; return PARAM_OK; /* Ok */ } } @@ -301,40 +308,6 @@ ParameterError str2unummax(long *val, const char *str, long max) return PARAM_OK; } - -/* - * Parse the string and write the double in the given address. Return PARAM_OK - * on success, otherwise a parameter specific error enum. - * - * The 'max' argument is the maximum value allowed, as the numbers are often - * multiplied when later used. - * - * Since this function gets called with the 'nextarg' pointer from within the - * getparameter a lot, we must check it for NULL before accessing the str - * data. - */ - -static ParameterError str2double(double *val, const char *str, double max) -{ - if(str) { - char *endptr; - double num; - CURL_SETERRNO(0); - num = strtod(str, &endptr); - if(errno == ERANGE) - return PARAM_NUMBER_TOO_LARGE; - if(num > max) { - /* too large */ - return PARAM_NUMBER_TOO_LARGE; - } - if((endptr != str) && (endptr == str + strlen(str))) { - *val = num; - return PARAM_OK; /* Ok */ - } - } - return PARAM_BAD_NUMERIC; /* badness */ -} - /* * Parse the string as seconds with decimals, and write the number of * milliseconds that corresponds in the given address. Return PARAM_OK on @@ -350,14 +323,29 @@ static ParameterError str2double(double *val, const char *str, double max) ParameterError secs2ms(long *valp, const char *str) { - double value; - ParameterError result = str2double(&value, str, (double)LONG_MAX/1000); - if(result != PARAM_OK) - return result; - if(value < 0) - return PARAM_NEGATIVE_NUMERIC; + curl_off_t secs; + long ms = 0; + const unsigned int digs[] = { 1, 10, 100, 1000, 10000, 1000000, + 1000000, 10000000, 100000000 }; + if(!str || + curlx_str_number(&str, &secs, CURL_OFF_T_MAX/100)) + return PARAM_BAD_NUMERIC; + if(!curlx_str_single(&str, '.')) { + curl_off_t fracs; + const char *s = str; + size_t len; + if(curlx_str_number(&str, &fracs, CURL_OFF_T_MAX)) + return PARAM_NUMBER_TOO_LARGE; + /* how many milliseconds are in fracs ? */ + len = (str - s); + while((len > sizeof(CURL_ARRAYSIZE(digs)) || (fracs > LONG_MAX/100))) { + fracs /= 10; + len--; + } + ms = ((long)fracs * 100) / digs[len - 1]; + } - *valp = (long)(value*1000); + *valp = (long)secs * 1000 + ms; return PARAM_OK; } @@ -560,29 +548,10 @@ ParameterError check_protocol(const char *str) */ ParameterError str2offset(curl_off_t *val, const char *str) { - char *endptr; - if(str[0] == '-') - /* offsets are not negative, this indicates weird input */ - return PARAM_NEGATIVE_NUMERIC; - -#if(SIZEOF_CURL_OFF_T > SIZEOF_LONG) - { - CURLofft offt = curlx_strtoofft(str, &endptr, 10, val); - if(CURL_OFFT_FLOW == offt) - return PARAM_NUMBER_TOO_LARGE; - else if(CURL_OFFT_INVAL == offt) - return PARAM_BAD_NUMERIC; - } -#else - CURL_SETERRNO(0); - *val = strtol(str, &endptr, 0); - if((*val == LONG_MIN || *val == LONG_MAX) && errno == ERANGE) - return PARAM_NUMBER_TOO_LARGE; -#endif - if((endptr != str) && (endptr == str + strlen(str))) - return PARAM_OK; - - return PARAM_BAD_NUMERIC; + if(curlx_str_number(&str, val, CURL_OFF_T_MAX) || + curlx_str_single(&str, '\0')) + return PARAM_BAD_NUMERIC; + return PARAM_OK; } #define MAX_USERPWDLENGTH (100*1024) diff --git a/src/tool_urlglob.c b/src/tool_urlglob.c index a296e3535b..07e1f05e50 100644 --- a/src/tool_urlglob.c +++ b/src/tool_urlglob.c @@ -85,7 +85,7 @@ static int multiply(curl_off_t *amount, curl_off_t with) return 0; } -static CURLcode glob_set(struct URLGlob *glob, char **patternp, +static CURLcode glob_set(struct URLGlob *glob, const char **patternp, size_t *posp, curl_off_t *amount, int globindex) { @@ -95,8 +95,8 @@ static CURLcode glob_set(struct URLGlob *glob, char **patternp, struct URLPattern *pat; bool done = FALSE; char *buf = glob->glob_buffer; - char *pattern = *patternp; - char *opattern = pattern; + const char *pattern = *patternp; + const char *opattern = pattern; size_t opos = *posp-1; pat = &glob->pattern[glob->size]; @@ -180,7 +180,7 @@ static CURLcode glob_set(struct URLGlob *glob, char **patternp, return CURLE_OK; } -static CURLcode glob_range(struct URLGlob *glob, char **patternp, +static CURLcode glob_range(struct URLGlob *glob, const char **patternp, size_t *posp, curl_off_t *amount, int globindex) { @@ -191,8 +191,8 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp, expression is checked for well-formedness and collected until the next ']' */ struct URLPattern *pat; - char *pattern = *patternp; - char *c; + const char *pattern = *patternp; + const char *c; pat = &glob->pattern[glob->size]; pat->globindex = globindex; @@ -214,13 +214,13 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp, pmatch = TRUE; if(end_c == ':') { - char *endp; - CURL_SETERRNO(0); - step = strtoul(&pattern[4], &endp, 10); - if(errno || &pattern[4] == endp || *endp != ']') + curl_off_t num; + const char *p = &pattern[4]; + if(curlx_str_number(&p, &num, 256) || curlx_str_single(&p, ']')) step = 0; else - pattern = endp + 1; + step = (unsigned long)num; + pattern = p; } else if(end_c != ']') /* then this is wrong */ @@ -232,7 +232,7 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp, *posp += (pattern - *patternp); - if(!pmatch || !step || step > (unsigned)INT_MAX || + if(!pmatch || !step || (min_c == max_c && step != 1) || (min_c != max_c && (min_c > max_c || step > (unsigned)(max_c - min_c) || (max_c - min_c) > ('z' - 'a')))) @@ -251,10 +251,10 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp, } else if(ISDIGIT(*pattern)) { /* numeric range detected */ - unsigned long min_n; + unsigned long min_n = 0; unsigned long max_n = 0; unsigned long step_n = 0; - char *endp; + curl_off_t num; pat->type = UPTNumRange; pat->content.NumRange.padlength = 0; @@ -269,48 +269,27 @@ static CURLcode glob_range(struct URLGlob *glob, char **patternp, } } - CURL_SETERRNO(0); - min_n = strtoul(pattern, &endp, 10); - if(errno || (endp == pattern)) - endp = NULL; - else { - if(*endp != '-') - endp = NULL; - else { - pattern = endp + 1; - while(ISBLANK(*pattern)) - pattern++; - if(!ISDIGIT(*pattern)) { - endp = NULL; - goto fail; - } - CURL_SETERRNO(0); - max_n = strtoul(pattern, &endp, 10); - if(errno) - /* overflow */ - endp = NULL; - else if(*endp == ':') { - pattern = endp + 1; - CURL_SETERRNO(0); - step_n = strtoul(pattern, &endp, 10); - if(errno) - /* over/underflow situation */ - endp = NULL; - } - else - step_n = 1; - if(endp && (*endp == ']')) { - pattern = endp + 1; + if(!curlx_str_number(&pattern, &num, CURL_OFF_T_MAX)) { + min_n = (unsigned long)num; + if(!curlx_str_single(&pattern, '-')) { + curlx_str_passblanks(&pattern); + if(!curlx_str_number(&pattern, &num, CURL_OFF_T_MAX)) { + max_n = (unsigned long)num; + if(!curlx_str_single(&pattern, ']')) + step_n = 1; + else if(!curlx_str_single(&pattern, ':') && + !curlx_str_number(&pattern, &num, CURL_OFF_T_MAX) && + !curlx_str_single(&pattern, ']')) { + step_n = (unsigned long)num; + } + /* else bad syntax */ } - else - endp = NULL; } } -fail: *posp += (pattern - *patternp); - if(!endp || !step_n || + if(!step_n || (min_n == max_n && step_n != 1) || (min_n != max_n && (min_n > max_n || step_n > (max_n - min_n)))) /* the pattern is not well-formed */ @@ -371,7 +350,7 @@ static bool peek_ipv6(const char *str, size_t *skip) return rc ? FALSE : TRUE; } -static CURLcode glob_parse(struct URLGlob *glob, char *pattern, +static CURLcode glob_parse(struct URLGlob *glob, const char *pattern, size_t pos, curl_off_t *amount) { /* processes a literal string component of a URL @@ -626,10 +605,11 @@ CURLcode glob_next_url(char **globbed, struct URLGlob *glob) #define MAX_OUTPUT_GLOB_LENGTH (10*1024) -CURLcode glob_match_url(char **result, char *filename, struct URLGlob *glob) +CURLcode glob_match_url(char **result, const char *filename, + struct URLGlob *glob) { char numbuf[18]; - char *appendthis = (char *)""; + const char *appendthis = (char *)""; size_t appendlen = 0; struct curlx_dynbuf dyn; @@ -642,11 +622,11 @@ CURLcode glob_match_url(char **result, char *filename, struct URLGlob *glob) while(*filename) { if(*filename == '#' && ISDIGIT(filename[1])) { - char *ptr = filename; - unsigned long num = strtoul(&filename[1], &filename, 10); + const char *ptr = filename; + curl_off_t num; struct URLPattern *pat = NULL; - - if(num && (num < glob->size)) { + filename++; + if(!curlx_str_number(&filename, &num, glob->size) && num) { unsigned long i; num--; /* make it zero based */ /* find the correct glob entry */ diff --git a/src/tool_urlglob.h b/src/tool_urlglob.h index e31c3d87e5..6fcd0db498 100644 --- a/src/tool_urlglob.h +++ b/src/tool_urlglob.h @@ -72,7 +72,7 @@ struct URLGlob { CURLcode glob_url(struct URLGlob**, char *, curl_off_t *, FILE *); CURLcode glob_next_url(char **, struct URLGlob *); -CURLcode glob_match_url(char **, char *, struct URLGlob *); +CURLcode glob_match_url(char **, const char *, struct URLGlob *); void glob_cleanup(struct URLGlob **glob); #endif /* HEADER_CURL_TOOL_URLGLOB_H */ diff --git a/src/var.c b/src/var.c index 1eab942b66..7d7fd513f0 100644 --- a/src/var.c +++ b/src/var.c @@ -448,25 +448,19 @@ ParameterError setvariable(struct GlobalConfig *global, clen = strlen(ge); } } - if(*line == '[') { + if(*line == '[' && ISDIGIT(line[1])) { /* is there a byte range specified? [num-num] */ - if(ISDIGIT(line[1])) { - char *endp; - if(curlx_strtoofft(&line[1], &endp, 10, &startoffset) || (*endp != '-')) - return PARAM_VAR_SYNTAX; - else { - char *p = endp + 1; /* pass the '-' */ - if(*p != ']') { - if(curlx_strtoofft(p, &endp, 10, &endoffset) || (*endp != ']')) - return PARAM_VAR_SYNTAX; - line = &endp[1]; /* pass the ']' */ - } - else - line = &p[1]; /* pass the ']' */ - } - if(startoffset > endoffset) + line++; + if(curlx_str_number(&line, &startoffset, CURL_OFF_T_MAX) || + curlx_str_single(&line, '-')) + return PARAM_VAR_SYNTAX; + if(curlx_str_single(&line, ']')) { + if(curlx_str_number(&line, &endoffset, CURL_OFF_T_MAX) || + curlx_str_single(&line, ']')) return PARAM_VAR_SYNTAX; } + if(startoffset > endoffset) + return PARAM_VAR_SYNTAX; } if(content) ; -- 2.47.3