OSDN Git Service

dlsym: fix the race when snd_libdir_origin is freed
authorJaroslav Kysela <perex@perex.cz>
Thu, 18 Jun 2020 06:41:02 +0000 (08:41 +0200)
committerJaroslav Kysela <perex@perex.cz>
Thu, 18 Jun 2020 06:41:16 +0000 (08:41 +0200)
snd_dlobj_cache_cleanup() function frees snd_libdir_origin, but
the pointer may be used again in snd_dlpath().

BugLink: https://bugzilla.redhat.com/show_bug.cgi?id=1847508
Signed-off-by: Jaroslav Kysela <perex@perex.cz>
src/dlmisc.c

index ca94b12..1a60e0d 100644 (file)
 #endif
 #include <limits.h>
 
+#if defined(HAVE_LIBDL) && defined(__GLIBC__) && !defined(__UCLIBC__)
+#define DL_ORIGIN_AVAILABLE 1
+#endif
+
 #ifndef DOC_HIDDEN
 #ifndef PIC
 struct snd_dlsym_link *snd_dlsym_start = NULL;
 #endif
+#ifdef DL_ORIGIN_AVAILABLE
+static int snd_libdir_plugin_dir_set = 0;
 static char *snd_libdir_origin = NULL;
 #endif
+#endif
 
-#ifdef HAVE_LIBPTHREAD
+#if defined(DL_ORIGIN_AVAILABLE) && defined(HAVE_LIBPTHREAD)
 static pthread_mutex_t snd_dlpath_mutex = PTHREAD_MUTEX_INITIALIZER;
 
 static inline void snd_dlpath_lock(void)
@@ -68,11 +75,9 @@ static inline void snd_dlpath_unlock(void) {}
  */
 int snd_dlpath(char *path, size_t path_len, const char *name)
 {
-#ifdef HAVE_LIBDL
-#if defined(__GLIBC__) && !defined(__UCLIBC__)
-       static int plugin_dir_set = 0;
+#ifdef DL_ORIGIN_AVAILABLE
        snd_dlpath_lock();
-       if (!plugin_dir_set) {
+       if (!snd_libdir_plugin_dir_set) {
                struct link_map *links;
                Dl_info info;
                char origin[PATH_MAX];
@@ -83,15 +88,17 @@ int snd_dlpath(char *path, size_t path_len, const char *name)
                        if (access(path, X_OK) == 0)
                                snd_libdir_origin = strdup(origin);
                }
-               plugin_dir_set = 1;
+               snd_libdir_plugin_dir_set = 1;
        }
-       snd_dlpath_unlock();
-#endif
-#endif
-       if (snd_libdir_origin)
+       if (snd_libdir_origin) {
                snprintf(path, path_len, "%s/alsa-lib/%s", snd_libdir_origin, name);
-       else
+       } else {
                snprintf(path, path_len, "%s/%s", ALSA_PLUGIN_DIR, name);
+       }
+       snd_dlpath_unlock();
+#else
+       snprintf(path, path_len, "%s/%s", ALSA_PLUGIN_DIR, name);
+#endif
        return 0;
 }
 
@@ -440,7 +447,13 @@ void snd_dlobj_cache_cleanup(void)
                free((void *)c->lib); /* shut up gcc warning */
                free(c);
        }
-       free(snd_libdir_origin);
        snd_dlobj_unlock();
+#ifdef DL_ORIGIN_AVAILABLE
+       snd_dlpath_lock();
+       snd_libdir_plugin_dir_set = 0;
+       free(snd_libdir_origin);
+       snd_libdir_origin = NULL;
+       snd_dlpath_unlock();
+#endif
 }
 #endif