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

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


The following commit(s) were added to refs/heads/master by this push:
     new 14ce04de82 ocsp: use Bravo lock for cached responses (#13490)
14ce04de82 is described below

commit 14ce04de82fde613b74f5f8b8c21a080983831f2
Author: Brian Neradt <[email protected]>
AuthorDate: Wed Aug 5 17:00:42 2026 -0500

    ocsp: use Bravo lock for cached responses (#13490)
    
    OCSP stapling serializes TLS handshake readers with refresh scans on a
    per-certificate mutex. Busy certificates therefore pay unnecessary lock
    contention on the handshake hot path.
    
    This patch uses the annotated Bravo reader-writer lock so handshake and
    refresh readers can proceed concurrently while cache updates remain
    exclusive. It keeps prefetched state immutable and allocates the OpenSSL
    destination outside the shared lock. The cached response is copied once
    after its size is revalidated.
    
    Co-authored-by: Craig Taylor <[email protected]>
---
 src/iocore/net/OCSPStapling.cc | 139 ++++++++++++++++++++++++++---------------
 1 file changed, 90 insertions(+), 49 deletions(-)

diff --git a/src/iocore/net/OCSPStapling.cc b/src/iocore/net/OCSPStapling.cc
index e07f18418a..c5fd6505eb 100644
--- a/src/iocore/net/OCSPStapling.cc
+++ b/src/iocore/net/OCSPStapling.cc
@@ -22,6 +22,7 @@
 #include "P_OCSPStapling.h"
 
 #include <memory>
+#include <mutex>
 
 #include <openssl/ssl.h>
 #include <openssl/x509v3.h>
@@ -39,6 +40,7 @@
 #include "SSLStats.h"
 #include "TLSCertCompression.h"
 #include "proxy/FetchSM.h"
+#include "tsutil/Bravo.h"
 
 // Macros for ASN1 and the code in TS_OCSP_* functions were borrowed from 
OpenSSL 3.1.0 (a92271e03a8d0dee507b6f1e7f49512568b2c7ad),
 // and were modified to make them compilable with BoringSSL and C++ compiler.
@@ -283,18 +285,21 @@ namespace
 // Cached info stored in SSL_CTX ex_info
 struct certinfo {
   unsigned char   idx[20]    = {};      // Index in session cache SHA1 hash of 
certificate
-  TS_OCSP_CERTID *cid        = nullptr; // Certificate ID for OCSP requests
+  TS_OCSP_CERTID *cid        = nullptr; // Certificate ID for OCSP requests or 
nullptr if ID cannot be determined
   char           *uri        = nullptr; // Responder details
   char           *certname   = nullptr;
   char           *user_agent = nullptr;
-  ink_mutex       stapling_mutex;
-  unsigned char   resp_der[MAX_STAPLING_DER] = {};
-  unsigned int    resp_derlen                = 0;
-  bool            is_prefetched              = false;
-  bool            is_expire                  = true;
-  time_t          expire_time                = 0;
-
-  certinfo() { ink_mutex_init(&stapling_mutex); }
+  const bool      is_prefetched;
+
+  // OCSP response data, protected by resp_mutex.
+  // Readers take a shared lock; the updater takes an exclusive lock.
+  unsigned char                   resp_der[MAX_STAPLING_DER] = {};
+  unsigned int                    resp_derlen                = 0;
+  bool                            is_expire                  = true;
+  time_t                          expire_time                = 0;
+  mutable ts::bravo::shared_mutex resp_mutex;
+
+  explicit certinfo(bool is_prefetched) : is_prefetched(is_prefetched) {}
   ~certinfo()
   {
     if (cid) {
@@ -305,7 +310,6 @@ struct certinfo {
     }
     ats_free(certname);
     ats_free(user_agent);
-    ink_mutex_destroy(&stapling_mutex);
   }
 
   certinfo(const certinfo &)            = delete;
@@ -848,12 +852,13 @@ stapling_cache_response(TS_OCSP_RESPONSE *rsp, certinfo 
*cinf)
     return false;
   }
 
-  ink_mutex_acquire(&cinf->stapling_mutex);
-  memcpy(cinf->resp_der, resp_der, resp_derlen);
-  cinf->resp_derlen = resp_derlen;
-  cinf->is_expire   = false;
-  cinf->expire_time = time(nullptr) + SSLConfigParams::ssl_ocsp_cache_timeout;
-  ink_mutex_release(&cinf->stapling_mutex);
+  {
+    std::lock_guard<ts::bravo::shared_mutex> lock(cinf->resp_mutex);
+    memcpy(cinf->resp_der, resp_der, resp_derlen);
+    cinf->resp_derlen = resp_derlen;
+    cinf->is_expire   = false;
+    cinf->expire_time = time(nullptr) + 
SSLConfigParams::ssl_ocsp_cache_timeout;
+  }
 
   Dbg(dbg_ctl_ssl_ocsp, "stapling_cache_response: success to cache response");
   return true;
@@ -882,7 +887,7 @@ ssl_stapling_init_cert(SSL_CTX *ctx, X509 *cert, const char 
*certname, const cha
     map        = new certinfo_map;
     map_is_new = true;
   }
-  auto      cinf_ptr = std::make_unique<certinfo>();
+  auto      cinf_ptr = std::make_unique<certinfo>(rsp_file != nullptr);
   certinfo *cinf     = cinf_ptr.get();
 
   // Initialize certinfo
@@ -890,8 +895,6 @@ ssl_stapling_init_cert(SSL_CTX *ctx, X509 *cert, const char 
*certname, const cha
   if (SSLConfigParams::ssl_ocsp_user_agent != nullptr) {
     cinf->user_agent = ats_strdup(SSLConfigParams::ssl_ocsp_user_agent);
   }
-  cinf->is_prefetched = rsp_file ? true : false;
-
   if (cinf->is_prefetched) {
     Dbg(dbg_ctl_ssl_ocsp, "using OCSP prefetched response file %s", rsp_file);
     FILE *fp = fopen(rsp_file, "r");
@@ -1331,11 +1334,14 @@ ocsp_update()
           if (map) {
             // Walk over all certs associated with this CTX
             for (auto &iter : *map) {
-              cinf = iter.second.get();
-              ink_mutex_acquire(&cinf->stapling_mutex);
+              cinf         = iter.second.get();
               current_time = time(nullptr);
-              if (cinf->resp_derlen == 0 || cinf->is_expire || 
cinf->expire_time < current_time) {
-                ink_mutex_release(&cinf->stapling_mutex);
+              bool needs_refresh;
+              {
+                ts::bravo::shared_lock<ts::bravo::shared_mutex> 
lock(cinf->resp_mutex);
+                needs_refresh = cinf->resp_derlen == 0 || cinf->is_expire || 
cinf->expire_time < current_time;
+              }
+              if (needs_refresh) {
                 if (stapling_refresh_response(cinf, &resp)) {
                   Dbg(dbg_ctl_ssl_ocsp, "Successfully refreshed OCSP for %s 
certificate. url=%s", cinf->certname, cinf->uri);
                   Metrics::Counter::increment(ssl_rsb.ocsp_refreshed_cert);
@@ -1345,8 +1351,6 @@ ocsp_update()
                   
Metrics::Counter::increment(ssl_rsb.ocsp_refresh_cert_failure);
                   cert_compress_invalidate_or_recompress(ctx.get());
                 }
-              } else {
-                ink_mutex_release(&cinf->stapling_mutex);
               }
             }
           }
@@ -1422,37 +1426,74 @@ ssl_callback_ocsp_stapling(SSL *ssl, void *)
     return SSL_TLSEXT_ERR_NOACK;
   }
 
-  ink_mutex_acquire(&cinf->stapling_mutex);
-  time_t current_time = time(nullptr);
-  if ((cinf->resp_derlen == 0 || cinf->is_expire) || (cinf->expire_time < 
current_time && !cinf->is_prefetched)) {
-    ink_mutex_release(&cinf->stapling_mutex);
-    SiteThrottledError("ssl_callback_ocsp_stapling: failed to get certificate 
status for %s", cinf->certname);
-    return SSL_TLSEXT_ERR_NOACK;
-  } else {
 #ifdef OPENSSL_IS_BORINGSSL
+  int set_ok;
+  {
+    ts::bravo::shared_lock<ts::bravo::shared_mutex> lock(cinf->resp_mutex);
+
+    time_t current_time = time(nullptr);
+    if (cinf->resp_derlen == 0 || cinf->is_expire || (cinf->expire_time < 
current_time && !cinf->is_prefetched)) {
+      SiteThrottledError("ssl_callback_ocsp_stapling: failed to get 
certificate status for %s", cinf->certname);
+      return SSL_TLSEXT_ERR_NOACK;
+    }
+
     // SSL_set_ocsp_response copies the response, so hand it the cached buffer 
directly.
-    int set_ok = SSL_set_ocsp_response(ssl, cinf->resp_der, cinf->resp_derlen);
-    ink_mutex_release(&cinf->stapling_mutex);
+    set_ok = SSL_set_ocsp_response(ssl, cinf->resp_der, cinf->resp_derlen);
+  }
 #else
-    unsigned char *p = static_cast<unsigned char 
*>(OPENSSL_malloc(cinf->resp_derlen));
-    if (p == nullptr) {
-      ink_mutex_release(&cinf->stapling_mutex);
-      Dbg(dbg_ctl_ssl_ocsp, "ssl_callback_ocsp_stapling: failed to allocate 
memory for %s", cinf->certname);
-      return SSL_TLSEXT_ERR_NOACK;
+  unsigned char *p             = nullptr;
+  unsigned int   resp_capacity = 0;
+  unsigned int   resp_derlen;
+
+  while (true) {
+    unsigned int required_capacity;
+    bool         is_response_available;
+    {
+      ts::bravo::shared_lock<ts::bravo::shared_mutex> lock(cinf->resp_mutex);
+
+      time_t current_time = time(nullptr);
+      is_response_available =
+        cinf->resp_derlen != 0 && !cinf->is_expire && (cinf->expire_time >= 
current_time || cinf->is_prefetched);
+
+      if (is_response_available) {
+        resp_derlen = cinf->resp_derlen;
+        if (resp_derlen <= resp_capacity) {
+          memcpy(p, cinf->resp_der, resp_derlen);
+          break;
+        }
+        required_capacity = resp_derlen;
+      }
     }
-    memcpy(p, cinf->resp_der, cinf->resp_derlen);
-    ink_mutex_release(&cinf->stapling_mutex);
-    // Takes ownership of p and frees it on success; on failure it does not.
-    int set_ok = SSL_set_tlsext_status_ocsp_resp(ssl, p, cinf->resp_derlen);
-    if (set_ok == 0) {
+
+    if (!is_response_available) {
       OPENSSL_free(p);
+      SiteThrottledError("ssl_callback_ocsp_stapling: failed to get 
certificate status for %s", cinf->certname);
+      return SSL_TLSEXT_ERR_NOACK;
     }
-#endif
-    if (set_ok == 0) {
+
+    unsigned char *new_p = static_cast<unsigned char 
*>(OPENSSL_malloc(required_capacity));
+    if (new_p == nullptr) {
+      OPENSSL_free(p);
+      Dbg(dbg_ctl_ssl_ocsp, "ssl_callback_ocsp_stapling: failed to allocate 
memory for %s", cinf->certname);
       return SSL_TLSEXT_ERR_NOACK;
     }
-    Dbg(dbg_ctl_ssl_ocsp, "ssl_callback_ocsp_stapling: successfully got 
certificate status for %s", cinf->certname);
-    Dbg(dbg_ctl_ssl_ocsp, "is_prefetched:%d uri:%s", cinf->is_prefetched, 
cinf->uri);
-    return SSL_TLSEXT_ERR_OK;
+    OPENSSL_free(p);
+    p             = new_p;
+    resp_capacity = required_capacity;
   }
+
+  // Takes ownership of p and frees it on success; on failure it does not.
+  int set_ok = SSL_set_tlsext_status_ocsp_resp(ssl, p, resp_derlen);
+  if (set_ok == 0) {
+    OPENSSL_free(p);
+  }
+#endif
+
+  if (set_ok == 0) {
+    return SSL_TLSEXT_ERR_NOACK;
+  }
+
+  Dbg(dbg_ctl_ssl_ocsp, "ssl_callback_ocsp_stapling: successfully got 
certificate status for %s", cinf->certname);
+  Dbg(dbg_ctl_ssl_ocsp, "is_prefetched:%d uri:%s", cinf->is_prefetched, 
cinf->uri);
+  return SSL_TLSEXT_ERR_OK;
 }

Reply via email to