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
This commit is contained in:
Daniel Stenberg 2025-03-09 12:49:24 +01:00
parent f3b599a7e2
commit 8dca3b0656
No known key found for this signature in database
GPG key ID: 5CC908FDB71E12C2
12 changed files with 150 additions and 205 deletions

View file

@ -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 */

View file

@ -25,6 +25,7 @@
***************************************************************************/
#include "curl_setup.h"
#include "strparse.h"
typedef enum {
CURL_OFFT_OK, /* parsed fine */

View file

@ -1,3 +1,5 @@
enable STDERR
banfunc strncpy
banfunc sscanf
banfunc strtol
banfunc strtoul

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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)

View file

@ -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;
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 */
}
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;
}
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 */

View file

@ -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 */

View file

@ -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)
;