This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit e48a073d7e4a7053e6f89d64e3dbb11fef41f0b0
Author:     Romain Beauxis <[email protected]>
AuthorDate: Sun Aug 23 21:19:07 2026 -0500
Commit:     Romain Beauxis <[email protected]>
CommitDate: Thu Aug 27 13:43:01 2026 +0000

    avformat/http: reject malformed response status lines
    
    A status line carrying no HTTP version parsed as status code 0, which
    check_http_code() then accepted as a success: the body of an error reply, 
or of
    a reply from something that is not an HTTP server at all, was handed to the
    demuxer. strtol() was equally lenient about the code itself, accepting a 
sign,
    a count of digits other than three, and a value outside the range HTTP 
defines.
    
    Parse the grammar RFC 9112 actually specifies: a case sensitive "HTTP/"
    followed by DIGIT "." DIGIT, then exactly three digits on a whitespace
    boundary, and reject a code outside the 100-599 range of RFC 9110 section 
15.
    
    A status line with no reason phrase at all, "HTTP/1.1 200", stays accepted; 
the
    separator RFC 9112 requires there is missing often enough in the wild.
    
    reason now points at the phrase itself rather than at the separator before 
it,
    so check_http_code() no longer has to skip that separator on its own.
    
    Worth noting: this invalidates several class of previously accepted
    invalid http responses, include the ICY responses class from legacy
    shoutcast servers.
    
    ICY responses are planned on being re-introduced as opt-in via the new
    libcurl HTTP stack.
---
 libavformat/http.c              | 39 ++++++++++++++++++++++++++------------
 tests/ref/fate/http-status-line | 42 ++++++++++++++++++++---------------------
 2 files changed, 48 insertions(+), 33 deletions(-)

diff --git a/libavformat/http.c b/libavformat/http.c
index 50fd88970d..fff0f25e36 100644
--- a/libavformat/http.c
+++ b/libavformat/http.c
@@ -892,26 +892,42 @@ static int http_get_line(HTTPContext *s, char *line, int 
line_size)
 
 int ff_http_parse_status_line(void *logctx, const char *line, HTTPStatusLine 
*st)
 {
-    const char *p = line;
-    char *end;
+    const char *p;
 
     memset(st, 0, sizeof(*st));
 
-    if (av_strncasecmp(p, "HTTP/1.0", 8) == 0)
-        st->willclose = 1;
+    if (!av_strstart(line, "HTTP/", &p) ||
+        !av_isdigit(p[0]) || p[1] != '.' || !av_isdigit(p[2]) ||
+        !av_isspace(p[3])) {
+        av_log(logctx, AV_LOG_ERROR, "Malformed HTTP status line.\n");
+        return AVERROR_INVALIDDATA;
+    }
 
-    while (*p != '/' && *p != '\0')
-        p++;
-    while (*p == '/')
-        p++;
     av_strlcpy(st->version, p, sizeof(st->version));
+    st->willclose = !strcmp(st->version, "1.0");
 
-    while (!av_isspace(*p) && *p != '\0')
+    p += 3;
+    while (av_isspace(*p))
         p++;
+
+    /* RFC 9112 mandates a space after the code, but a bare "HTTP/1.1 200"
+     * is common enough in the wild to be worth accepting. */
+    if (!av_isdigit(p[0]) || !av_isdigit(p[1]) || !av_isdigit(p[2]) ||
+        (p[3] && !av_isspace(p[3]))) {
+        av_log(logctx, AV_LOG_ERROR, "Malformed HTTP status code.\n");
+        return AVERROR_INVALIDDATA;
+    }
+
+    st->code = 100 * (p[0] - '0') + 10 * (p[1] - '0') + p[2] - '0';
+    if (st->code < 100 || st->code > 599) {
+        av_log(logctx, AV_LOG_ERROR, "HTTP status code %d out of range.\n",
+               st->code);
+        return AVERROR_INVALIDDATA;
+    }
+    p += 3;
     while (av_isspace(*p))
         p++;
-    st->code   = strtol(p, &end, 10);
-    st->reason = end;
+    st->reason = p;
 
     av_log(logctx, AV_LOG_TRACE, "http_code=%d\n", st->code);
 
@@ -926,7 +942,6 @@ static int check_http_code(URLContext *h, int http_code, 
const char *end)
     if (http_code >= 400 && http_code < 600 &&
         (http_code != 401 || s->auth_state.auth_type != HTTP_AUTH_NONE) &&
         (http_code != 407 || s->proxy_auth_state.auth_type != HTTP_AUTH_NONE)) 
{
-        end += strspn(end, SPACE_CHARS);
         av_log(h, AV_LOG_WARNING, "HTTP error %d %s\n", http_code, end);
         return ff_http_averror(http_code, AVERROR(EIO));
     }
diff --git a/tests/ref/fate/http-status-line b/tests/ref/fate/http-status-line
index 503ed9a59a..ae75ad9377 100644
--- a/tests/ref/fate/http-status-line
+++ b/tests/ref/fate/http-status-line
@@ -1,44 +1,44 @@
 "HTTP/1.1 200 OK"
-  version="1.1" code=200 willclose=0 reason=" OK"
+  version="1.1" code=200 willclose=0 reason="OK"
 "HTTP/1.0 200 OK"
-  version="1.0" code=200 willclose=1 reason=" OK"
+  version="1.0" code=200 willclose=1 reason="OK"
 "HTTP/1.1 404 Not Found"
-  version="1.1" code=404 willclose=0 reason=" Not Found"
+  version="1.1" code=404 willclose=0 reason="Not Found"
 "HTTP/1.1 500 Internal Server Error"
-  version="1.1" code=500 willclose=0 reason=" Internal Server Error"
+  version="1.1" code=500 willclose=0 reason="Internal Server Error"
 "HTTP/1.1 204 No Content"
-  version="1.1" code=204 willclose=0 reason=" No Content"
+  version="1.1" code=204 willclose=0 reason="No Content"
 "HTTP/1.1 200"
   version="1.1" code=200 willclose=0 reason=""
 "HTTP/1.1"
-  version="1.1" code=0 willclose=0 reason=""
+  rejected
 "HTTP/1.1 OK"
-  version="1.1" code=0 willclose=0 reason="OK"
+  rejected
 "HTTP"
-  version="" code=0 willclose=0 reason=""
+  rejected
 "200 OK"
-  version="" code=0 willclose=0 reason=""
+  rejected
 "NOT A STATUS LINE"
-  version="" code=0 willclose=0 reason=""
+  rejected
 ""
-  version="" code=0 willclose=0 reason=""
+  rejected
 "ICY 200 OK"
-  version="" code=0 willclose=0 reason=""
+  rejected
 "ICY 404 Not Found"
-  version="" code=0 willclose=0 reason=""
+  rejected
 "HTTP/1.1 200OK"
-  version="1.1" code=200 willclose=0 reason="OK"
+  rejected
 "HTTP/1.1 +200 Plus"
-  version="1.1" code=200 willclose=0 reason=" Plus"
+  rejected
 "HTTP/1.1 20 Short"
-  version="1.1" code=20 willclose=0 reason=" Short"
+  rejected
 "HTTP/1.1 2000 Four"
-  version="1.1" code=2000 willclose=0 reason=" Four"
+  rejected
 "HTTP/x 200 Bad version"
-  version="x 2" code=200 willclose=0 reason=" Bad version"
+  rejected
 "http/1.1 200 lowercase"
-  version="1.1" code=200 willclose=0 reason=" lowercase"
+  rejected
 "HTTP/1.1 099 Too low"
-  version="1.1" code=99 willclose=0 reason=" Too low"
+  rejected
 "HTTP/1.1 600 Too high"
-  version="1.1" code=600 willclose=0 reason=" Too high"
+  rejected

-- 
To stop receiving notification emails like this one, please contact
[email protected].
_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to