<p>Sebastian Kemper has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11217">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">loader: support for permanent dlopen()<br><br>Asterisk assumes that dlopen() will always run the constructor of a<br>shared library and every dlclose() will run its destructor. But dlopen()<br>may be permanent, meaning the constructor will only be run once, as is<br>the case with musl libc.<br><br>With a permanent dlopen() the Asterisk module loader does not work<br>correctly, because it's expectations regarding when the constructors and<br>destructors are run are not met. In fact a segmentation fault will occur<br>when the first module is "re-opened" that has AST_MODFLAG_GLOBAL_SYMBOLS<br>set (the dlopen() does not call the constructor, resource_being_loaded<br>is not set to NULL, then strlen is called with NULL instead of a string,<br>see issue ASTERISK-28319).<br><br>This commit adds code to the loader that will manually run the<br>constructors/destructors of the (non-builtin) modules where needed. To<br>achieve this a new ao2 container (linked list) is started and filled<br>with objects that contain the names of the modules and the pointers to<br>their respective info structs.<br><br>This behavior can be activated when configuring Asterisk<br>(--enable-permanent-dlopen()). By default this is disabled, of course.<br><br>ASTERISK-28319 #close<br><br>Signed-off-by: Sebastian Kemper <sebastian_ml@gmx.net><br>Change-Id: I86693a0ecf25d5ba81c73773a03df4abc3426875<br>---<br>M configure.ac<br>M main/loader.c<br>2 files changed, 165 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/17/11217/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/configure.ac b/configure.ac</span><br><span>index 1ec7765..e65f159 100644</span><br><span>--- a/configure.ac</span><br><span>+++ b/configure.ac</span><br><span>@@ -729,6 +729,20 @@</span><br><span> </span><br><span> fi</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+AC_ARG_ENABLE([permanent-dlopen],</span><br><span style="color: hsl(120, 100%, 40%);">+     [AS_HELP_STRING([--enable-permanent-dlopen],</span><br><span style="color: hsl(120, 100%, 40%);">+          [Enable when your libc has a permanent dlopen like musl])],</span><br><span style="color: hsl(120, 100%, 40%);">+   [case "${enableval}" in</span><br><span style="color: hsl(120, 100%, 40%);">+             y|ye|yes) PERMANENT_DLOPEN=yes ;;</span><br><span style="color: hsl(120, 100%, 40%);">+             n|no)  PERMANENT_DLOPEN=no ;;</span><br><span style="color: hsl(120, 100%, 40%);">+         *) AC_MSG_ERROR(bad value ${enableval} for --enable-permanent-dlopen)  ;;</span><br><span style="color: hsl(120, 100%, 40%);">+     esac], [PERMANENT_DLOPEN=no])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AC_SUBST([PERMANENT_DLOPEN])</span><br><span style="color: hsl(120, 100%, 40%);">+if test "${PERMANENT_DLOPEN}" == "yes"; then</span><br><span style="color: hsl(120, 100%, 40%);">+       AC_DEFINE([HAVE_PERMANENT_DLOPEN], 1, [Define to support libc with permanent dlopen.])</span><br><span style="color: hsl(120, 100%, 40%);">+fi</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> # some embedded systems omit internationalization (locale) support</span><br><span> AC_CHECK_HEADERS([xlocale.h])</span><br><span> </span><br><span>diff --git a/main/loader.c b/main/loader.c</span><br><span>index 3749c95..b3e25a1 100644</span><br><span>--- a/main/loader.c</span><br><span>+++ b/main/loader.c</span><br><span>@@ -153,6 +153,38 @@</span><br><span> static struct ast_vector_string startup_errors;</span><br><span> static struct ast_str *startup_error_builder;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_PERMANENT_DLOPEN)</span><br><span style="color: hsl(120, 100%, 40%);">+#define FIRST_DLOPEN 999</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ao2_container *info_list = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct info_list_obj {</span><br><span style="color: hsl(120, 100%, 40%);">+    const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct ast_module_info *info;</span><br><span style="color: hsl(120, 100%, 40%);">+   int dlopened;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void info_list_obj_dtor(void *obj)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct info_list_obj *remove_entry = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   ast_free((char *)remove_entry->name);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct info_list_obj *info_list_obj_alloc(const char *name,</span><br><span style="color: hsl(120, 100%, 40%);">+     const struct ast_module_info *info)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        struct info_list_obj *new_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    new_entry = ao2_alloc(sizeof(*new_entry), info_list_obj_dtor);</span><br><span style="color: hsl(120, 100%, 40%);">+        new_entry->name = ast_strdup(name);</span><br><span style="color: hsl(120, 100%, 40%);">+        new_entry->info = info;</span><br><span style="color: hsl(120, 100%, 40%);">+    new_entry->dlopened = FIRST_DLOPEN;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return new_entry;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static __attribute__((format(printf, 1, 2))) void module_load_error(const char *fmt, ...)</span><br><span> {</span><br><span>     char *copy = NULL;</span><br><span>@@ -250,6 +282,94 @@</span><br><span>  */</span><br><span> static struct module_list builtin_module_list;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_PERMANENT_DLOPEN)</span><br><span style="color: hsl(120, 100%, 40%);">+static int info_list_obj_cmp_fn(void *obj1, void *obj2, int flags)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     struct info_list_obj *entry1 = obj1;</span><br><span style="color: hsl(120, 100%, 40%);">+  struct info_list_obj *entry2 = obj2;</span><br><span style="color: hsl(120, 100%, 40%);">+  const char *entry_name = (flags & OBJ_KEY) ? obj2 : entry2->name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return strcasecmp(entry1->name, entry_name) ? 0 : CMP_MATCH | CMP_STOP;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static char *get_name_from_resource(const char *resource)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int len;</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *last_three;</span><br><span style="color: hsl(120, 100%, 40%);">+       char *mod_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (!resource) {</span><br><span style="color: hsl(120, 100%, 40%);">+              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   len = strlen(resource);</span><br><span style="color: hsl(120, 100%, 40%);">+       if (len > 3) {</span><br><span style="color: hsl(120, 100%, 40%);">+             last_three = &resource[len-3];</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!strcasecmp(last_three, ".so")) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       mod_name = ast_calloc(1, len - 2);</span><br><span style="color: hsl(120, 100%, 40%);">+                    if (mod_name) {</span><br><span style="color: hsl(120, 100%, 40%);">+                               ast_copy_string(mod_name, resource, len - 2);</span><br><span style="color: hsl(120, 100%, 40%);">+                         return mod_name;</span><br><span style="color: hsl(120, 100%, 40%);">+                      } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                              /* Unable to allocate memory. */</span><br><span style="color: hsl(120, 100%, 40%);">+                              return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+                  }</span><br><span style="color: hsl(120, 100%, 40%);">+             }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Resource is the name - happens when manually unloading a module. */</span><br><span style="color: hsl(120, 100%, 40%);">+        mod_name = ast_calloc(1, len + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+    if (mod_name) {</span><br><span style="color: hsl(120, 100%, 40%);">+               ast_copy_string(mod_name, resource, len + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+         return mod_name;</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   /* Unable to allocate memory. */</span><br><span style="color: hsl(120, 100%, 40%);">+      return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void manual_mod_reg(const void *lib, const char *resource)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       struct info_list_obj *obj_tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+        char *mod_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     if (lib) {</span><br><span style="color: hsl(120, 100%, 40%);">+            mod_name = get_name_from_resource(resource);</span><br><span style="color: hsl(120, 100%, 40%);">+          if (mod_name) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       obj_tmp = ao2_find(info_list, mod_name, OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+                      if (obj_tmp) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                if (obj_tmp->dlopened == FIRST_DLOPEN) {</span><br><span style="color: hsl(120, 100%, 40%);">+                                   obj_tmp->dlopened = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+                             } else {</span><br><span style="color: hsl(120, 100%, 40%);">+                                      ast_module_register(obj_tmp->info);</span><br><span style="color: hsl(120, 100%, 40%);">+                                }</span><br><span style="color: hsl(120, 100%, 40%);">+                     }</span><br><span style="color: hsl(120, 100%, 40%);">+                     ast_free(mod_name);</span><br><span style="color: hsl(120, 100%, 40%);">+           }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void manual_mod_unreg(const char *resource)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct info_list_obj *obj_tmp;</span><br><span style="color: hsl(120, 100%, 40%);">+  char *mod_name;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     /* When Asterisk shuts down the destructor is called automatically. */</span><br><span style="color: hsl(120, 100%, 40%);">+        if (ast_shutdown_final()) {</span><br><span style="color: hsl(120, 100%, 40%);">+           return;</span><br><span style="color: hsl(120, 100%, 40%);">+       }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   mod_name = get_name_from_resource(resource);</span><br><span style="color: hsl(120, 100%, 40%);">+  if (mod_name) {</span><br><span style="color: hsl(120, 100%, 40%);">+               obj_tmp = ao2_find(info_list, mod_name, OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+              if (obj_tmp) {</span><br><span style="color: hsl(120, 100%, 40%);">+                        ast_module_unregister(obj_tmp->info);</span><br><span style="color: hsl(120, 100%, 40%);">+              }</span><br><span style="color: hsl(120, 100%, 40%);">+             ast_free(mod_name);</span><br><span style="color: hsl(120, 100%, 40%);">+   }</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int module_vector_strcasecmp(struct ast_module *a, struct ast_module *b)</span><br><span> {</span><br><span>       return strcasecmp(a->resource, b->resource);</span><br><span>@@ -597,6 +717,27 @@</span><br><span> </span><br><span>        /* give the module a copy of its own handle, for later use in registrations and the like */</span><br><span>  *((struct ast_module **) &(info->self)) = mod;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_PERMANENT_DLOPEN)</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!info_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+             info_list = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_NOLOCK, 0, NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+                      info_list_obj_cmp_fn);</span><br><span style="color: hsl(120, 100%, 40%);">+        }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (info_list && mod->flags.builtin != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+                struct info_list_obj *obj_tmp = ao2_find(info_list, info->name,</span><br><span style="color: hsl(120, 100%, 40%);">+                    OBJ_SEARCH_KEY);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+            if (!obj_tmp) {</span><br><span style="color: hsl(120, 100%, 40%);">+                       ao2_link(info_list, info_list_obj_alloc(info->name, info));</span><br><span style="color: hsl(120, 100%, 40%);">+                }</span><br><span style="color: hsl(120, 100%, 40%);">+     }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (!info_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+             fprintf(stderr, "Module info list allocation failure during startup.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+           exit(2);</span><br><span style="color: hsl(120, 100%, 40%);">+      }</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> }</span><br><span> </span><br><span> static int module_post_register(struct ast_module *mod)</span><br><span>@@ -843,6 +984,10 @@</span><br><span>                 error = dlerror();</span><br><span>           ast_log(AST_LOG_ERROR, "Failure in dlclose for module '%s': %s\n",</span><br><span>                         S_OR(name, "unknown"), S_OR(error, "Unknown error"));</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_PERMANENT_DLOPEN)</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+              manual_mod_unreg(name);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span>      }</span><br><span> }</span><br><span> </span><br><span>@@ -949,6 +1094,9 @@</span><br><span> </span><br><span>        resource_being_loaded = mod;</span><br><span>         mod->lib = dlopen(filename, flags);</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_PERMANENT_DLOPEN)</span><br><span style="color: hsl(120, 100%, 40%);">+    manual_mod_reg(mod->lib, mod->resource);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span>       if (resource_being_loaded) {</span><br><span>                 struct ast_str *list;</span><br><span>                int c = 0;</span><br><span>@@ -968,6 +1116,9 @@</span><br><span> </span><br><span>                resource_being_loaded = mod;</span><br><span>                 mod->lib = dlopen(filename, RTLD_LAZY | RTLD_LOCAL);</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(HAVE_PERMANENT_DLOPEN)</span><br><span style="color: hsl(120, 100%, 40%);">+           manual_mod_reg(mod->lib, mod->resource);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span>               if (resource_being_loaded) {</span><br><span>                         resource_being_loaded = NULL;</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/11217">change 11217</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/11217"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I86693a0ecf25d5ba81c73773a03df4abc3426875 </div>
<div style="display:none"> Gerrit-Change-Number: 11217 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Sebastian Kemper <sebastian_ml@gmx.net> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>