Skip to content

Commit 79d0577

Browse files
authored
9.2.x: Fix an error on SSL config reload (plus some cleanup). (#9334) (#9647)
* Fix an error on SSL config reload (plus some cleanup). (#9334) * Fix an error on SSL config reload (plus some cleanup). This seems to have eliminated some ERROR diags we were seeing in Yahoo Prod when doing config reloads. The SSLSecret public functions no longer return pointers into the unorded_map of secrets, they return a copy of the secret data. This seemed thread unsafe. A periodic poll running in the background can update the secret data for an entry for a secret name in the map. To avoid exporting pointers, I had to change the prototype of TSSslSecretGet(). Hopefully there are no existing plugins that are already using this TS API function, so breaking this rule will be moot. * YTSATS-4067: Fix deadlock with secret_map_mutex (#740) 1. getOrLoadSecret grabbed the secret_map_mutex and called loadSecret. 2. loadSecret dispatched to Continations that registered for the TS_EVENT_SSL_SECRET event. This would try to grab the Continuation's lock. 3. In the meantime, those Continuations could call setSecret which would try to grab the secret_map_mutex. If this Continuation did this while holding the lock that step 2 is waiting upon, then there will be a deadlock between the Continuation lock and the secret_map_mutex between the two threads. This patch avoids the deadlock by releasing the secret_map_mutex lock before calling loadSecret. It also updates the secret_map when loading secrets from a file in loadSecret. --------- Co-authored-by: Brian Neradt <brian.neradt@verizonmedia.com> (cherry picked from commit 0c2488c) Conflicts: src/traffic_server/InkAPI.cc * Remove TSSslSecretXxx TS API functions. * Restore version of Au test tls_keepalive not needing ssl_secret_load_test.so.
1 parent 7ade4a6 commit 79d0577

23 files changed

+158
-1530
lines changed

doc/developer-guide/api/functions/TSLifecycleHookAdd.en.rst

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ specified by :arg:`id`. Lifecycle hooks are based on the Traffic Server
4040
process, not on any specific transaction or session. These will typically be
4141
called only once during the execution of the Traffic Server process and
4242
therefore should be added in :func:`TSPluginInit` (which could itself be
43-
considered a lifecycle hook). Unlike other hooks, lifecycle hooks may not have a
44-
well defined ordering and use of them should not assume that one of the hooks
43+
considered a lifecycle hook). Unlike other hooks, lifecycle hooks may not have
44+
a well defined ordering and use of them should not assume that one of the hooks
4545
is always called before another unless specifically mentioned.
4646

4747
Types
@@ -106,14 +106,6 @@ Types
106106
Invoked with the event :c:data:`TS_EVENT_LIFECYCLE_TASK_THREADS_READY` and ``NULL``
107107
data.
108108

109-
.. cpp:enumerator:: TS_LIFECYCLE_SSL_SECRET_HOOK
110-
111-
Called before the data for the certificate or key is loaded. The data argument to the callback is a pointer to a :type:`TSSecretID` which
112-
contains a pointer to the name of the certificate or key and the relevant version if applicable.
113-
114-
This hook gives the plugin a chance to load the certificate or key from an alternative source and set via the :c:func:`TSSslSecretSet` API.
115-
If there is no plugin override, the certificate or key will be loaded from disk and the secret name will be interpreted as a file path.
116-
117109
.. cpp:enumerator:: TS_LIFECYCLE_SHUTDOWN_HOOK
118110

119111
Called after |TS| receiving a shutdown signal, such as SIGTERM.

doc/developer-guide/api/functions/TSSslSecret.en.rst

Lines changed: 0 additions & 77 deletions
This file was deleted.

doc/release-notes/whats-new.en.rst

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -547,9 +547,6 @@ make the older ``stats_over_http`` obsolete.
547547
Plugin APIs
548548
-----------
549549

550-
A new hook for loading certificates was added, :cpp:enumerator:`TS_LIFECYCLE_SSL_SECRET_HOOK`. When using
551-
this hook, the plugin recieved a structure with a type :c:type:`TSSecretID`.
552-
553550
The transction control APIs where refactored and promoted to that ``ts.h`` public APIs. This adds
554551
:c:func:`TSHttpTxnCntlGet` and :c:func:`TSHttpTxnCntlSet`, and the c:enum::`TSHttpCntlType` enum.
555552

include/ts/apidefs.h.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -438,8 +438,8 @@ typedef enum {
438438
TS_LIFECYCLE_MSG_HOOK,
439439
TS_LIFECYCLE_TASK_THREADS_READY_HOOK,
440440
TS_LIFECYCLE_SHUTDOWN_HOOK,
441-
TS_LIFECYCLE_SSL_SECRET_HOOK,
442-
TS_LIFECYCLE_LAST_HOOK
441+
// TS_LIFECYCLE_SSL_SECRET_HOOK, future release
442+
TS_LIFECYCLE_LAST_HOOK = TS_LIFECYCLE_SHUTDOWN_HOOK + 2
443443
} TSLifecycleHookID;
444444

445445
/**

include/ts/ts.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,13 +1302,6 @@ tsapi TSSslContext TSSslClientContextFindByName(const char *ca_paths, const char
13021302
tsapi TSReturnCode TSSslClientCertUpdate(const char *cert_path, const char *key_path);
13031303
tsapi TSReturnCode TSSslServerCertUpdate(const char *cert_path, const char *key_path);
13041304

1305-
/* Update the transient secret table for SSL_CTX loading */
1306-
tsapi TSReturnCode TSSslSecretSet(const char *secret_name, int secret_name_length, const char *secret_data, int secret_data_len);
1307-
tsapi TSReturnCode TSSslSecretGet(const char *secret_name, int secret_name_length, const char **secret_data_return,
1308-
int *secret_data_len);
1309-
1310-
tsapi TSReturnCode TSSslSecretUpdate(const char *secret_name, int secret_name_length);
1311-
13121305
/* Create a new SSL context based on the settings in records.config */
13131306
tsapi TSSslContext TSSslServerContextCreate(TSSslX509 cert, const char *certname, const char *rsp_file);
13141307
tsapi void TSSslContextDestroy(TSSslContext ctx);

iocore/cache/test/stub.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ APIHook::invoke(int, void *) const
5151
return 0;
5252
}
5353

54+
int
55+
APIHook::blocking_invoke(int, void *) const
56+
{
57+
ink_assert(false);
58+
return 0;
59+
}
60+
5461
APIHook *
5562
APIHook::next() const
5663
{

iocore/net/P_SSLConfig.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
****************************************************************************/
3131
#pragma once
3232

33+
#include <atomic>
34+
3335
#include <openssl/rand.h>
3436

3537
#include "tscore/ink_inet.h"
@@ -165,6 +167,11 @@ struct SSLConfigParams : public ConfigInfo {
165167
void cleanup();
166168
void reset();
167169
void SSLConfigInit(IpMap *global);
170+
171+
private:
172+
// c_str() of string passed to in-progess call to updateCTX().
173+
//
174+
mutable std::atomic<char const *> secret_for_updateCTX{nullptr};
168175
};
169176

170177
/////////////////////////////////////////////////////////////

iocore/net/P_SSLSecret.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
limitations under the License.
2020
*/
2121

22+
#pragma once
23+
2224
#include <string>
2325
#include <string_view>
2426
#include <mutex>
@@ -28,14 +30,13 @@ class SSLSecret
2830
{
2931
public:
3032
SSLSecret() {}
31-
bool getSecret(const std::string &name, std::string_view &data) const;
32-
bool setSecret(const std::string &name, const char *data, int data_len);
33-
bool getOrLoadSecret(const std::string &name, const std::string &name2, std::string_view &data, std::string_view &data2);
33+
std::string getSecret(const std::string &name) const;
34+
void setSecret(const std::string &name, std::string_view data);
35+
void getOrLoadSecret(const std::string &name1, const std::string &name2, std::string &data, std::string &data2);
3436

3537
private:
36-
const std::string *getSecretItem(const std::string &name) const;
37-
bool loadSecret(const std::string &name, const std::string &name2, std::string &data_item, std::string &data_item2);
38-
bool loadFile(const std::string &name, std::string &data_item);
38+
void loadSecret(const std::string &name1, const std::string &name2, std::string &data_item, std::string &data_item2);
39+
std::string loadFile(const std::string &name);
3940

4041
std::unordered_map<std::string, std::string> secret_map;
4142
mutable std::recursive_mutex secret_map_mutex;

iocore/net/SSLConfig.cc

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -739,13 +739,33 @@ cleanup_bio(BIO *&biop)
739739
void
740740
SSLConfigParams::updateCTX(const std::string &cert_secret_name) const
741741
{
742+
Debug("ssl_config_updateCTX", "Update cert %s, %p", cert_secret_name.c_str(), this);
743+
744+
// Instances of SSLConfigParams should access by one thread at a time only. secret_for_updateCTX is accessed
745+
// atomically as a fail-safe.
746+
//
747+
char const *expected = nullptr;
748+
if (!secret_for_updateCTX.compare_exchange_strong(expected, cert_secret_name.c_str())) {
749+
if (is_debug_tag_set("ssl_config_updateCTX")) {
750+
// As a fail-safe, handle it if secret_for_updateCTX doesn't or no longer points to a null-terminated string.
751+
//
752+
char const *s{expected};
753+
for (; *s && (std::size_t(s - expected) < cert_secret_name.size()); ++s) {
754+
}
755+
Debug("ssl_config_updateCTX", "Update cert, indirect recusive call caused by call for %.*s", int(s - expected), expected);
756+
}
757+
return;
758+
}
759+
742760
// Clear the corresponding client CTXs. They will be lazy loaded later
743761
Debug("ssl_load", "Update cert %s", cert_secret_name.c_str());
744762
this->clearCTX(cert_secret_name);
745763

746764
// Update the server cert
747765
SSLMultiCertConfigLoader loader(this);
748766
loader.update_ssl_ctx(cert_secret_name);
767+
768+
secret_for_updateCTX = nullptr;
749769
}
750770

751771
void
@@ -800,8 +820,8 @@ SSLConfigParams::getCTX(const std::string &client_cert, const std::string &key_f
800820

801821
// Set public and private keys
802822
if (!client_cert.empty()) {
803-
std::string_view secret_data;
804-
std::string_view secret_key_data;
823+
std::string secret_data;
824+
std::string secret_key_data;
805825

806826
// Fetch the client_cert data
807827
std::string completeSecretPath{Layout::get()->relative_to(this->clientCertPathOnly, client_cert)};

0 commit comments

Comments
 (0)