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]

Reply via email to