This is an automated email from the ASF dual-hosted git repository.

chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git


The following commit(s) were added to refs/heads/master by this push:
     new ca370c28 Fix RTMP abort message deleting the chunk stream being parsed 
(#3452)
ca370c28 is described below

commit ca370c28a905a387e482ee6deae446ac82a6464a
Author: Weibing Wang <[email protected]>
AuthorDate: Sun Aug 16 01:08:30 2026 +0800

    Fix RTMP abort message deleting the chunk stream being parsed (#3452)
    
    * Fix RTMP abort message deleting the chunk stream being parsed
    
    * Address review comments on RTMP abort regression test
---
 src/brpc/policy/rtmp_protocol.cpp |  7 ++++-
 test/brpc_rtmp_unittest.cpp       | 59 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/src/brpc/policy/rtmp_protocol.cpp 
b/src/brpc/policy/rtmp_protocol.cpp
index 62322018..c5ece81a 100644
--- a/src/brpc/policy/rtmp_protocol.cpp
+++ b/src/brpc/policy/rtmp_protocol.cpp
@@ -1869,7 +1869,12 @@ bool RtmpChunkStream::OnAbortMessage(
         RTMP_ERROR(socket, mh) << "Invalid chunk_stream_id=" << cs_id;
         return false;
     }
-    connection_context()->ClearChunkStream(cs_id);
+    // Do not delete the chunk stream that is currently being parsed (i.e.
+    // the one running this Feed). Clearing it here would free `this' while
+    // Feed() still holds and later touches it, causing a use-after-free.
+    if (cs_id != _cs_id) {
+        connection_context()->ClearChunkStream(cs_id);
+    }
     return true;
 }
 
diff --git a/test/brpc_rtmp_unittest.cpp b/test/brpc_rtmp_unittest.cpp
index 61a63744..286f8a87 100644
--- a/test/brpc_rtmp_unittest.cpp
+++ b/test/brpc_rtmp_unittest.cpp
@@ -28,6 +28,8 @@
 #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
 #include "butil/time.h"
 #include "butil/macros.h"
+#include "butil/fd_guard.h"
+#include "brpc/policy/rtmp_protocol.h"
 #include "brpc/socket.h"
 #include "brpc/acceptor.h"
 #include "brpc/server.h"
@@ -805,6 +807,63 @@ TEST(RtmpTest, flv_reader_rejects_zero_datasize_audio_tag) 
{
     ASSERT_EQ(before, buf.size());
 }
 
+// A crafted Abort message that names the chunk stream currently being parsed
+// (itself) used to make ClearChunkStream delete the RtmpChunkStream while its
+// Feed() is still running, which caused a heap-use-after-free right after
+// OnMessage() returned.
+TEST(RtmpTest, abort_message_naming_own_chunk_stream) {
+    int pipe_fds[2];
+    ASSERT_EQ(0, pipe(pipe_fds));
+    butil::fd_guard guard0(pipe_fds[0]);   // read end, closed by this guard
+    butil::fd_guard guard1(pipe_fds[1]);   // write end, handed over to Socket
+
+    brpc::SocketId id;
+    brpc::SocketOptions options;
+    options.fd = guard1.release();         // Socket takes ownership of the fd
+    ASSERT_EQ(0, brpc::Socket::Create(options, &id));
+    brpc::SocketUniquePtr sock;
+    ASSERT_EQ(0, brpc::Socket::Address(id, &sock));
+
+    brpc::policy::RtmpContext ctx(NULL, NULL);
+    ctx.SetState(sock->remote_side(),
+                 brpc::policy::RtmpContext::STATE_RECEIVED_C2);
+
+    // fmt0 chunk on chunk stream 2 carrying an Abort message (type 2) whose
+    // payload is the same chunk stream id (2).
+    std::string chunk;
+    chunk.push_back((char)0x02);   // basic header: fmt=0, cs_id=2
+    chunk.append(3, '\0');         // timestamp = 0
+    chunk.push_back('\0');         // message_length (3 bytes) = 4
+    chunk.push_back('\0');
+    chunk.push_back((char)0x04);
+    chunk.push_back((char)0x02);   // message_type = Abort
+    chunk.append(4, '\0');         // stream_id = 0 (little endian)
+    chunk.push_back('\0');         // payload: cs_id = 2 (big endian)
+    chunk.push_back('\0');
+    chunk.push_back('\0');
+    chunk.push_back((char)0x02);
+
+    butil::IOBuf buf;
+    buf.append(chunk);
+    ASSERT_EQ(brpc::PARSE_OK, ctx.Feed(&buf, sock.get()).error());
+
+    // A following type-1 chunk inherits the message header from the previous
+    // message on the same stream. If the abort had wrongly deleted the chunk
+    // stream, the freshly recreated stream would have no last header and this
+    // chunk would be rejected; instead it must be parsed successfully.
+    std::string cont;
+    cont.push_back((char)0x42);   // basic header: fmt=1, cs_id=2
+    cont.append(3, '\0');         // timestamp delta = 0
+    cont.push_back('\0');         // message_length (3 bytes) = 4
+    cont.push_back('\0');
+    cont.push_back((char)0x04);
+    cont.push_back((char)0x03);   // message_type = Ack
+    cont.append(4, '\0');         // payload: bytes_received = 0
+    butil::IOBuf buf2;
+    buf2.append(cont);
+    ASSERT_EQ(brpc::PARSE_OK, ctx.Feed(&buf2, sock.get()).error());
+}
+
 TEST(RtmpTest, successfully_play_streams) {
     PlayingDummyService rtmp_service;
     brpc::Server server;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to