From af9cdf2cbe4d696e6bdedef75fa6d96a51feefe3 Mon Sep 17 00:00:00 2001 From: Lukas Hutak Date: Fri, 10 Jul 2020 12:47:00 +0200 Subject: [PATCH] Core: replace RTLD_DEEPBIND (dlopen flag) with a new plugin flag Using RTLD_DEEPBIND flag when loading plugin helps to solve issue with some 3rd party libraries (e.g. librdkafka) if the library redefines a common symbol. However, using this dlopen flag globally for all plugins caused segfault when using C++ iostream (e.g. "std::cout << "some text";) Plugins can use IPX_PF_DEEPBIND flag in ipx_plugin_info to instruct the collector to use RTLD_DEEPBIND when loading the plugin. --- include/ipfixcol2/plugins.h | 17 +++++++++- src/CMakeLists.txt | 3 ++ src/build_config.h.in | 3 ++ src/core/configurator/plugin_mgr.cpp | 51 +++++++++++++++++++++++----- 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/include/ipfixcol2/plugins.h b/include/ipfixcol2/plugins.h index 2b6326e0..772beb3c 100644 --- a/include/ipfixcol2/plugins.h +++ b/include/ipfixcol2/plugins.h @@ -135,6 +135,21 @@ typedef struct ipx_ctx_ext ipx_ctx_ext_t; */ #define IPX_PT_OUTPUT 3U +/** + * \def IPX_PF_DEEPBIND + * \brief Use deep bind when resolving symbols of a plugin (and additional depending libraries) + * + * Some plugins might depend on an external library which redefines one or more common symbols + * (e.g. thrd_create) used by the collector (or other plugins). Since common version of these + * symbols is resolved before any plugin is loaded, these redefined symbols are ignored. + * Therefore, the plugin (or third party libraries) might not be able to correctly work. + * + * This flag instructs the collector to use RTLD_DEEPBIND (see manual page of dlopen) which + * solves this issue. However, it might not be supported by non-glibc implementations + * (as it is a GNU extension) and might break some other functions. Use only if really required! + */ +#define IPX_PF_DEEPBIND 1U + /** * \brief Identification of a plugin * @@ -149,7 +164,7 @@ struct ipx_plugin_info { const char *dsc; /** Plugin type (one of #IPX_PT_INPUT, #IPX_PT_INTERMEDIATE, #IPX_PT_OUTPUT) */ uint16_t type; - /** Configuration flags (reserved for future use) */ + /** Configuration flags (zero or more IPX_PF_* values might be ORed here) */ uint16_t flags; /** Plugin version string (like "1.2.3") */ const char *version; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 86b5c759..495feaf8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,9 @@ if (POLICY CMP0063) cmake_policy(SET CMP0063 NEW) endif() +include(CheckSymbolExists) +check_symbol_exists("RTLD_DEEPBIND" "dlfcn.h" HAVE_RTLD_DEEPBIND) + # Configure a header file to pass some CMake variables configure_file( "${PROJECT_SOURCE_DIR}/src/build_config.h.in" diff --git a/src/build_config.h.in b/src/build_config.h.in index d1175e1b..22dbc9ff 100644 --- a/src/build_config.h.in +++ b/src/build_config.h.in @@ -125,6 +125,9 @@ #define IPX_BUILD_ARCH "unknown" #endif +// Deep bind support +#cmakedefine HAVE_RTLD_DEEPBIND + /**@}*/ #endif /* _BUILD_CONFIG_ */ diff --git a/src/core/configurator/plugin_mgr.cpp b/src/core/configurator/plugin_mgr.cpp index 45a6312d..79c8d4ef 100644 --- a/src/core/configurator/plugin_mgr.cpp +++ b/src/core/configurator/plugin_mgr.cpp @@ -207,7 +207,7 @@ ipx_plugin_mgr::cache_add_file(const char *path) dlerror(); // Try to load the plugin (and unload it automatically) - const int flags = RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND; + const int flags = RTLD_LAZY | RTLD_LOCAL; auto delete_fn = [](void *handle) {dlclose(handle);}; std::unique_ptr> handle(dlopen(path, flags), delete_fn); if (!handle) { @@ -308,7 +308,7 @@ ipx_plugin_mgr::plugin_list() dlerror(); const char *path = cache_entry.path.c_str(); - const int flags = RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND; + const int flags = RTLD_LAZY | RTLD_LOCAL; auto delete_fn = [](void *handle) {dlclose(handle);}; std::unique_ptr> handle(dlopen(path, flags), delete_fn); @@ -348,6 +348,15 @@ ipx_plugin_mgr::plugin_list() continue; } + if (info->flags & IPX_PF_DEEPBIND) { +#ifdef HAVE_RTLD_DEEPBIND + plugin_entry.msg_notes.emplace_back("Deep bind (RTLD_DEEPBIND) required"); +#else + plugin_entry.msg_warning.emplace_back( + "Deep bind (RTLD_DEEPBIND) required but not supported by C library"); +#endif + } + if (!version_ok) { std::stringstream ss; ss << "incompatible with this collector version (min. required: "; @@ -503,18 +512,44 @@ ipx_plugin_mgr::version_check(const std::string &min_version) * \param[in] path Path to the plugin to load * \param[in] auto_unload Automatically unload the plugin on destroy (can be changed later) */ -ipx_plugin_mgr::plugin::plugin(const std::string &path, bool auto_unload) +ipx_plugin_mgr::plugin::plugin(const std::string &path, bool auto_unload) : unload(auto_unload) { + auto delete_fn = [](void *handle) {dlclose(handle);}; + std::unique_ptr> handle_wrap(nullptr, delete_fn); + + const struct ipx_plugin_info *info; + int load_flags = RTLD_NOW | RTLD_LOCAL; + // Initialize default parameters ref_cnt = 0; std::memset(&cbs, 0, sizeof(cbs)); - // Load the plugin - auto delete_fn = [](void *handle) {dlclose(handle);}; - int flags = RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND; - dlerror(); // Clear all errors first - std::unique_ptr> handle_wrap(dlopen(path.c_str(), flags), delete_fn); + // Determine whether or not to use deep bind + dlerror(); + handle_wrap.reset(dlopen(path.c_str(), RTLD_LAZY | RTLD_LOCAL)); + if (!handle_wrap) { + // Failed to open the file + std::string err_msg = "Failed to load a plugin from '" + path + "': " + dlerror(); + throw ipx_plugin_mgr::error(err_msg); + } + + *(void **)(&info) = symbol_get(handle_wrap.get(), "ipx_plugin_info", false); + if (info->flags & IPX_PF_DEEPBIND) { +#ifdef HAVE_RTLD_DEEPBIND + IPX_DEBUG(comp_str, "Loading plugin from '%s' using RTLD_DEEPBIND flag!", path.c_str()); + load_flags |= RTLD_DEEPBIND; +#else + std::string err_msg = "Deep bind (RTLD_DEEPBIND) required but not supported by C library"; + throw ipx_plugin_mgr::error("Failed to load a plugin from '" + path + "': " + err_msg); +#endif + } + + // Close the plugin and reload it + handle_wrap.reset(); + + dlerror(); + handle_wrap.reset(dlopen(path.c_str(), load_flags)); if (!handle_wrap) { // Failed to open the file std::string err_msg = "Failed to load a plugin from '" + path + "': " + dlerror();