AI: HTTP-FLV: Fix heap-use-after-free crash during stream unmount. v6.0.171 v7.0.50 (#4432)
## Summary Fixes a critical heap-use-after-free crash in HTTP-FLV streaming that occurs when a client requests a stream while it's being unmounted asynchronously. ## Problem - **Issue**: #4429 - Heap-use-after-free crash in `SrsLiveStream::serve_http()` - **Root Cause**: Race condition between coroutines in single-threaded SRS server: 1. **Coroutine A**: HTTP client requests FLV stream → `serve_http()` starts 2. **Coroutine B**: RTMP publisher disconnects → triggers async stream destruction 3. **Async Worker**: Destroys `SrsLiveStream` object while Coroutine A is yielded 4. **Coroutine A**: Resumes and accesses freed memory → **CRASH** ## Solution 1. **Early viewer registration**: Add HTTP connection to `viewers_` list immediately in `serve_http()` before any I/O operations that could yield 2. **Lifecycle protection**: Split `serve_http()` into wrapper and implementation to ensure proper viewer management 3. **Stream availability checks**: Add fast checks for stream disposal state before critical operations 4. **Improved error handling**: Convert warnings to fatal errors when trying to free alive streams ## Key Changes - **`SrsLiveStream::serve_http()`**: Now immediately registers viewer and delegates to `serve_http_impl()` - **`SrsLiveStream::serve_http_impl()`**: Contains the actual HTTP serving logic - **`SrsHttpStreamDestroy::call()`**: Enhanced error handling and longer wait timeout - **Stream state validation**: Added checks for `entry->enabled` before proceeding with stream operations Fixes #4429
This commit is contained in:
parent
c921c5a52f
commit
c762e8204a
|
|
@ -7,6 +7,7 @@ The changelog for SRS.
|
||||||
<a name="v7-changes"></a>
|
<a name="v7-changes"></a>
|
||||||
|
|
||||||
## SRS 7.0 Changelog
|
## SRS 7.0 Changelog
|
||||||
|
* v7.0, 2025-08-11, Merge [#4432](https://github.com/ossrs/srs/pull/4432): AI: HTTP-FLV: Fix heap-use-after-free crash during stream unmount. v7.0.50 (#4432)
|
||||||
* v7.0, 2025-07-28, Merge [#4245](https://github.com/ossrs/srs/pull/4245): Allow Forward to be configured with Env Var. v7.0.49 (#4245)
|
* v7.0, 2025-07-28, Merge [#4245](https://github.com/ossrs/srs/pull/4245): Allow Forward to be configured with Env Var. v7.0.49 (#4245)
|
||||||
* v7.0, 2025-07-16, Merge [#4295](https://github.com/ossrs/srs/pull/4295): RTC: audio packet jitter buffer. v7.0.48 (#4295)
|
* v7.0, 2025-07-16, Merge [#4295](https://github.com/ossrs/srs/pull/4295): RTC: audio packet jitter buffer. v7.0.48 (#4295)
|
||||||
* v7.0, 2025-07-11, Merge [#4333](https://github.com/ossrs/srs/pull/4333): NEW PROTOCOL: Support viewing stream over RTSP. v7.0.47 (#4333)
|
* v7.0, 2025-07-11, Merge [#4333](https://github.com/ossrs/srs/pull/4333): NEW PROTOCOL: Support viewing stream over RTSP. v7.0.47 (#4333)
|
||||||
|
|
@ -60,6 +61,7 @@ The changelog for SRS.
|
||||||
<a name="v6-changes"></a>
|
<a name="v6-changes"></a>
|
||||||
|
|
||||||
## SRS 6.0 Changelog
|
## SRS 6.0 Changelog
|
||||||
|
* v6.0, 2025-08-11, Merge [#4432](https://github.com/ossrs/srs/pull/4432): AI: HTTP-FLV: Fix heap-use-after-free crash during stream unmount. v6.0.171 (#4432)
|
||||||
* v6.0, 2025-07-28, Merge [#4245](https://github.com/ossrs/srs/pull/4245): Allow Forward to be configured with Env Var. v6.0.170 (#4245)
|
* v6.0, 2025-07-28, Merge [#4245](https://github.com/ossrs/srs/pull/4245): Allow Forward to be configured with Env Var. v6.0.170 (#4245)
|
||||||
* v6.0, 2025-07-10, Merge [#4414](https://github.com/ossrs/srs/pull/4414): Fix H.264 B-frame detection logic to comply with specification. v6.0.169 (#4414)
|
* v6.0, 2025-07-10, Merge [#4414](https://github.com/ossrs/srs/pull/4414): Fix H.264 B-frame detection logic to comply with specification. v6.0.169 (#4414)
|
||||||
* v6.0, 2025-06-04, Merge [#4325](https://github.com/ossrs/srs/pull/4325): fix bug: loop transcoding #3516. v6.0.168 (#4325)
|
* v6.0, 2025-06-04, Merge [#4325](https://github.com/ossrs/srs/pull/4325): fix bug: loop transcoding #3516. v6.0.168 (#4325)
|
||||||
|
|
|
||||||
|
|
@ -619,6 +619,26 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage
|
||||||
{
|
{
|
||||||
srs_error_t err = srs_success;
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
|
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
|
SrsHttpConn* hc = dynamic_cast<SrsHttpConn*>(hr->connection());
|
||||||
|
|
||||||
|
// Add the viewer to the viewers list.
|
||||||
|
viewers_.push_back(hc);
|
||||||
|
|
||||||
|
err = serve_http_impl(w, r);
|
||||||
|
|
||||||
|
// Remove viewer from the viewers list.
|
||||||
|
vector<ISrsExpire*>::iterator it = std::find(viewers_.begin(), viewers_.end(), hc);
|
||||||
|
srs_assert (it != viewers_.end());
|
||||||
|
viewers_.erase(it);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
srs_error_t SrsLiveStream::serve_http_impl(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
|
||||||
|
{
|
||||||
|
srs_error_t err = srs_success;
|
||||||
|
|
||||||
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r);
|
||||||
SrsHttpConn* hc = dynamic_cast<SrsHttpConn*>(hr->connection());
|
SrsHttpConn* hc = dynamic_cast<SrsHttpConn*>(hr->connection());
|
||||||
SrsHttpxConn* hxc = dynamic_cast<SrsHttpxConn*>(hc->handler());
|
SrsHttpxConn* hxc = dynamic_cast<SrsHttpxConn*>(hc->handler());
|
||||||
|
|
@ -651,6 +671,11 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage
|
||||||
return srs_error_wrap(err, "http hook");
|
return srs_error_wrap(err, "http hook");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fast check whether stream is still available.
|
||||||
|
if (!entry->enabled) {
|
||||||
|
return srs_error_new(ERROR_RTMP_STREAM_NOT_FOUND, "stream not found");
|
||||||
|
}
|
||||||
|
|
||||||
// Always try to create the source, because http handler won't create it.
|
// Always try to create the source, because http handler won't create it.
|
||||||
SrsSharedPtr<SrsLiveSource> live_source;
|
SrsSharedPtr<SrsLiveSource> live_source;
|
||||||
if ((err = _srs_sources->fetch_or_create(req, server_, live_source)) != srs_success) {
|
if ((err = _srs_sources->fetch_or_create(req, server_, live_source)) != srs_success) {
|
||||||
|
|
@ -673,17 +698,14 @@ srs_error_t SrsLiveStream::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage
|
||||||
// object is not alive.
|
// object is not alive.
|
||||||
SrsUniquePtr<SrsLiveConsumer> consumer(consumer_raw);
|
SrsUniquePtr<SrsLiveConsumer> consumer(consumer_raw);
|
||||||
|
|
||||||
// Add the viewer to the viewers list.
|
// Fast check whether stream is still available.
|
||||||
viewers_.push_back(hc);
|
if (!entry->enabled) {
|
||||||
|
return srs_error_new(ERROR_RTMP_STREAM_NOT_FOUND, "stream not found");
|
||||||
|
}
|
||||||
|
|
||||||
// Serve the viewer connection.
|
// Serve the viewer connection.
|
||||||
err = do_serve_http(live_source.get(), consumer.get(), w, r);
|
err = do_serve_http(live_source.get(), consumer.get(), w, r);
|
||||||
|
|
||||||
// Remove viewer from the viewers list.
|
|
||||||
vector<ISrsExpire*>::iterator it = std::find(viewers_.begin(), viewers_.end(), hc);
|
|
||||||
srs_assert (it != viewers_.end());
|
|
||||||
viewers_.erase(it);
|
|
||||||
|
|
||||||
// Do hook after serving.
|
// Do hook after serving.
|
||||||
http_hooks_on_stop(r);
|
http_hooks_on_stop(r);
|
||||||
|
|
||||||
|
|
@ -1321,7 +1343,7 @@ srs_error_t SrsHttpStreamDestroy::call()
|
||||||
|
|
||||||
// Wait for cache and stream to stop.
|
// Wait for cache and stream to stop.
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (; i < 1024; i++) {
|
for (; i < 3000; i++) {
|
||||||
if (!cache->alive() && !stream->alive()) {
|
if (!cache->alive() && !stream->alive()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1329,7 +1351,9 @@ srs_error_t SrsHttpStreamDestroy::call()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cache->alive() || stream->alive()) {
|
if (cache->alive() || stream->alive()) {
|
||||||
srs_warn("http: try to free a alive stream, cache=%d, stream=%d", cache->alive(), stream->alive());
|
srs_error("http: try to free an alive stream, sid=%s, cache=%d, stream=%d",
|
||||||
|
sid_.c_str(), cache->alive(), stream->alive());
|
||||||
|
srs_assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove the entry from handlers.
|
// Remove the entry from handlers.
|
||||||
|
|
|
||||||
|
|
@ -198,6 +198,9 @@ public:
|
||||||
virtual srs_error_t update_auth(SrsRequest* r);
|
virtual srs_error_t update_auth(SrsRequest* r);
|
||||||
public:
|
public:
|
||||||
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
|
private:
|
||||||
|
srs_error_t serve_http_impl(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
|
||||||
|
public:
|
||||||
virtual bool alive();
|
virtual bool alive();
|
||||||
// Interface ISrsExpire
|
// Interface ISrsExpire
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
#define VERSION_MAJOR 6
|
#define VERSION_MAJOR 6
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 170
|
#define VERSION_REVISION 171
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@
|
||||||
|
|
||||||
#define VERSION_MAJOR 7
|
#define VERSION_MAJOR 7
|
||||||
#define VERSION_MINOR 0
|
#define VERSION_MINOR 0
|
||||||
#define VERSION_REVISION 49
|
#define VERSION_REVISION 50
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
Loading…
Reference in New Issue
Block a user