summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2013-04-15 18:12:17 +0200
committerTakashi Iwai <tiwai@suse.de>2013-04-15 18:23:46 +0200
commite1e40c25535af35fa5cdf7ffc95a01fbff098ddd (patch)
treec18112f28c444485ce391dff25fd0b7089d26e04
parent812e4b0c5ba74fecb6e086cadeb10e19df0e1e88 (diff)
downloadalsa-lib-e1e40c25535af35fa5cdf7ffc95a01fbff098ddd.tar.gz
Fix doubly call of dlclose() in dlobj caching code
When multiple dlobj_cache items point to the same dlobj, dlclose() may be called wrongly multiple times when these items are cleared, because we manage the dlobj_cache list as a flat list. This results in a bad segfault we've seen in openal-soft, for example. For fixing this, we need the refcounting of dlobj itself. But, in this case, we don't have to manage yet another list, since dlopen() does a proper refcounting by itself. That is, we can just call always dlopen() at each time a new function is assigned, and also call dlclose() for each released dlobj_cache item at cleanup. Bugzilla: https://bugzilla.novell.com/show_bug.cgi?id=814250 Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--src/dlmisc.c31
1 files changed, 11 insertions, 20 deletions
diff --git a/src/dlmisc.c b/src/dlmisc.c
index 37883822..2de02340 100644
--- a/src/dlmisc.c
+++ b/src/dlmisc.c
@@ -208,8 +208,7 @@ void *snd_dlobj_cache_get(const char *lib, const char *name,
{
struct list_head *p;
struct dlobj_cache *c;
- void *func, *dlobj = NULL;
- int dlobj_close = 0;
+ void *func, *dlobj;
snd_dlobj_lock();
list_for_each(p, &pcm_dlobj_list) {
@@ -220,7 +219,6 @@ void *snd_dlobj_cache_get(const char *lib, const char *name,
continue;
if (!lib && c->lib)
continue;
- dlobj = c->dlobj;
if (strcmp(c->name, name) == 0) {
c->refcnt++;
func = c->func;
@@ -228,17 +226,16 @@ void *snd_dlobj_cache_get(const char *lib, const char *name,
return func;
}
}
+
+ dlobj = snd_dlopen(lib, RTLD_NOW);
if (dlobj == NULL) {
- dlobj = snd_dlopen(lib, RTLD_NOW);
- if (dlobj == NULL) {
- if (verbose)
- SNDERR("Cannot open shared library %s",
+ if (verbose)
+ SNDERR("Cannot open shared library %s",
lib ? lib : "[builtin]");
- snd_dlobj_unlock();
- return NULL;
- }
- dlobj_close = 1;
+ snd_dlobj_unlock();
+ return NULL;
}
+
func = snd_dlsym(dlobj, name, version);
if (func == NULL) {
if (verbose)
@@ -257,8 +254,7 @@ void *snd_dlobj_cache_get(const char *lib, const char *name,
free((void *)c->lib);
free(c);
__err:
- if (dlobj_close)
- snd_dlclose(dlobj);
+ snd_dlclose(dlobj);
snd_dlobj_unlock();
return NULL;
}
@@ -298,16 +294,11 @@ void snd_dlobj_cache_cleanup(void)
struct list_head *p, *npos;
struct dlobj_cache *c;
- /* clean up caches only when really no user is present */
snd_dlobj_lock();
- list_for_each(p, &pcm_dlobj_list) {
- c = list_entry(p, struct dlobj_cache, list);
- if (c->refcnt)
- goto unlock;
- }
-
list_for_each_safe(p, npos, &pcm_dlobj_list) {
c = list_entry(p, struct dlobj_cache, list);
+ if (c->refcnt)
+ continue;
list_del(p);
snd_dlclose(c->dlobj);
free((void *)c->name); /* shut up gcc warning */