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

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


The following commit(s) were added to refs/heads/master by this push:
     new a86882fa29c [fix](http) Fix EvHttpServer crash during graceful 
shutdown in ASAN mode (#59769)
a86882fa29c is described below

commit a86882fa29caf859af3c7c2ac3dd05718586966f
Author: Xin Liao <[email protected]>
AuthorDate: Wed Jan 14 09:58:03 2026 +0800

    [fix](http) Fix EvHttpServer crash during graceful shutdown in ASAN mode 
(#59769)
    
    The crash occurred because evhttp was created as a local shared_ptr in
    the lambda function, and its destruction happened after
    event_base_dispatch returned. This caused use-after-free issues when
    ASAN/UBSAN tried to verify the object dynamic type during shared_ptr
    destruction.
    
    Fix by:
    1. Moving evhttp objects to class member _evhttp_servers for explicit
    lifecycle management
    2. Reordering resource cleanup in stop():
       - Close server_fd first to reject new connections
       - Break event loops to make dispatch return
       - Wait for worker threads to finish
       - Clear evhttp before event_base (correct dependency order)
---
 be/src/http/ev_http_server.cpp | 49 +++++++++++++++++++++++++++++++-----------
 be/src/http/ev_http_server.h   |  4 +++-
 2 files changed, 40 insertions(+), 13 deletions(-)

diff --git a/be/src/http/ev_http_server.cpp b/be/src/http/ev_http_server.cpp
index 457cf0e0322..ceb53df9180 100644
--- a/be/src/http/ev_http_server.cpp
+++ b/be/src/http/ev_http_server.cpp
@@ -117,16 +117,14 @@ void EvHttpServer::start() {
                               .set_min_threads(_num_workers)
                               .set_max_threads(_num_workers)
                               .build(&_workers));
-    for (int i = 0; i < _num_workers; ++i) {
-        auto status = _workers->submit_func([this, i]() {
-            std::shared_ptr<event_base> base;
-            {
-                std::lock_guard lock(_event_bases_lock);
-                base = _event_bases[i];
-            }
 
-            /* Create a new evhttp object to handle requests. */
-            std::shared_ptr<evhttp> http(evhttp_new(base.get()),
+    // Pre-create all evhttp objects and store them as class members
+    // to ensure proper lifecycle management during shutdown
+    {
+        std::lock_guard lock(_event_bases_lock);
+        _evhttp_servers.resize(_num_workers);
+        for (int i = 0; i < _num_workers; ++i) {
+            std::shared_ptr<evhttp> http(evhttp_new(_event_bases[i].get()),
                                          [](evhttp* http) { evhttp_free(http); 
});
             CHECK(http != nullptr) << "Couldn't create an evhttp.";
 
@@ -136,6 +134,17 @@ void EvHttpServer::start() {
             evhttp_set_newreqcb(http.get(), on_connection, this);
             evhttp_set_gencb(http.get(), on_request, this);
 
+            _evhttp_servers[i] = http;
+        }
+    }
+
+    for (int i = 0; i < _num_workers; ++i) {
+        auto status = _workers->submit_func([this, i]() {
+            std::shared_ptr<event_base> base;
+            {
+                std::lock_guard lock(_event_bases_lock);
+                base = _event_bases[i];
+            }
             event_base_dispatch(base.get());
         });
         CHECK(status.ok());
@@ -143,15 +152,31 @@ void EvHttpServer::start() {
 }
 
 void EvHttpServer::stop() {
+    // 1. Close server fd first to reject new connections
+    close(_server_fd);
+    _server_fd = -1;
+
+    // 2. Break all event loops to make dispatch return
     {
         std::lock_guard<std::mutex> lock(_event_bases_lock);
         for (int i = 0; i < _num_workers; ++i) {
-            event_base_loopbreak(_event_bases[i].get());
+            if (_event_bases[i]) {
+                event_base_loopbreak(_event_bases[i].get());
+            }
         }
     }
+
+    // 3. Wait for all worker threads to finish event_base_dispatch
     _workers->shutdown();
-    _event_bases.clear();
-    close(_server_fd);
+
+    // 4. Now it's safe to cleanup - all worker threads have exited
+    // Clear evhttp before event_base since evhttp depends on event_base
+    {
+        std::lock_guard<std::mutex> lock(_event_bases_lock);
+        _evhttp_servers.clear();
+        _event_bases.clear();
+    }
+
     _started = false;
 }
 
diff --git a/be/src/http/ev_http_server.h b/be/src/http/ev_http_server.h
index d74a8cb4efd..fa1c5a27928 100644
--- a/be/src/http/ev_http_server.h
+++ b/be/src/http/ev_http_server.h
@@ -27,6 +27,7 @@
 #include "util/path_trie.hpp"
 
 struct event_base;
+struct evhttp;
 
 namespace doris {
 
@@ -74,8 +75,9 @@ private:
 
     int _server_fd = -1;
     std::unique_ptr<ThreadPool> _workers;
-    std::mutex _event_bases_lock; // protect _event_bases
+    std::mutex _event_bases_lock; // protect _event_bases and _evhttp_servers
     std::vector<std::shared_ptr<event_base>> _event_bases;
+    std::vector<std::shared_ptr<evhttp>> _evhttp_servers;
 
     std::mutex _handler_lock;
     PathTrie<HttpHandler*> _get_handlers;


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

Reply via email to