PR #24832 opened by Jun Zhao (mypopydev) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24832 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24832.patch
setup_curl() ignored every return value, so an unsupported option such as -http_version 3 on a libcurl built without HTTP/3 was silently dropped and the request went out with curl default version. Check all setopt calls in setup_curl(), setup_protocols() and start_request(), log the curl error and fail the transfer instead of continuing with settings that were not applied. Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/24812 Signed-off-by: Jun Zhao <[email protected]> # Summary of changes Briefly describe what this PR does and why. <!-- If this PR requires new FATE test samples, attach them to the PR and list their target paths below (relative to the fate-suite root). Attached filenames must match the sample's filename: ```fate-samples # e.g. vorbis/new-sample.ogg ``` --> >From 414c6212042411f67d6760215ab0c57268769998 Mon Sep 17 00:00:00 2001 From: Jun Zhao <[email protected]> Date: Thu, 1 Oct 2026 12:19:55 +0800 Subject: [PATCH] avformat/libcurl: check curl_easy_setopt() return values setup_curl() ignored every return value, so an unsupported option such as -http_version 3 on a libcurl built without HTTP/3 was silently dropped and the request went out with curl default version. Check all setopt calls in setup_curl(), setup_protocols() and start_request(), log the curl error and fail the transfer instead of continuing with settings that were not applied. Fixes: https://code.ffmpeg.org/FFmpeg/FFmpeg/issues/24812 Signed-off-by: Jun Zhao <[email protected]> --- libavformat/libcurl.c | 159 ++++++++++++++++++++++++++++-------------- 1 file changed, 105 insertions(+), 54 deletions(-) diff --git a/libavformat/libcurl.c b/libavformat/libcurl.c index 983b207722..3d7cf2c27a 100644 --- a/libavformat/libcurl.c +++ b/libavformat/libcurl.c @@ -210,6 +210,16 @@ static int curlcode_to_averror(CURLcode code) } } +static int curl_setopt_checked(CurlContext *c, const char *name, CURLcode code) +{ + if (code == CURLE_OK) + return 0; + + av_log(c->h, AV_LOG_ERROR, "Could not set %s: %s\n", + name, curl_easy_strerror(code)); + return curlcode_to_averror(code); +} + static int curlmcode_to_averror(CURLMcode code) { switch (code) { @@ -634,9 +644,13 @@ static int xferinfo_callback(void *userdata, curl_off_t dltotal, curl_off_t dlno * thread only. */ static void start_request(CurlContext *c) { + char range[48]; + const char *range_value = NULL; + CURLcode cc; + int ret; + if (!c->probed || c->seekable) { int64_t start = c->request_start; - char range[48]; int64_t request_size = c->request_size; if (c->is_initial && c->initial_request_size > 0) request_size = c->initial_request_size; @@ -652,9 +666,15 @@ static void start_request(CurlContext *c) } else { snprintf(range, sizeof(range), "%"PRId64"-", start); } - curl_easy_setopt(c->easy, CURLOPT_RANGE, range); - } else { - curl_easy_setopt(c->easy, CURLOPT_RANGE, NULL); + range_value = range; + } + cc = curl_easy_setopt(c->easy, CURLOPT_RANGE, range_value); + if ((ret = curl_setopt_checked(c, "CURLOPT_RANGE", cc)) < 0) { + pthread_mutex_lock(&c->mutex); + update_status_locked(c, ret, cc); + pthread_cond_broadcast(&c->cond); + pthread_mutex_unlock(&c->mutex); + return; } c->loop->num_requests++; c->request_received = 0; @@ -1123,6 +1143,7 @@ static int setup_protocols(CurlContext *c) { const char *wl = c->h->protocol_whitelist; const char *bl = c->h->protocol_blacklist; + int ret; if (!wl && !bl) return 0; @@ -1154,13 +1175,27 @@ static int setup_protocols(CurlContext *c) return AVERROR(EINVAL); } - curl_easy_setopt(c->easy, CURLOPT_PROTOCOLS_STR, bp.str); - curl_easy_setopt(c->easy, CURLOPT_REDIR_PROTOCOLS_STR, bp.str); + ret = curl_setopt_checked(c, "CURLOPT_PROTOCOLS_STR", + curl_easy_setopt(c->easy, CURLOPT_PROTOCOLS_STR, bp.str)); + if (!ret) + ret = curl_setopt_checked(c, "CURLOPT_REDIR_PROTOCOLS_STR", + curl_easy_setopt(c->easy, CURLOPT_REDIR_PROTOCOLS_STR, bp.str)); av_bprint_finalize(&bp, NULL); - return 0; + return ret; } -static void setup_curl(CurlContext *c) +/* A failed setopt used to be ignored, so an unsupported option such as + * -http_version 3 on a libcurl built without HTTP/3 was silently dropped and + * the request went out with curl's default version. */ +#define CURL_SETOPT(opt, val) \ + do { \ + int ret_ = curl_setopt_checked(c, #opt, \ + curl_easy_setopt(e, opt, val)); \ + if (ret_ < 0) \ + return ret_; \ + } while (0) + +static int setup_curl(CurlContext *c) { CURL *e = c->easy; const char *url = c->h->filename; @@ -1168,83 +1203,97 @@ static void setup_curl(CurlContext *c) /* Drop an optional "libcurl:" prefix that forces this protocol. */ av_strstart(url, "libcurl:", &url); - curl_easy_setopt(e, CURLOPT_URL, url); - curl_easy_setopt(e, CURLOPT_PRIVATE, c); - curl_easy_setopt(e, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(e, CURLOPT_SHARE, c->loop->share); + CURL_SETOPT(CURLOPT_URL, url); + CURL_SETOPT(CURLOPT_PRIVATE, c); + CURL_SETOPT(CURLOPT_NOSIGNAL, 1L); + CURL_SETOPT(CURLOPT_SHARE, c->loop->share); - curl_easy_setopt(e, CURLOPT_WRITEFUNCTION, write_callback); - curl_easy_setopt(e, CURLOPT_WRITEDATA, c); - curl_easy_setopt(e, CURLOPT_HEADERFUNCTION, header_callback); - curl_easy_setopt(e, CURLOPT_HEADERDATA, c); + CURL_SETOPT(CURLOPT_WRITEFUNCTION, write_callback); + CURL_SETOPT(CURLOPT_WRITEDATA, c); + CURL_SETOPT(CURLOPT_HEADERFUNCTION, header_callback); + CURL_SETOPT(CURLOPT_HEADERDATA, c); - curl_easy_setopt(e, CURLOPT_NOPROGRESS, 0L); - curl_easy_setopt(e, CURLOPT_XFERINFOFUNCTION, xferinfo_callback); - curl_easy_setopt(e, CURLOPT_XFERINFODATA, c); + CURL_SETOPT(CURLOPT_NOPROGRESS, 0L); + CURL_SETOPT(CURLOPT_XFERINFOFUNCTION, xferinfo_callback); + CURL_SETOPT(CURLOPT_XFERINFODATA, c); if (av_log_get_level() >= AV_LOG_DEBUG) { - curl_easy_setopt(e, CURLOPT_VERBOSE, 1L); - curl_easy_setopt(e, CURLOPT_DEBUGFUNCTION, debug_callback); - curl_easy_setopt(e, CURLOPT_DEBUGDATA, c); + CURL_SETOPT(CURLOPT_VERBOSE, 1L); + CURL_SETOPT(CURLOPT_DEBUGFUNCTION, debug_callback); + CURL_SETOPT(CURLOPT_DEBUGDATA, c); } - curl_easy_setopt(e, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(e, CURLOPT_MAXREDIRS, (long)c->max_redirects); - curl_easy_setopt(e, CURLOPT_HTTP_VERSION, (long)c->http_version); - curl_easy_setopt(e, CURLOPT_TCP_KEEPALIVE, c->multiple_requests ? 1L : 0L); - curl_easy_setopt(e, CURLOPT_FORBID_REUSE, c->multiple_requests ? 0L : 1L); - curl_easy_setopt(e, CURLOPT_HSTS_CTRL, (long)CURLHSTS_ENABLE); - curl_easy_setopt(e, CURLOPT_ACCEPT_ENCODING, - c->off > 0 || c->end_off > 0 ? "identity" : ""); + CURL_SETOPT(CURLOPT_FOLLOWLOCATION, 1L); + CURL_SETOPT(CURLOPT_MAXREDIRS, (long)c->max_redirects); + CURL_SETOPT(CURLOPT_HTTP_VERSION, (long)c->http_version); + CURL_SETOPT(CURLOPT_TCP_KEEPALIVE, c->multiple_requests ? 1L : 0L); + CURL_SETOPT(CURLOPT_FORBID_REUSE, c->multiple_requests ? 0L : 1L); + CURL_SETOPT(CURLOPT_HSTS_CTRL, (long)CURLHSTS_ENABLE); + CURL_SETOPT(CURLOPT_ACCEPT_ENCODING, + c->off > 0 || c->end_off > 0 ? "identity" : ""); if (c->connect_timeout > 0) - curl_easy_setopt(e, CURLOPT_CONNECTTIMEOUT_MS, - (long)c->connect_timeout * 1000); + CURL_SETOPT(CURLOPT_CONNECTTIMEOUT_MS, + (long)c->connect_timeout * 1000); if (c->user_agent && c->user_agent[0]) - curl_easy_setopt(e, CURLOPT_USERAGENT, c->user_agent); + CURL_SETOPT(CURLOPT_USERAGENT, c->user_agent); if (c->http_proxy && c->http_proxy[0]) - curl_easy_setopt(e, CURLOPT_PROXY, c->http_proxy); + CURL_SETOPT(CURLOPT_PROXY, c->http_proxy); - curl_easy_setopt(e, CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA); - curl_easy_setopt(e, CURLOPT_SSL_VERIFYPEER, c->tls_verify ? 1L : 0L); - curl_easy_setopt(e, CURLOPT_SSL_VERIFYHOST, c->tls_verify ? 2L : 0L); + CURL_SETOPT(CURLOPT_SSL_OPTIONS, (long)CURLSSLOPT_NATIVE_CA); + CURL_SETOPT(CURLOPT_SSL_VERIFYPEER, c->tls_verify ? 1L : 0L); + CURL_SETOPT(CURLOPT_SSL_VERIFYHOST, c->tls_verify ? 2L : 0L); if (c->ca_file) - curl_easy_setopt(e, CURLOPT_CAINFO, c->ca_file); + CURL_SETOPT(CURLOPT_CAINFO, c->ca_file); if (c->cert_file) - curl_easy_setopt(e, CURLOPT_SSLCERT, c->cert_file); + CURL_SETOPT(CURLOPT_SSLCERT, c->cert_file); if (c->key_file) - curl_easy_setopt(e, CURLOPT_SSLKEY, c->key_file); + CURL_SETOPT(CURLOPT_SSLKEY, c->key_file); - curl_easy_setopt(e, CURLOPT_COOKIEFILE, ""); + CURL_SETOPT(CURLOPT_COOKIEFILE, ""); if (c->cookies && c->cookies[0]) { char *copy = av_strdup(c->cookies); char *line, *saveptr = NULL; - if (copy) { - for (line = av_strtok(copy, "\r\n", &saveptr); line; - line = av_strtok(NULL, "\r\n", &saveptr)) { - char *sc = av_asprintf("Set-Cookie: %s", line); - if (sc) { - curl_easy_setopt(e, CURLOPT_COOKIELIST, sc); - av_free(sc); - } + if (!copy) + return AVERROR(ENOMEM); + for (line = av_strtok(copy, "\r\n", &saveptr); line; + line = av_strtok(NULL, "\r\n", &saveptr)) { + char *sc = av_asprintf("Set-Cookie: %s", line); + CURLcode cc; + int ret; + + if (!sc) { + av_free(copy); + return AVERROR(ENOMEM); + } + cc = curl_easy_setopt(e, CURLOPT_COOKIELIST, sc); + av_free(sc); + if ((ret = curl_setopt_checked(c, "CURLOPT_COOKIELIST", cc)) < 0) { + av_free(copy); + return ret; } - av_free(copy); } + av_free(copy); } c->header_list = build_headers(c); if (c->header_list) - curl_easy_setopt(e, CURLOPT_HTTPHEADER, c->header_list); + CURL_SETOPT(CURLOPT_HTTPHEADER, c->header_list); /* Shoutcast v1 answers "ICY 200 OK", which curl would otherwise reject as * HTTP/0.9 before any header reaches header_callback(). */ if (c->icy_status) { c->alias_list = curl_slist_append(NULL, "ICY 200"); - if (c->alias_list) - curl_easy_setopt(e, CURLOPT_HTTP200ALIASES, c->alias_list); + if (!c->alias_list) + return AVERROR(ENOMEM); + CURL_SETOPT(CURLOPT_HTTP200ALIASES, c->alias_list); } + + return 0; } +#undef CURL_SETOPT + static void curl_cond_wait(CurlContext *c) { int64_t t = av_gettime() + CURL_WAIT_US; @@ -1385,7 +1434,9 @@ static int libcurl_open(URLContext *h, const char *url, int flags, if (ret < 0) goto fail; - setup_curl(c); + ret = setup_curl(c); + if (ret < 0) + goto fail; ret = curl_dispatch(c->loop, CMD_ADD, c, 0, 0); if (ret < 0) -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
