Hi,

I try to give it one more attempt with a bit more description about the
bug.

After calling dlclose in _dl_notify_unload_shlib_ group reference counts
are decreased by following the object's grpref-list.  Unfortunately the
references are removed from the list during the graph traversal.

dlclose will run all destructors of the unused objects and tries to
unload all objects reachable from the closed object's child and
grpref-list.  Since the grpref-list references were removed, the unused
destructed object stays in memory as garbage.  Next time when this
object is loaded it is found in the memory and crashes during load.

This patch unloads all unused objects instead of following the closed
object's child and grpref list.

Best regards,
Peter Hajdu

Index: dlfcn.c
===================================================================
RCS file: /cvs/src/libexec/ld.so/dlfcn.c,v
retrieving revision 1.91
diff -u -p -r1.91 dlfcn.c
--- dlfcn.c     19 Sep 2015 20:56:47 -0000      1.91
+++ dlfcn.c     21 Oct 2015 13:52:46 -0000
@@ -302,7 +302,7 @@ _dl_real_close(void *handle)
        object->opencount--;
        _dl_notify_unload_shlib(object);
        _dl_run_all_dtors();
-       _dl_unload_shlib(object);
+       _dl_unload_unused();
        _dl_cleanup_objects();
        return (0);
 }
Index: library.c
===================================================================
RCS file: /cvs/src/libexec/ld.so/library.c,v
retrieving revision 1.71
diff -u -p -r1.71 library.c
--- library.c   16 Jan 2015 16:18:07 -0000      1.71
+++ library.c   21 Oct 2015 13:52:46 -0000
@@ -74,6 +74,22 @@ _dl_unload_shlib(elf_object_t *object)
        }
 }
 
+void
+_dl_unload_unused(void)
+{
+       elf_object_t *obj, *next;
+
+       for (obj = _dl_objects->next; obj != NULL; obj = next) {
+               next = obj->next;
+               if (OBJECT_REF_CNT(obj) != 0 || obj->status & STAT_UNLOADED)
+                       continue;
+               obj->status |= STAT_UNLOADED;
+               _dl_load_list_free(obj->load_list);
+               _dl_munmap((void *)obj->load_base, obj->load_size);
+               _dl_remove_object(obj);
+       }
+}
+
 elf_object_t *
 _dl_tryload_shlib(const char *libname, int type, int flags)
 {
Index: library_mquery.c
===================================================================
RCS file: /cvs/src/libexec/ld.so/library_mquery.c,v
retrieving revision 1.49
diff -u -p -r1.49 library_mquery.c
--- library_mquery.c    22 Jan 2015 05:48:17 -0000      1.49
+++ library_mquery.c    21 Oct 2015 13:52:46 -0000
@@ -79,6 +79,20 @@ _dl_unload_shlib(elf_object_t *object)
        }
 }
 
+void
+_dl_unload_unused(void)
+{
+       elf_object_t *obj, *next;
+
+       for (obj = _dl_objects->next; obj != NULL; obj = next) {
+               next = obj->next;
+               if (OBJECT_REF_CNT(obj) != 0 || obj->status & STAT_UNLOADED)
+                       continue;
+               obj->status |= STAT_UNLOADED;
+               _dl_load_list_free(obj->load_list);
+               _dl_remove_object(obj);
+       }
+}
 
 elf_object_t *
 _dl_tryload_shlib(const char *libname, int type, int flags)
Index: resolve.h
===================================================================
RCS file: /cvs/src/libexec/ld.so/resolve.h,v
retrieving revision 1.73
diff -u -p -r1.73 resolve.h
--- resolve.h   19 Sep 2015 20:56:47 -0000      1.73
+++ resolve.h   21 Oct 2015 13:52:46 -0000
@@ -223,6 +223,7 @@ void _dl_unlink_dlopen(elf_object_t *dep
 void _dl_notify_unload_shlib(elf_object_t *object);
 void _dl_unload_shlib(elf_object_t *object);
 void _dl_unload_dlopen(void);
+void _dl_unload_unused(void);
 
 void _dl_run_all_dtors(void);
 

Reply via email to