diff --git a/include/tscore/DbgCtl.h b/include/tscore/DbgCtl.h index f7b2f59ad3f..74d80236d05 100644 --- a/include/tscore/DbgCtl.h +++ b/include/tscore/DbgCtl.h @@ -59,4 +59,27 @@ class DbgCtl friend TSDbgCtl const *TSDbgCtlCreate(char const *tag); friend void TSDbgCtlDestroy(TSDbgCtl const *dbg_ctl); + +public: + // When loading an ATS plugin with dlopen(), an instance of this class should exist in the stack. This will prevent + // https://github.com/apache/trafficserver/issues/10129 . It will prevent DbgCtl member functions from indiredtly + // calling Regex::compile(), which calls a function that defines a thread_local variable. Such functions try to lock + // the same global mutex (in the C/C++ runtime) that is locked when dlopen() is in progress. This prevents a deadlock + // where shared library static initialization is holding the global mutex and waiting on the Registry mutex, and + // a DbgCtl intanstantiantion has called Regex::compile() in a different thread, is holding the Registry mutex, and + // waiting on the global mutex. + // + class Guard_dlopen + { + public: + Guard_dlopen(); + ~Guard_dlopen(); + + // No copying. + Guard_dlopen(Guard_dlopen const &) = delete; + Guard_dlopen &operator=(Guard_dlopen const &) = delete; + + private: + _RegistryAccessor *rap; + }; }; diff --git a/proxy/Plugin.cc b/proxy/Plugin.cc index e0a0bc3dc9e..148ef61120f 100644 --- a/proxy/Plugin.cc +++ b/proxy/Plugin.cc @@ -31,6 +31,7 @@ #include "Plugin.h" #include "tscore/ink_cap.h" #include "tscore/Filenames.h" +#include "tscore/DbgCtl.h" #define MAX_PLUGIN_ARGS 64 @@ -102,8 +103,11 @@ PluginRegInfo::~PluginRegInfo() bool plugin_dso_load(const char *path, void *&handle, void *&init, std::string &error) { - handle = dlopen(path, RTLD_NOW); - init = nullptr; + { + DbgCtl::Guard_dlopen g; + handle = dlopen(path, RTLD_NOW); + } + init = nullptr; if (!handle) { error.assign("unable to load '").append(path).append("': ").append(dlerror()); Error("%s", error.c_str()); diff --git a/proxy/http/remap/PluginDso.cc b/proxy/http/remap/PluginDso.cc index abba587d36d..5f29e97eabd 100644 --- a/proxy/http/remap/PluginDso.cc +++ b/proxy/http/remap/PluginDso.cc @@ -34,6 +34,7 @@ #include "unit-tests/plugin_testing_common.h" #else #include "tscore/Diags.h" +#include "tscore/DbgCtl.h" #define PluginDebug Debug #define PluginError Error #endif @@ -105,10 +106,15 @@ PluginDso::load(std::string &error) /* Now attempt to load the plugin DSO */ #if defined(darwin) - if (!dlopen_preflight(_runtimePath.c_str()) || (_dlh = dlopen(_runtimePath.c_str(), RTLD_NOW | RTLD_LOCAL)) == nullptr) { -#else - if ((_dlh = dlopen(_runtimePath.c_str(), RTLD_NOW | RTLD_LOCAL)) == nullptr) { + if (!dlopen_preflight(_runtimePath.c_str())) { + _dlh = nullptr; + } else #endif + { + DbgCtl::Guard_dlopen g; + _dlh = dlopen(_runtimePath.c_str(), RTLD_NOW | RTLD_LOCAL); + } + if (_dlh == nullptr) { const char *err = dlerror(); concat_error(error, err ? err : "unknown dlopen() error"); _dlh = nullptr; /* mark that the constructor failed. */ diff --git a/src/tscore/DbgCtl.cc b/src/tscore/DbgCtl.cc index 9a8dcaf4693..77177f957fa 100644 --- a/src/tscore/DbgCtl.cc +++ b/src/tscore/DbgCtl.cc @@ -66,7 +66,7 @@ class DbgCtl::_RegistryAccessor _mtx.unlock(); } - std::mutex _mtx; + std::recursive_mutex _mtx; friend class DbgCtl::_RegistryAccessor; }; @@ -195,3 +195,10 @@ DbgCtl::update() const_cast(i.on) = diags()->tag_activated(i.tag, DiagsTagType_Debug); } } + +DbgCtl::Guard_dlopen::Guard_dlopen() : rap{new _RegistryAccessor} {}; + +DbgCtl::Guard_dlopen::~Guard_dlopen() +{ + delete rap; +}