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

Kostik Belousov kostikbel at gmail.com
Tue Nov 2 20:20:17 UTC 2010


The following reply was made to PR misc/151861; it has been noted by GNATS.

From: Kostik Belousov <kostikbel at gmail.com>
To: Jaakko Heinonen <jh at freebsd.org>
Cc: Arjan van Leeuwen <freebsd-maintainer at opera.com>, bug-followup at freebsd.org,
        kan at freebsd.org
Subject: Re: misc/151861: dlclose() of library causes separately opened libraries to unload as well
Date: Tue, 2 Nov 2010 22:13:06 +0200

 --kQl6KHw6oLQq4hVK
 Content-Type: text/plain; charset=us-ascii
 Content-Disposition: inline
 Content-Transfer-Encoding: quoted-printable
 
 On Tue, Nov 02, 2010 at 07:36:54PM +0200, Jaakko Heinonen wrote:
 > On 2010-11-01, Arjan van Leeuwen wrote:
 > > 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.
 > >=20
 > > 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().
 > >=20
 > > As soon as the application doesn't need a() anymore, it dlclose()s
 > > liba.so.
 > >=20
 > > 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.
 > >=20
 > > Extract the attached shar archive and execute 'make test'.
 >=20
 > Thank you for providing the test case.
 >=20
 > > This will cause a crash on FreeBSD, and will print 'success' on Linux.
 >=20
 > There is a problem with reference counting in dlopen(). If an object has
 > been loaded by load_needed_objects() its dagmembers list may be empty
 > after loading. If the list is empty, the ref_dag() call done for already
 > loaded objects in dlopen() doesn't have effect.
 >=20
 > Here is a patch to demonstrate the problem. The test passes with the
 > patch applied.
 >=20
 > %%%
 > Index: libexec/rtld-elf/rtld.c
 > =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
 =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
 > --- libexec/rtld-elf/rtld.c	(revision 214676)
 > +++ libexec/rtld-elf/rtld.c	(working copy)
 > @@ -2046,7 +2046,10 @@ dlopen(const char *name, int mode)
 >  	} else {
 > =20
 >  	    /* Bump the reference counts for objects on this DAG. */
 > -	    ref_dag(obj);
 > +	    if (STAILQ_EMPTY(&obj->dagmembers))
 > +		init_dag(obj);
 > +	    else
 > +		ref_dag(obj);
 > =20
 >  	    if (ld_tracing)
 >  		goto trace;
 > %%%
 >=20
 > I have cc'd kan@ and kib at . Do you have ideas how to fix this correctly?
 
 Why do you think that your patch is not correct ?
 I feel that more explicit handling of the state of the DAG is cleaner.
 
 diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c
 index 8bc8692..f1ffc3e 100644
 --- a/libexec/rtld-elf/rtld.c
 +++ b/libexec/rtld-elf/rtld.c
 @@ -1275,8 +1275,11 @@ init_dag(Obj_Entry *root)
  {
      DoneList donelist;
 =20
 +    if (root->dag_inited)
 +	    return;
      donelist_init(&donelist);
      init_dag1(root, root, &donelist);
 +    root->dag_inited =3D true;
  }
 =20
  static void
 @@ -2045,8 +2048,16 @@ dlopen(const char *name, int mode)
  	    }
  	} else {
 =20
 -	    /* Bump the reference counts for objects on this DAG. */
 -	    ref_dag(obj);
 +	    /*
 +	     * Bump the reference counts for objects on this DAG.  If
 +	     * this is the first dlopen() call for the object that was
 +	     * already loaded as a dependency, initialize the dag
 +	     * starting at it.
 +	     */
 +	    if (obj->dl_refcount =3D=3D 1)
 +		init_dag(obj);
 +	    else
 +		ref_dag(obj);
 =20
  	    if (ld_tracing)
  		goto trace;
 diff --git a/libexec/rtld-elf/rtld.h b/libexec/rtld-elf/rtld.h
 index 6bae2f0..faa84e2 100644
 --- a/libexec/rtld-elf/rtld.h
 +++ b/libexec/rtld-elf/rtld.h
 @@ -222,6 +222,7 @@ typedef struct Struct_Obj_Entry {
      bool ref_nodel : 1;		/* Refcount increased to prevent dlclose */
      bool init_scanned: 1;	/* Object is already on init list. */
      bool on_fini_list: 1;	/* Object is already on fini list. */
 +    bool dag_inited : 1;	/* Object has its DAG initialized. */
 =20
      struct link_map linkmap;	/* For GDB and dlinfo() */
      Objlist dldags;		/* Object belongs to these dlopened DAGs (%) */
 
 --kQl6KHw6oLQq4hVK
 Content-Type: application/pgp-signature
 Content-Disposition: inline
 
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.11 (FreeBSD)
 
 iEYEARECAAYFAkzQcNEACgkQC3+MBN1Mb4jABgCg7KYATJ1CUDLOwOPHYufcx3P6
 lXEAnjRvZPn3H1yYOZnFO9htGSIFywnj
 =Kfrv
 -----END PGP SIGNATURE-----
 
 --kQl6KHw6oLQq4hVK--


More information about the freebsd-bugs mailing list