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 f98d5d06 Fix HTTPS pooled client crash on unexpected SSL EOF (#3316)
f98d5d06 is described below

commit f98d5d0628483a7a730d2ec4db5e3bea89cf8f00
Author: Felix-Gong <[email protected]>
AuthorDate: Fri Jun 5 10:30:31 2026 +0800

    Fix HTTPS pooled client crash on unexpected SSL EOF (#3316)
    
    * Fix HTTPS pooled client crash on unexpected SSL EOF
    
    When OpenSSL 3.x detects unexpected EOF (peer closed without
    close_notify), SSL_read returns 0 with SSL_ERROR_SSL. The code
    didn't return -1, causing error_code=0 to propagate to
    Controller::SetFailed() which triggers CHECK(false).
    
    Fix by returning -1 with errno=ESSL when SSL errors are detected
    in DoRead(), instead of falling through and returning nr.
    
    Discovered during RISC-V porting and integration testing.
    
    Fixes #3307
    Signed-off-by: Felix Gong <[email protected]>
    
    * Add unit test for unexpected SSL EOF handling
    
    Test that Socket::DoRead() returns -1 with errno=ESSL when the remote
    side closes the TCP connection without sending close_notify, verifying
    the fix in commit 7a1dfe7c.
    
    ---------
    
    Signed-off-by: Felix Gong <[email protected]>
---
 src/brpc/socket.cpp        |  9 +++++-
 test/brpc_ssl_unittest.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 84 insertions(+), 1 deletion(-)

diff --git a/src/brpc/socket.cpp b/src/brpc/socket.cpp
index 0ca69504..aa349ec8 100644
--- a/src/brpc/socket.cpp
+++ b/src/brpc/socket.cpp
@@ -2133,7 +2133,14 @@ ssize_t Socket::DoRead(size_t size_hint) {
     default: {
         const unsigned long e = ERR_get_error();
         if (nr == 0) {
-            // Socket EOF or SSL session EOF
+            if (ssl_error != SSL_ERROR_ZERO_RETURN) {
+                // Unexpected EOF without proper SSL shutdown (close_notify)
+                LOG(WARNING) << "Fail to read from ssl_fd=" << fd()
+                             << ": unexpected ssl_error=" << ssl_error;
+                errno = ESSL;
+                return -1;
+            }
+            // Clean SSL shutdown (close_notify received)
         } else if (e != 0) {
             LOG(WARNING) << "Fail to read from ssl_fd=" << fd()
                          << ": " << SSLError(e);
diff --git a/test/brpc_ssl_unittest.cpp b/test/brpc_ssl_unittest.cpp
index a469a623..e7545a0c 100644
--- a/test/brpc_ssl_unittest.cpp
+++ b/test/brpc_ssl_unittest.cpp
@@ -497,3 +497,79 @@ TEST_F(SSLTest, ssl_perf) {
     close(clifd);
     close(servfd);
 }
+
+struct AbruptCloseArgs { int listenfd; };
+
+static void* abrupt_close_server(void* arg) {
+    AbruptCloseArgs* a = (AbruptCloseArgs*)arg;
+    int connfd = accept(a->listenfd, NULL, NULL);
+    if (connfd < 0) return NULL;
+    SSL_CTX* ctx = brpc::CreateServerSSLContext(
+        "cert1.crt", "cert1.key", brpc::SSLOptions(), NULL, NULL);
+    SSL* ssl = brpc::CreateSSLSession(ctx, 0, connfd, true);
+    if (ssl) { SSL_do_handshake(ssl); SSL_free(ssl); }
+    close(connfd);
+    return NULL;
+}
+
+TEST_F(SSLTest, ssl_unexpected_eof) {
+    // Verify that Socket::DoRead() returns -1 with errno=ESSL when the
+    // remote side closes the TCP connection without sending close_notify.
+    // Without the fix, DoRead() returns 0, causing error_code=0 to
+    // propagate to Controller::SetFailed() which triggers CHECK(false).
+
+    const int port = 5962;
+    butil::EndPoint ep(butil::IP_ANY, port);
+    butil::fd_guard listenfd(butil::tcp_listen(ep));
+    ASSERT_GT(listenfd, 0);
+
+    AbruptCloseArgs server_args = { listenfd };
+    pthread_t server_tid;
+    ASSERT_EQ(0, pthread_create(&server_tid, NULL, abrupt_close_server,
+                                &server_args));
+
+    brpc::Protocol dummy_protocol = {
+        brpc::policy::ParseRpcMessage, brpc::SerializeRequestDefault,
+        brpc::policy::PackRpcRequest, NULL, ProcessResponse,
+        NULL, NULL, NULL, brpc::CONNECTION_TYPE_ALL, "ssl_ut_eof"
+    };
+    ASSERT_EQ(0, RegisterProtocol((brpc::ProtocolType)31, dummy_protocol));
+
+    brpc::InputMessageHandler dummy_handler = {
+        dummy_protocol.parse, dummy_protocol.process_response,
+        NULL, NULL, dummy_protocol.name
+    };
+    brpc::InputMessenger messenger;
+    ASSERT_EQ(0, messenger.AddHandler(dummy_handler));
+
+    brpc::SocketOptions socket_options;
+    butil::EndPoint server_ep(butil::IP_ANY, port);
+    socket_options.remote_side = server_ep;
+    socket_options.connect_on_create = true;
+    // Do NOT set on_edge_triggered_events — we will call DoRead manually.
+    socket_options.user = &messenger;
+
+    brpc::ChannelSSLOptions ssl_options;
+    SSL_CTX* raw_ctx = brpc::CreateClientSSLContext(ssl_options);
+    ASSERT_NE(nullptr, raw_ctx);
+    std::shared_ptr<brpc::SocketSSLContext> ssl_ctx =
+        std::make_shared<brpc::SocketSSLContext>();
+    ssl_ctx->raw_ctx = raw_ctx;
+    socket_options.initial_ssl_ctx = ssl_ctx;
+
+    brpc::SocketId socket_id;
+    ASSERT_EQ(0, brpc::Socket::Create(socket_options, &socket_id));
+    brpc::SocketUniquePtr ptr;
+    ASSERT_EQ(0, brpc::Socket::Address(socket_id, &ptr));
+
+    // Wait for server to close the connection without close_notify.
+    pthread_join(server_tid, NULL);
+    usleep(50000);
+
+    // DoRead should detect the unexpected EOF and return -1 with errno=ESSL.
+    ssize_t nr = ptr->DoRead(1024);
+    EXPECT_EQ(-1, nr);
+    EXPECT_EQ(brpc::ESSL, errno);
+
+    ptr->SetFailed();
+}


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

Reply via email to