PR #25761 opened by Kilian (lusoris)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25761
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25761.patch

Keep the original error in three RTSP paths that replaced it with a fixed code. 
These are the pre-existing error-identity issues named in the review of pull 
request 25726. This branch is stacked on pull request 25726 (head 
edd7328845e3); only the last three commits are new here.

- `rtsp_listen()` replaced every non-zero handler result with 
`AVERROR_INVALIDDATA`. Negative errors are now returned unchanged; a positive 
result keeps the existing mapping.
- `check_sessionid()` returned `AVERROR_STREAM_NOT_FOUND` after a failed 
error-reply write. It now returns the write error.
- The muxer poll loop in `rtsp_write_packet()` replaced any 
`ff_rtsp_read_reply()` failure with `AVERROR(EPIPE)`. It now returns the 
reported error.

Measured with an `LD_PRELOAD` and socketpair fault probe that fails the second 
write with EPIPE:

| Path | Before | After |
| --- | --- | --- |
| listen dispatch | -1094995529 (`AVERROR_INVALIDDATA`) | -32 
(`AVERROR(EPIPE)`) |
| `check_sessionid` | -1381258232 (`AVERROR_STREAM_NOT_FOUND`) | -32 
(`AVERROR(EPIPE)`) |
| muxer poll loop | -32 (substituted EPIPE) | -541478725 (`AVERROR_EOF`, the 
reported error) |

`make fate-rtsp fate-source` passes. tools/patcheck reports nothing on the 
added lines.

The PLAY and PAUSE status fallback from the same review is in its own pull 
request, #25760.



>From 9fa9b661dfca684d621959362b57ab8fbd72c36e Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Tue, 3 Nov 2026 16:23:55 +0100
Subject: [PATCH 1/6] avformat/rtspenc: check the interleaved frame write

ff_rtsp_tcp_write_packet() ignored the result of writing each
interleaved frame to the RTSP control connection, so a failed or short
write was reported as success and the muxer kept writing. Stop at the
first failed write and return its error, or AVERROR(EIO) for a short
write.

Signed-off-by: lusoris <[email protected]>
---
 libavformat/rtspenc.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c
index f2f4b6d5258..724b5c6b8b4 100644
--- a/libavformat/rtspenc.c
+++ b/libavformat/rtspenc.c
@@ -145,7 +145,7 @@ int ff_rtsp_tcp_write_packet(AVFormatContext *s, RTSPStream 
*rtsp_st)
     RTSPState *rt = s->priv_data;
     AVFormatContext *rtpctx = rtsp_st->transport_priv;
     uint8_t *buf, *ptr;
-    int size;
+    int size, ret = 0;
     uint8_t *interleave_header, *interleaved_packet;
 
     size = avio_close_dyn_buf(rtpctx->pb, &buf);
@@ -171,12 +171,17 @@ int ff_rtsp_tcp_write_packet(AVFormatContext *s, 
RTSPStream *rtsp_st)
         interleave_header[0] = '$';
         interleave_header[1] = id;
         AV_WB16(interleave_header + 2, packet_len);
-        ffurl_write(rt->rtsp_hd_out, interleaved_packet, 4 + packet_len);
+        ret = ffurl_write(rt->rtsp_hd_out, interleaved_packet, 4 + packet_len);
+        if (ret != 4 + packet_len) {
+            ret = ret < 0 ? ret : AVERROR(EIO);
+            break;
+        }
         ptr += packet_len;
         size -= packet_len;
     }
     av_free(buf);
-    return ffio_open_dyn_packet_buf(&rtpctx->pb, rt->pkt_size);
+    size = ffio_open_dyn_packet_buf(&rtpctx->pb, rt->pkt_size);
+    return ret < 0 ? ret : size;
 }
 
 static int rtsp_write_packet(AVFormatContext *s, AVPacket *pkt)
-- 
2.54.0


>From 35c136d46b71a3ccdde762f416e6674bb922ca9f Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Tue, 3 Nov 2026 16:23:56 +0100
Subject: [PATCH 2/6] avformat/rtsp: check the write of the automatic reply

ff_rtsp_read_reply() answers server requests (OPTIONS, GET_PARAMETER
and others) on its own but ignored whether the reply was written.
Return the write error, or AVERROR(EIO) for a short write, and update
last_cmd_time only after a complete write.

Signed-off-by: lusoris <[email protected]>
---
 libavformat/rtsp.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/libavformat/rtsp.c b/libavformat/rtsp.c
index 29f219c366a..186be3def9b 100644
--- a/libavformat/rtsp.c
+++ b/libavformat/rtsp.c
@@ -1358,14 +1358,16 @@ start:
             av_base64_encode(base64buf, sizeof(base64buf), buf, strlen(buf));
             ptr = base64buf;
         }
-        ffurl_write(rt->rtsp_hd_out, ptr, strlen(ptr));
+        ret = ffurl_write(rt->rtsp_hd_out, ptr, strlen(ptr));
 
-        rt->last_cmd_time = av_gettime_relative();
         /* Even if the request from the server had data, it is not the data
          * that the caller wants or expects. The memory could also be leaked
          * if the actual following reply has content data. */
         if (content_ptr)
             av_freep(content_ptr);
+        if (ret != strlen(ptr))
+            return ret < 0 ? ret : AVERROR(EIO);
+        rt->last_cmd_time = av_gettime_relative();
         /* If method is set, this is called from ff_rtsp_send_cmd,
          * where a reply to exactly this request is awaited. For
          * callers from within packet receiving, we just want to
-- 
2.54.0


>From edd7328845e3c91ee9373f5d0f9a19668e7a7229 Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Tue, 3 Nov 2026 16:23:58 +0100
Subject: [PATCH 3/6] avformat/rtspdec: check the listen-mode reply write

rtsp_send_reply() always returned 0, so in listen mode a reply that
could not be written to the client went unnoticed and the server state
advanced anyway. Return the write error, or AVERROR(EIO) for a short
write, and propagate it from the ANNOUNCE, OPTIONS, SETUP and RECORD
handlers before the state changes.

Signed-off-by: lusoris <[email protected]>
---
 libavformat/rtspdec.c | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c
index 0e592eebaed..e6b9b0c7382 100644
--- a/libavformat/rtspdec.c
+++ b/libavformat/rtspdec.c
@@ -118,7 +118,7 @@ static int rtsp_send_reply(AVFormatContext *s, enum 
RTSPStatusCode code,
 {
     RTSPState *rt = s->priv_data;
     char message[MAX_URL_SIZE];
-    int index = 0;
+    int index = 0, ret;
     while (status_messages[index].code) {
         if (status_messages[index].code == code) {
             snprintf(message, sizeof(message), "RTSP/1.0 %d %s\r\n",
@@ -135,7 +135,9 @@ static int rtsp_send_reply(AVFormatContext *s, enum 
RTSPStatusCode code,
         av_strlcat(message, extracontent, sizeof(message));
     av_strlcat(message, "\r\n", sizeof(message));
     av_log(s, AV_LOG_TRACE, "Sending response:\n%s", message);
-    ffurl_write(rt->rtsp_hd_out, message, strlen(message));
+    ret = ffurl_write(rt->rtsp_hd_out, message, strlen(message));
+    if (ret != strlen(message))
+        return ret < 0 ? ret : AVERROR(EIO);
 
     return 0;
 }
@@ -225,8 +227,7 @@ static int rtsp_read_announce(AVFormatContext *s)
         av_free(sdp);
         if (ret)
             return ret;
-        rtsp_send_reply(s, RTSP_STATUS_OK, NULL, request.seq);
-        return 0;
+        return rtsp_send_reply(s, RTSP_STATUS_OK, NULL, request.seq);
     }
     av_log(s, AV_LOG_ERROR,
            "Invalid ANNOUNCE Content-Length %d\n", request.content_length);
@@ -247,10 +248,9 @@ static int rtsp_read_options(AVFormatContext *s)
         return ret;
     rt->seq++;
     /* Send Reply */
-    rtsp_send_reply(s, RTSP_STATUS_OK,
-                    "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, RECORD\r\n",
-                    request.seq);
-    return 0;
+    return rtsp_send_reply(s, RTSP_STATUS_OK,
+                           "Public: ANNOUNCE, PAUSE, SETUP, TEARDOWN, 
RECORD\r\n",
+                           request.seq);
 }
 
 static int rtsp_read_setup(AVFormatContext *s, char* host, char *controlurl)
@@ -366,7 +366,9 @@ static int rtsp_read_setup(AVFormatContext *s, char* host, 
char *controlurl)
     av_strlcatf(responseheaders, sizeof(responseheaders), "Session: %s\r\n",
                 rt->session_id);
     /* Send Reply */
-    rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq);
+    ret = rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq);
+    if (ret < 0)
+        return ret;
 
     rt->state = RTSP_STATE_PAUSED;
     return 0;
@@ -388,7 +390,9 @@ static int rtsp_read_record(AVFormatContext *s)
     rt->seq++;
     snprintf(responseheaders, sizeof(responseheaders), "Session: %s\r\n",
              rt->session_id);
-    rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq);
+    ret = rtsp_send_reply(s, RTSP_STATUS_OK, responseheaders, request.seq);
+    if (ret < 0)
+        return ret;
 
     rt->state = RTSP_STATE_STREAMING;
     return 0;
-- 
2.54.0


>From 7cf2fb7ae1a6366a76789bb865dbfbe9a94f0b67 Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Wed, 4 Nov 2026 00:06:11 +0100
Subject: [PATCH 4/6] avformat/rtspdec: keep the handler error in rtsp_listen

rtsp_listen() replaced every non-zero result of the OPTIONS, ANNOUNCE,
RECORD and SETUP handlers with AVERROR_INVALIDDATA, so a failed reply
write surfaced as invalid data instead of EPIPE or EIO. Return negative
handler errors unchanged and keep the mapping for positive results.

Signed-off-by: lusoris <[email protected]>
---
 libavformat/rtspdec.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c
index e6b9b0c7382..69224d3596c 100644
--- a/libavformat/rtspdec.c
+++ b/libavformat/rtspdec.c
@@ -843,7 +843,8 @@ static int rtsp_listen(AVFormatContext *s)
         } else if (methodcode == SETUP)
             ret = rtsp_read_setup(s, host, uri);
         if (ret) {
-            ret = AVERROR_INVALIDDATA;
+            if (ret > 0)
+                ret = AVERROR_INVALIDDATA;
             goto fail;
         }
     }
-- 
2.54.0


>From 90782a5ef2c41be6d9c14da3805f78f95654697b Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Wed, 4 Nov 2026 00:06:19 +0100
Subject: [PATCH 5/6] avformat/rtspdec: return the reply write error in
 check_sessionid

When the session id did not match, check_sessionid() sent the error
reply and returned AVERROR_STREAM_NOT_FOUND even if that write failed.
Return the write error instead, so a closed control connection is not
reported as an unknown session.

Signed-off-by: lusoris <[email protected]>
---
 libavformat/rtspdec.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/libavformat/rtspdec.c b/libavformat/rtspdec.c
index 69224d3596c..40dc80e73ce 100644
--- a/libavformat/rtspdec.c
+++ b/libavformat/rtspdec.c
@@ -152,9 +152,12 @@ static inline int check_sessionid(AVFormatContext *s,
         return 0;
     }
     if (strcmp(session_id, request->session_id)) {
+        int ret;
         av_log(s, AV_LOG_ERROR, "Unexpected session-id %s\n",
                request->session_id);
-        rtsp_send_reply(s, RTSP_STATUS_SESSION, NULL, request->seq);
+        ret = rtsp_send_reply(s, RTSP_STATUS_SESSION, NULL, request->seq);
+        if (ret < 0)
+            return ret;
         return AVERROR_STREAM_NOT_FOUND;
     }
     return 0;
-- 
2.54.0


>From 9d99bd37fc8d39d8126ffcc1618ea9dd1ec964b7 Mon Sep 17 00:00:00 2001
From: lusoris <[email protected]>
Date: Wed, 4 Nov 2026 00:06:22 +0100
Subject: [PATCH 6/6] avformat/rtspenc: keep the read error in the muxer poll
 loop

rtsp_write_packet() polls the control connection for server requests
between packets and replaced any failure of ff_rtsp_read_reply() with
AVERROR(EPIPE). Return the error it reported, such as AVERROR_EOF when
the server closed the connection.

Signed-off-by: lusoris <[email protected]>
---
 libavformat/rtspenc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavformat/rtspenc.c b/libavformat/rtspenc.c
index 724b5c6b8b4..0667f9c42b7 100644
--- a/libavformat/rtspenc.c
+++ b/libavformat/rtspenc.c
@@ -206,7 +206,7 @@ static int rtsp_write_packet(AVFormatContext *s, AVPacket 
*pkt)
              * interleaved packets internally. */
             ret = ff_rtsp_read_reply(s, &reply, NULL, 1, NULL);
             if (ret < 0)
-                return AVERROR(EPIPE);
+                return ret;
             if (ret == 1) {
                 ret = ff_rtsp_skip_packet(s);
                 if (ret < 0)
-- 
2.54.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to