misc/151861: dlclose() of library causes separately opened libraries to unload as well

Arjan van Leeuwen freebsd-maintainer at opera.com
Mon Nov 1 12:20:12 UTC 2010


>Number:         151861
>Category:       misc
>Synopsis:       dlclose() of library causes separately opened libraries to unload as well
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Mon Nov 01 12:20:11 UTC 2010
>Closed-Date:
>Last-Modified:
>Originator:     Arjan van Leeuwen
>Release:        FreeBSD 8.1-RELEASE amd64
>Organization:
Opera Software ASA 
>Environment:


System: FreeBSD 8.1-RELEASE #0: Mon Jul 19 02:36:49 UTC 2010
    root at mason.cse.buffalo.edu:/usr/obj/usr/src/sys/GENERIC



>Description:


Assume we have a library liba.so, containing a function a(), and a library libb.so, containing function b(). liba.so needs functionality from libb.so, so liba.so links in libb.so.

An application doesn't know about the relation between these libraries, but needs to call a() and b(). It dlopen()s libb.so and obtains a pointer to b(), and it dlopen()s liba.so and obtains a pointer to a().

As soon as the application doesn't need a() anymore, it dlclose()s liba.so.

Expected result: the pointer to b() is still valid and can be called
Actual result: the pointer to b() has become invalid, even though the application did not dlclose() the handle to libb.so. On calling b(), the application crashes with a segmentation fault.

Note: this bug is the cause of a FreeBSD-specific crash in Opera (ports/150117).


>How-To-Repeat:


Extract the attached shar archive and execute 'make test'.

This will cause a crash on FreeBSD, and will print 'success' on Linux.


>Fix:


--- dlopentest.sh begins here ---
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	dlopentest
#	dlopentest/Makefile
#	dlopentest/a.h
#	dlopentest/a.c
#	dlopentest/b.h
#	dlopentest/b.c
#	dlopentest/main.c
#
echo c - dlopentest
mkdir -p dlopentest > /dev/null 2>&1
echo x - dlopentest/Makefile
sed 's/^X//' >dlopentest/Makefile << '2075fca623562c5bd8776415e7b01bf6'
Xall: dlopentest liba.so
X
Xlibb.so: b.c
X	gcc -g -fPIC -shared -o libb.so b.c
X
Xliba.so: libb.so a.c
X	gcc -g -L. -fPIC -shared -lb -o liba.so a.c
X
Xdlopentest: main.c
X	gcc -g -o dlopentest main.c
X
Xclean:
X	rm -f dlopentest liba.so libb.so
X
Xtest: all
X	LD_LIBRARY_PATH=. ./dlopentest
2075fca623562c5bd8776415e7b01bf6
echo x - dlopentest/a.h
sed 's/^X//' >dlopentest/a.h << 'fc414acea66cdc81629a88bfee826a11'
Xconst char* a();
fc414acea66cdc81629a88bfee826a11
echo x - dlopentest/a.c
sed 's/^X//' >dlopentest/a.c << '0ad10ab266ed1f8ba3b38758bb80e7f7'
X#include "a.h"
X#include "b.h"
X
Xconst char* a()
X{
X  return b();
X}
0ad10ab266ed1f8ba3b38758bb80e7f7
echo x - dlopentest/b.h
sed 's/^X//' >dlopentest/b.h << '318c19e5810f4ba86fe6f909829d72e7'
Xconst char* b();
318c19e5810f4ba86fe6f909829d72e7
echo x - dlopentest/b.c
sed 's/^X//' >dlopentest/b.c << 'f9fa01da76fe4015102bc9a5d0c22c09'
X#include "b.h"
X
Xconst char* b()
X{
X  return "b";
X}
f9fa01da76fe4015102bc9a5d0c22c09
echo x - dlopentest/main.c
sed 's/^X//' >dlopentest/main.c << 'a3b6e3077e820f950e38c9b3614efead'
X#include <stdio.h>
X#include <dlfcn.h>
X#include <stdlib.h>
X
Xvoid* LoadLibrary(const char* library)
X{
X  void* handle = dlopen(library, RTLD_LAZY);
X  if (!handle)
X  {
X	fprintf(stderr, "can't load %s: %s\n", library, dlerror());
X	exit(1);
X  }
X  return handle;
X}
X
Xvoid* LoadFunction(void* handle, const char* function)
X{
X  void* fptr = dlsym(handle, function);
X  if (!fptr)
X  {
X	fprintf(stderr, "can't resolve function %s: %s\n", function, dlerror());
X	exit(1);
X  }
X  return fptr;
X}
X
Xint main(int argc, char* argv[])
X{
X  void* handle_a = LoadLibrary("liba.so");
X  const char* (*a)() = LoadFunction(handle_a, "a");
X
X  printf("from a: %s\n", a());
X
X  void* handle_b = LoadLibrary("libb.so");
X  const char* (*b)() = LoadFunction(handle_b, "b");
X
X  printf("from b: %s\n", b());
X
X  dlclose(handle_a);
X
X  printf("from b after dlclosing a: %s\n", b());
X
X  dlclose(handle_b);
X
X  printf("success\n");
X
X  return 0;
X}
a3b6e3077e820f950e38c9b3614efead
exit

--- dlopentest.sh ends here ---



>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list