From: Daniel Stenberg Date: Mon, 18 Dec 2023 09:34:17 +0000 (+0100) Subject: curl.h: add CURLE_TOO_LARGE X-Git-Tag: curl-8_6_0~201 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=f58e493e44ad2f17bb574a4ccc5d47134e6f515f;p=thirdparty%2Fcurl.git curl.h: add CURLE_TOO_LARGE A new error code to be used when an internal field grows too large, like when a dynbuf reaches its maximum. Previously it would return CURLE_OUT_OF_MEMORY for this, which is highly misleading. Ref: #12268 Closes #12269 --- diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors.3 index 03f18e6b53..cd7bd9a3df 100644 --- a/docs/libcurl/libcurl-errors.3 +++ b/docs/libcurl/libcurl-errors.3 @@ -288,6 +288,8 @@ the specific problem. SSL Client Certificate required. .IP "CURLE_UNRECOVERABLE_POLL (99)" An internal call to poll() or select() returned error that is not recoverable. +.IP "CURLE_TOO_LARGE (100)" +A value or data field grew larger than allowed. .SH "CURLMcode" This is the generic return code used by functions in the libcurl multi interface. Also consider \fIcurl_multi_strerror(3)\fP. @@ -410,6 +412,8 @@ The URL contained an invalid number of slashes. The user part of the URL contained bad or invalid characters. .IP "CURLUE_LACKS_IDN (30)" libcurl lacks IDN support. +.IP "CURLUE_TOO_LARGE (31)" +A value or data field is larger than allowed. .SH "CURLHcode" The header interface returns a \fICURLHcode\fP to indicate when an error has occurred. diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index dc0e0629f0..179ba11025 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -328,6 +328,7 @@ CURLE_TFTP_NOSUCHUSER 7.15.0 CURLE_TFTP_NOTFOUND 7.15.0 CURLE_TFTP_PERM 7.15.0 CURLE_TFTP_UNKNOWNID 7.15.0 +CURLE_TOO_LARGE 8.6.0 CURLE_TOO_MANY_REDIRECTS 7.5 CURLE_UNKNOWN_OPTION 7.21.5 CURLE_UNKNOWN_TELNET_OPTION 7.7 7.21.5 @@ -1094,6 +1095,7 @@ CURLUE_NO_USER 7.62.0 CURLUE_NO_ZONEID 7.81.0 CURLUE_OK 7.62.0 CURLUE_OUT_OF_MEMORY 7.62.0 +CURLUE_TOO_LARGE 8.6.0 CURLUE_UNKNOWN_PART 7.62.0 CURLUE_UNSUPPORTED_SCHEME 7.62.0 CURLUE_URLDECODE 7.62.0 diff --git a/include/curl/curl.h b/include/curl/curl.h index cc24c05065..ab600b83c3 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -631,6 +631,7 @@ typedef enum { CURLE_PROXY, /* 97 - proxy handshake error */ CURLE_SSL_CLIENTCERT, /* 98 - client-side certificate required */ CURLE_UNRECOVERABLE_POLL, /* 99 - poll/select returned fatal error */ + CURLE_TOO_LARGE, /* 100 - a value/data met its maximum */ CURL_LAST /* never use! */ } CURLcode; diff --git a/include/curl/urlapi.h b/include/curl/urlapi.h index 88cdeb3bca..91f8c4548a 100644 --- a/include/curl/urlapi.h +++ b/include/curl/urlapi.h @@ -63,6 +63,7 @@ typedef enum { CURLUE_BAD_SLASHES, /* 28 */ CURLUE_BAD_USER, /* 29 */ CURLUE_LACKS_IDN, /* 30 */ + CURLUE_TOO_LARGE, /* 31 */ CURLUE_LAST } CURLUcode; diff --git a/lib/c-hyper.c b/lib/c-hyper.c index 787d6bbdbe..c4a395f0e9 100644 --- a/lib/c-hyper.c +++ b/lib/c-hyper.c @@ -148,7 +148,7 @@ static int hyper_each_header(void *userdata, if(name_len + value_len + 2 > CURL_MAX_HTTP_HEADER) { failf(data, "Too long response header"); - data->state.hresult = CURLE_OUT_OF_MEMORY; + data->state.hresult = CURLE_TOO_LARGE; return HYPER_ITER_BREAK; } diff --git a/lib/curl_printf.h b/lib/curl_printf.h index 46ef344f76..c2457d2a64 100644 --- a/lib/curl_printf.h +++ b/lib/curl_printf.h @@ -31,6 +31,10 @@ #include +#define MERR_OK 0 +#define MERR_MEM 1 +#define MERR_TOO_LARGE 2 + # undef printf # undef fprintf # undef msnprintf diff --git a/lib/dynbuf.c b/lib/dynbuf.c index a20266a681..a6a71c5daa 100644 --- a/lib/dynbuf.c +++ b/lib/dynbuf.c @@ -81,7 +81,7 @@ static CURLcode dyn_nappend(struct dynbuf *s, if(fit > s->toobig) { Curl_dyn_free(s); - return CURLE_OUT_OF_MEMORY; + return CURLE_TOO_LARGE; } else if(!a) { DEBUGASSERT(!indx); @@ -199,6 +199,9 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) if(!rc) return CURLE_OK; + else if(rc == MERR_TOO_LARGE) + return CURLE_TOO_LARGE; + return CURLE_OUT_OF_MEMORY; #else char *str; #ifdef __clang__ @@ -217,8 +220,8 @@ CURLcode Curl_dyn_vaddf(struct dynbuf *s, const char *fmt, va_list ap) } /* If we failed, we cleanup the whole buffer and return error */ Curl_dyn_free(s); + return CURLE_OK; #endif - return CURLE_OUT_OF_MEMORY; } /* diff --git a/lib/http.c b/lib/http.c index 582cb1cdbc..5b8f3e54b0 100644 --- a/lib/http.c +++ b/lib/http.c @@ -3179,7 +3179,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) ) { result = Curl_http2_switch(data, conn, FIRSTSOCKET); if(result) - return result; + goto fail; } else #endif @@ -3194,7 +3194,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) DEBUGF(infof(data, "HTTP/2 over clean TCP")); result = Curl_http2_switch(data, conn, FIRSTSOCKET); if(result) - return result; + goto fail; } break; } @@ -3204,11 +3204,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) result = Curl_http_host(data, conn); if(result) - return result; + goto fail; result = Curl_http_useragent(data); if(result) - return result; + goto fail; Curl_http_method(data, conn, &request, &httpreq); @@ -3224,7 +3224,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) (pq ? pq : data->state.up.path), FALSE); free(pq); if(result) - return result; + goto fail; } Curl_safefree(data->state.aptr.ref); @@ -3249,23 +3249,23 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* we only consider transfer-encoding magic if libz support is built-in */ result = Curl_transferencode(data); if(result) - return result; + goto fail; #endif result = Curl_http_body(data, conn, httpreq, &te); if(result) - return result; + goto fail; p_accept = Curl_checkheaders(data, STRCONST("Accept"))?NULL:"Accept: */*\r\n"; result = Curl_http_resume(data, conn, httpreq); if(result) - return result; + goto fail; result = Curl_http_range(data, httpreq); if(result) - return result; + goto fail; httpstring = get_http_string(data, conn); @@ -3283,7 +3283,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) result = Curl_http_target(data, conn, &req); if(result) { Curl_dyn_free(&req); - return result; + goto fail; } #ifndef CURL_DISABLE_ALTSVC @@ -3354,7 +3354,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) if(result) { Curl_dyn_free(&req); - return result; + goto fail; } if(!(conn->handler->flags&PROTOPT_SSL) && @@ -3390,7 +3390,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) } if(result) { Curl_dyn_free(&req); - return result; + goto fail; } if((http->postsize > -1) && @@ -3426,6 +3426,9 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) but is disabled here again to avoid that the chunked encoded version is actually used when sending the request body over h2 */ data->req.upload_chunky = FALSE; +fail: + if(CURLE_TOO_LARGE == result) + failf(data, "HTTP request too large"); return result; } diff --git a/lib/mprintf.c b/lib/mprintf.c index 60cf531486..514fdc87a8 100644 --- a/lib/mprintf.c +++ b/lib/mprintf.c @@ -39,6 +39,7 @@ #include "curl_setup.h" #include "dynbuf.h" +#include "curl_printf.h" #include #include "curl_memory.h" @@ -169,8 +170,7 @@ struct nsprintf { struct asprintf { struct dynbuf *b; - bool fail; /* if an alloc has failed and thus the output is not the complete - data */ + char merr; }; static long dprintf_DollarString(char *input, char **end) @@ -1062,25 +1062,27 @@ static int alloc_addbyter(int output, FILE *data) { struct asprintf *infop = (struct asprintf *)data; unsigned char outc = (unsigned char)output; + CURLcode result; - if(Curl_dyn_addn(infop->b, &outc, 1)) { - infop->fail = 1; + result = Curl_dyn_addn(infop->b, &outc, 1); + if(result) { + infop->merr = result == CURLE_TOO_LARGE ? MERR_TOO_LARGE : MERR_MEM; return -1; /* fail */ } return outc; /* fputc() returns like this on success */ } -/* appends the formatted string, returns 0 on success, 1 on error */ +/* appends the formatted string, returns MERR error code */ int Curl_dyn_vprintf(struct dynbuf *dyn, const char *format, va_list ap_save) { struct asprintf info; info.b = dyn; - info.fail = 0; + info.merr = MERR_OK; (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save); - if(info.fail) { + if(info.merr) { Curl_dyn_free(info.b); - return 1; + return info.merr; } return 0; } @@ -1091,10 +1093,10 @@ char *curl_mvaprintf(const char *format, va_list ap_save) struct dynbuf dyn; info.b = &dyn; Curl_dyn_init(info.b, DYN_APRINTF); - info.fail = 0; + info.merr = MERR_OK; (void)dprintf_formatf(&info, alloc_addbyter, format, ap_save); - if(info.fail) { + if(info.merr) { Curl_dyn_free(info.b); return NULL; } diff --git a/lib/strerror.c b/lib/strerror.c index 51e1485408..e35193a48c 100644 --- a/lib/strerror.c +++ b/lib/strerror.c @@ -319,6 +319,9 @@ curl_easy_strerror(CURLcode error) case CURLE_UNRECOVERABLE_POLL: return "Unrecoverable error in select/poll"; + case CURLE_TOO_LARGE: + return "A value or data field grew larger than allowed"; + /* error codes not used by current libcurl */ case CURLE_OBSOLETE20: case CURLE_OBSOLETE24: @@ -553,6 +556,9 @@ curl_url_strerror(CURLUcode error) case CURLUE_LACKS_IDN: return "libcurl lacks IDN support"; + case CURLUE_TOO_LARGE: + return "A value or data field is larger than allowed"; + case CURLUE_LAST: break; } diff --git a/lib/urlapi.c b/lib/urlapi.c index 56ee16f055..b9ea68eac5 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -126,6 +126,9 @@ static const char *find_host_sep(const char *url) return sep < query ? sep : query; } +/* convert CURLcode to CURLUcode */ +#define cc2cu(x) ((x) == CURLE_TOO_LARGE ? CURLUE_TOO_LARGE : \ + CURLUE_OUT_OF_MEMORY) /* * Decide whether a character in a URL must be escaped. */ @@ -146,6 +149,7 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, bool left = !query; const unsigned char *iptr; const unsigned char *host_sep = (const unsigned char *) url; + CURLcode result; if(!relative) host_sep = (const unsigned char *) find_host_sep(url); @@ -154,20 +158,19 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, len; iptr++, len--) { if(iptr < host_sep) { - if(Curl_dyn_addn(o, iptr, 1)) - return CURLUE_OUT_OF_MEMORY; + result = Curl_dyn_addn(o, iptr, 1); + if(result) + return cc2cu(result); continue; } if(*iptr == ' ') { - if(left) { - if(Curl_dyn_addn(o, "%20", 3)) - return CURLUE_OUT_OF_MEMORY; - } - else { - if(Curl_dyn_addn(o, "+", 1)) - return CURLUE_OUT_OF_MEMORY; - } + if(left) + result = Curl_dyn_addn(o, "%20", 3); + else + result = Curl_dyn_addn(o, "+", 1); + if(result) + return cc2cu(result); continue; } @@ -178,13 +181,12 @@ static CURLUcode urlencode_str(struct dynbuf *o, const char *url, char out[3]={'%'}; out[1] = hexdigits[*iptr>>4]; out[2] = hexdigits[*iptr & 0xf]; - if(Curl_dyn_addn(o, out, 3)) - return CURLUE_OUT_OF_MEMORY; - } - else { - if(Curl_dyn_addn(o, iptr, 1)) - return CURLUE_OUT_OF_MEMORY; + result = Curl_dyn_addn(o, out, 3); } + else + result = Curl_dyn_addn(o, iptr, 1); + if(result) + return cc2cu(result); } return CURLUE_OK; @@ -248,7 +250,7 @@ size_t Curl_is_absolute_url(const char *url, char *buf, size_t buflen, * * Note that this function destroys the 'base' string. */ -static char *concat_url(char *base, const char *relurl) +static CURLcode concat_url(char *base, const char *relurl, char **newurl) { /*** TRY to append this new path to the old URL @@ -260,6 +262,9 @@ static char *concat_url(char *base, const char *relurl) char *pathsep; bool host_changed = FALSE; const char *useurl = relurl; + CURLcode result = CURLE_OK; + CURLUcode uc; + *newurl = NULL; /* protsep points to the start of the host name */ protsep = strstr(base, "//"); @@ -360,21 +365,27 @@ static char *concat_url(char *base, const char *relurl) Curl_dyn_init(&newest, CURL_MAX_INPUT_LENGTH); /* copy over the root url part */ - if(Curl_dyn_add(&newest, base)) - return NULL; + result = Curl_dyn_add(&newest, base); + if(result) + return result; /* check if we need to append a slash */ if(('/' == useurl[0]) || (protsep && !*protsep) || ('?' == useurl[0])) ; else { - if(Curl_dyn_addn(&newest, "/", 1)) - return NULL; + result = Curl_dyn_addn(&newest, "/", 1); + if(result) + return result; } /* then append the new piece on the right side */ - urlencode_str(&newest, useurl, strlen(useurl), !host_changed, FALSE); + uc = urlencode_str(&newest, useurl, strlen(useurl), !host_changed, + FALSE); + if(uc) + return (uc == CURLUE_TOO_LARGE) ? CURLE_TOO_LARGE : CURLE_OUT_OF_MEMORY; - return Curl_dyn_ptr(&newest); + *newurl = Curl_dyn_ptr(&newest); + return CURLE_OK; } /* scan for byte values <= 31, 127 and sometimes space */ @@ -775,7 +786,7 @@ static CURLUcode urldecode_host(struct dynbuf *host) result = Curl_dyn_addn(host, decoded, dlen); free(decoded); if(result) - return CURLUE_OUT_OF_MEMORY; + return cc2cu(result); } return CURLUE_OK; @@ -788,22 +799,24 @@ static CURLUcode parse_authority(struct Curl_URL *u, bool has_scheme) { size_t offset; - CURLUcode result; + CURLUcode uc; + CURLcode result; /* * Parse the login details and strip them out of the host name. */ - result = parse_hostname_login(u, auth, authlen, flags, &offset); - if(result) + uc = parse_hostname_login(u, auth, authlen, flags, &offset); + if(uc) goto out; - if(Curl_dyn_addn(host, auth + offset, authlen - offset)) { - result = CURLUE_OUT_OF_MEMORY; + result = Curl_dyn_addn(host, auth + offset, authlen - offset); + if(result) { + uc = cc2cu(result); goto out; } - result = Curl_parse_port(u, host, has_scheme); - if(result) + uc = Curl_parse_port(u, host, has_scheme); + if(uc) goto out; if(!Curl_dyn_len(host)) @@ -813,24 +826,24 @@ static CURLUcode parse_authority(struct Curl_URL *u, case HOST_IPV4: break; case HOST_IPV6: - result = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); + uc = ipv6_parse(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); break; case HOST_NAME: - result = urldecode_host(host); - if(!result) - result = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); + uc = urldecode_host(host); + if(!uc) + uc = hostname_check(u, Curl_dyn_ptr(host), Curl_dyn_len(host)); break; case HOST_ERROR: - result = CURLUE_OUT_OF_MEMORY; + uc = CURLUE_OUT_OF_MEMORY; break; case HOST_BAD: default: - result = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */ + uc = CURLUE_BAD_HOSTNAME; /* Bad IPv4 address even */ break; } out: - return result; + return uc; } CURLUcode Curl_url_set_authority(CURLU *u, const char *authority, @@ -1079,8 +1092,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) len = path - ptr; if(len) { - if(Curl_dyn_addn(&host, ptr, len)) { - result = CURLUE_OUT_OF_MEMORY; + CURLcode code = Curl_dyn_addn(&host, ptr, len); + if(code) { + result = cc2cu(code); goto fail; } uncpath = TRUE; @@ -1233,10 +1247,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if(flags & CURLU_URLENCODE) { struct dynbuf enc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE)) { - result = CURLUE_OUT_OF_MEMORY; + result = urlencode_str(&enc, fragment + 1, fraglen - 1, TRUE, FALSE); + if(result) goto fail; - } u->fragment = Curl_dyn_ptr(&enc); } else { @@ -1262,10 +1275,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) struct dynbuf enc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); /* skip the leading question mark */ - if(urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE)) { - result = CURLUE_OUT_OF_MEMORY; + result = urlencode_str(&enc, query + 1, qlen - 1, TRUE, TRUE); + if(result) goto fail; - } u->query = Curl_dyn_ptr(&enc); } else { @@ -1289,10 +1301,9 @@ static CURLUcode parseurl(const char *url, CURLU *u, unsigned int flags) if(pathlen && (flags & CURLU_URLENCODE)) { struct dynbuf enc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, path, pathlen, TRUE, FALSE)) { - result = CURLUE_OUT_OF_MEMORY; + result = urlencode_str(&enc, path, pathlen, TRUE, FALSE); + if(result) goto fail; - } pathlen = Curl_dyn_len(&enc); path = u->path = Curl_dyn_ptr(&enc); } @@ -1628,10 +1639,11 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, } if(urlencode) { struct dynbuf enc; + CURLUcode uc; Curl_dyn_init(&enc, CURL_MAX_INPUT_LENGTH); - if(urlencode_str(&enc, *part, partlen, TRUE, - what == CURLUPART_QUERY)) - return CURLUE_OUT_OF_MEMORY; + uc = urlencode_str(&enc, *part, partlen, TRUE, what == CURLUPART_QUERY); + if(uc) + return uc; free(*part); *part = Curl_dyn_ptr(&enc); } @@ -1816,7 +1828,8 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, * If the existing contents is enough for a URL, allow a relative URL to * replace it. */ - CURLUcode result; + CURLcode result; + CURLUcode uc; char *oldurl; char *redired_url; @@ -1836,14 +1849,14 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, /* apply the relative part to create a new URL * and replace the existing one with it. */ - redired_url = concat_url(oldurl, part); + result = concat_url(oldurl, part, &redired_url); free(oldurl); - if(!redired_url) - return CURLUE_OUT_OF_MEMORY; + if(result) + return cc2cu(result); - result = parseurl_and_replace(redired_url, u, flags); + uc = parseurl_and_replace(redired_url, u, flags); free(redired_url); - return result; + return uc; } default: return CURLUE_UNKNOWN_PART; @@ -1857,7 +1870,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, if(leadingslash && (part[0] != '/')) { CURLcode result = Curl_dyn_addn(&enc, "/", 1); if(result) - return CURLUE_OUT_OF_MEMORY; + return cc2cu(result); } if(urlencode) { const unsigned char *i; @@ -1877,7 +1890,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, equalsencode = FALSE; result = Curl_dyn_addn(&enc, i, 1); if(result) - return CURLUE_OUT_OF_MEMORY; + return cc2cu(result); } else { char out[3]={'%'}; @@ -1885,7 +1898,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, out[2] = hexdigits[*i & 0xf]; result = Curl_dyn_addn(&enc, out, 3); if(result) - return CURLUE_OUT_OF_MEMORY; + return cc2cu(result); } } } @@ -1893,7 +1906,7 @@ CURLUcode curl_url_set(CURLU *u, CURLUPart what, char *p; CURLcode result = Curl_dyn_add(&enc, part); if(result) - return CURLUE_OUT_OF_MEMORY; + return cc2cu(result); p = Curl_dyn_ptr(&enc); while(*p) { /* make sure percent encoded are lower case */ diff --git a/tests/data/test1154 b/tests/data/test1154 index b7349e17d2..bd08ce26c1 100644 --- a/tests/data/test1154 +++ b/tests/data/test1154 @@ -47,9 +47,9 @@ User-Agent: curl/%VERSION Accept: */* -# 27 == CURLE_OUT_OF_MEMORY +# 100 == CURLE_TOO_LARGE -27 +100 diff --git a/tests/data/test1538 b/tests/data/test1538 index 59cd1628ea..c0f038be40 100644 --- a/tests/data/test1538 +++ b/tests/data/test1538 @@ -132,7 +132,8 @@ e96: QUIC connection error e97: proxy handshake error e98: SSL Client Certificate required e99: Unrecoverable error in select/poll -e100: Unknown error +e100: A value or data field grew larger than allowed +e101: Unknown error m-1: Please call curl_multi_perform() soon m0: No error m1: Invalid multi handle @@ -186,7 +187,8 @@ u27: Bad scheme u28: Unsupported number of slashes following scheme u29: Bad user u30: libcurl lacks IDN support -u31: CURLUcode unknown +u31: A value or data field is larger than allowed +u32: CURLUcode unknown