Bug 115631 - [libc] [request] make dlclose(3) atexit-aware
Summary: [libc] [request] make dlclose(3) atexit-aware
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 6.2-STABLE
Hardware: Any Any
: Normal Affects Only Me
Assignee: freebsd-bugs (Nobody)
URL:
Keywords: patch
Depends on:
Blocks:
 
Reported: 2007-08-19 22:00 UTC by Mikhail T.
Modified: 2018-11-24 06:55 UTC (History)
2 users (show)

See Also:


Attachments
Patch (4.37 KB, patch)
2015-02-20 18:45 UTC, Pedro F. Giffuni
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Mikhail T. 2007-08-19 22:00:03 UTC
	Currently, an application can shoot itself in the tail by
	registering a symbol with atexit and then dlclose-ing the
	library, which provides the symbol.

	Programs aren't supposed to that, but sometimes they do --
	most notably ImageMagick, which dlopens libjasper, which
	registers its own clean-up routine with atexit.

	The proposed patch will make such bugs easier to diagnose --
	the ImageMagick problem (crash upon exiting) was mystifying
	people for years...

Fix: 

The up-to-date version of the patch can be found here:

		http://aldan.algebra.com/~mi/dlclose-atexit.patch

	It exposes the __atexit pointer defined in atexit.c and its
	type and modifies dlclose to scan through all listed functions
	looking for those, that belong to the object being dlclosed.

	The method to check whether a symbol belongs to an object
	is to check, whether the address is between mapbase and
	mapbase+mapsize of the entry.

	What could be improved is the addition of a an addr_to_name
	lookup -- to print the _name_ of the function found to be
	registered with atexit, rather than merely the function's
	address.
Comment 1 Šimun Mikecin 2007-09-13 11:52:08 UTC
--- Gordon Stratton <tsr2600@gmail.com> wrote:
> I did some testing, and the patch does seem to help. However, there seem
> to be a couple cases where a segmentation fault still occurs.  The first
> is when I attempt to open a file with PHP that does not exist:
> 
> $ php doesntexist
> Could not open input file: doesntexist
> Segmentation fault
> 
> The second case involves a different PHP extension, pdo_dblib.so (the
> OCI8 extensions seem to work OK now):
> 
> (gdb) bt
> #0  0x2912313c in ?? ()
> #1  0x285855c7 in pthread_mutex_lock () from /lib/libc.so.6
> #2  0x28f09bf3 in dbexit () from /usr/local/lib/libsybdb.so.5
> #3  0x28efb86b in zm_shutdown_pdo_dblib () from
> /usr/local/lib/php/20060613-debug/pdo_dblib.so
> #4  0x0816ceb0 in module_destructor (module=0x832a300) at
> /usr/ports/lang/php5/work/php-5.2.3/Zend/zend_API.c:1894
> #5  0x0817171a in zend_hash_apply_deleter (ht=0x826c620, p=0x8330280)
> at /usr/ports/lang/php5/work/php-5.2.3/Zend/zend_hash.c:611
> #6  0x08171873 in zend_hash_graceful_reverse_destroy (ht=0x826c620) at
> /usr/ports/lang/php5/work/php-5.2.3/Zend/zend_hash.c:646
> #7  0x08166ca7 in zend_shutdown () at
> /usr/ports/lang/php5/work/php-5.2.3/Zend/zend.c:733
> #8  0x081205f5 in php_module_shutdown () at
> /usr/ports/lang/php5/work/php-5.2.3/main/main.c:1684
> #9  0x081ce73b in main (argc=20, argv=0xbfbfeca0) at
> /usr/ports/lang/php5/work/php-5.2.3/sapi/cli/php_cli.c:1333

Have those two cases (doesntexist and pdo_dblib) segfaulted without this patch?



       
____________________________________________________________________________________
Yahoo! oneSearch: Finally, mobile search 
that gives answers, not web links. 
http://mobile.yahoo.com/mobileweb/onesearch?refer=1ONXIC
Comment 2 tsr2600 2007-09-13 18:34:54 UTC
> On 9/13/07, Simun Mikecin <numisemis@yahoo.com> wrote:
> Have those two cases (doesntexist and pdo_dblib) segfaulted without this patch?

(CC'ing lists this time, my apologies)

doesntexist segfaults without the patch for sure, I can't say for sure
about pdo_dblib.. I never _saw_ it but it's possible that other modules
were segfaulting first so it never got a chance to segfault. There is at
least one case now where PHP does not segfault with the patch where it
did before.

Gordon
Comment 3 Mikhail T. 2011-07-13 14:12:58 UTC
This remains a problem on 8.2 today. multimedia/vlc port always dumps 
core on exit and the core's stack always contains __cxa_atexit()

The patch I submitted with this PR 4 years ago still applies.

    -mi
Comment 4 Pedro F. Giffuni freebsd_committer freebsd_triage 2015-02-20 18:45:37 UTC
Created attachment 153231 [details]
Patch

Upload the patch so that it's easier to review and doesn't got lost.
Comment 5 Eitan Adler freebsd_committer freebsd_triage 2018-05-20 23:53:29 UTC
For bugs matching the following conditions:
- Status == In Progress
- Assignee == "bugs@FreeBSD.org"
- Last Modified Year <= 2017

Do
- Set Status to "Open"
Comment 6 Bryan Drewery freebsd_committer freebsd_triage 2018-11-21 00:04:48 UTC
Old discussion here: https://freebsd-arch.freebsd.narkive.com/zzyHX3Bw/dlclose-vs-atexit
glibc handles this:
   Linux notes
       Since glibc 2.2.3, atexit() (and on_exit(3)) can be used within a shared library to establish functions that are called when the shared library is unloaded.
Comment 7 Bryan Drewery freebsd_committer freebsd_triage 2018-11-21 00:05:42 UTC
The patch here is interesting but not something that should be committed. A real
solution should be committed that prevents a crash.
Perhaps making atexit() aware that it is being called from a dso and properly
registering that dso in atexit_register(), etc.
Comment 8 Bryan Drewery freebsd_committer freebsd_triage 2018-11-21 00:51:58 UTC
*Very naive and untested*.
It seems like we need something like this:

diff --git lib/libc/stdlib/atexit.c lib/libc/stdlib/atexit.c
index bc9ad3ebd7bf..8df6cd4e31b0 100644
--- lib/libc/stdlib/atexit.c
+++ lib/libc/stdlib/atexit.c
@@ -125,6 +125,8 @@ atexit_register(struct atexit_fn *fptr)
        return 0;
 }

+extern void *__dso_handle __hidden;
+
 /*
  * Register a function to be performed at exit.
  */
@@ -137,7 +139,7 @@ atexit(void (*func)(void))
        fn.fn_type = ATEXIT_FN_STD;
        fn.fn_ptr.std_func = func;
        fn.fn_arg = NULL;
-       fn.fn_dso = NULL;
+       fn.fn_dso = __dso_handle;

        error = atexit_register(&fn);
        return (error);