diff options
| author | Denys Vlasenko <dvlasenk@redhat.com> | 2011-09-15 16:59:21 +0200 | 
|---|---|---|
| committer | Denys Vlasenko <dvlasenk@redhat.com> | 2011-09-15 16:59:21 +0200 | 
| commit | 82f8d0bce10403deab704871e638edc24e7933ee (patch) | |
| tree | 1afb443b538e0dcdaed4908dac4872a73366f3bb /libc/stdlib | |
| parent | 200895dab88678b5b4e7d1dac9172be23e92eb31 (diff) | |
Defeat compiler optimization which assumes function addresses are never NULL
From email:
A warning for people who can be hit by the same or similar issue:
gcc 4.1.2 with -march=i486 here with -Os and even with -O2 or -O
is "optimizing away" the check
       if (_stdio_term)
in libc/stdlib/_atexit.c
which results in a "call 0" and a segfault at exit
if you do not happen to link in stdio.
Signed-off-by: Denys Vlasenko <dvlasenk@redhat.com>
Diffstat (limited to 'libc/stdlib')
| -rw-r--r-- | libc/stdlib/_atexit.c | 49 | ||||
| -rw-r--r-- | libc/stdlib/abort.c | 13 | 
2 files changed, 42 insertions, 20 deletions
diff --git a/libc/stdlib/_atexit.c b/libc/stdlib/_atexit.c index 0d420d3c9..48b97ffd2 100644 --- a/libc/stdlib/_atexit.c +++ b/libc/stdlib/_atexit.c @@ -48,9 +48,9 @@ __UCLIBC_MUTEX_EXTERN(__atexit_lock); -typedef void (*aefuncp) (void);         /* atexit function pointer */ -typedef void (*oefuncp) (int, void *);  /* on_exit function pointer */ -typedef void (*cxaefuncp) (void *);     /* __cxa_atexit function pointer */ +typedef void (*aefuncp)(void);         /* atexit function pointer */ +typedef void (*oefuncp)(int, void *);  /* on_exit function pointer */ +typedef void (*cxaefuncp)(void *);     /* __cxa_atexit function pointer */  typedef enum {      ef_free,      ef_in_use, @@ -59,7 +59,7 @@ typedef enum {  } ef_type; /* exit function types */  /* this is in the L_exit object */ -extern void (*__exit_cleanup) (int) attribute_hidden; +extern void (*__exit_cleanup)(int) attribute_hidden;  /* these are in the L___do_exit object */  extern int __exit_slots attribute_hidden; @@ -88,10 +88,10 @@ extern struct exit_function *__exit_function_table attribute_hidden;  #else  extern struct exit_function __exit_function_table[__UCLIBC_MAX_ATEXIT] attribute_hidden;  #endif -extern struct exit_function *__new_exitfn (void) attribute_hidden; +extern struct exit_function *__new_exitfn(void) attribute_hidden;  /* this is in the L___cxa_atexit object */ -extern int __cxa_atexit (cxaefuncp, void *arg, void *dso_handle); +extern int __cxa_atexit(cxaefuncp, void *arg, void *dso_handle);  /* remove old_atexit after 0.9.29 */ @@ -153,7 +153,7 @@ int on_exit(oefuncp func, void *arg)  #ifdef L___cxa_atexit  libc_hidden_proto(__cxa_atexit) -int __cxa_atexit (cxaefuncp func, void *arg, void *dso_handle) +int __cxa_atexit(cxaefuncp func, void *arg, void *dso_handle)  {      struct exit_function *efp; @@ -183,8 +183,8 @@ libc_hidden_def(__cxa_atexit)   *  with the same dso handle.  Otherwise, if D is NULL, call all of the   *  registered handlers.   */ -void __cxa_finalize (void *dso_handle); -void __cxa_finalize (void *dso_handle) +void __cxa_finalize(void *dso_handle); +void __cxa_finalize(void *dso_handle)  {      struct exit_function *efp;      int exit_count_snapshot = __exit_count; @@ -214,7 +214,7 @@ void __cxa_finalize (void *dso_handle)       */  #ifdef UNREGISTER_ATFORK      if (d != NULL) { -        UNREGISTER_ATFORK (d); +        UNREGISTER_ATFORK(d);      }  #endif  #endif @@ -243,7 +243,7 @@ struct exit_function attribute_hidden *__new_exitfn(void)  #ifdef __UCLIBC_DYNAMIC_ATEXIT__      /* If we are out of function table slots, make some more */      if (__exit_slots < __exit_count+1) { -        efp=realloc(__exit_function_table, +        efp = realloc(__exit_function_table,                      (__exit_slots+20)*sizeof(struct exit_function));          if (efp == NULL) {              __set_errno(ENOMEM); @@ -279,18 +279,18 @@ void __exit_handler(int status)  	struct exit_function *efp;  	/* In reverse order */ -	while ( __exit_count ) { +	while (__exit_count) {  		efp = &__exit_function_table[--__exit_count];  		switch (efp->type) {  		case ef_on_exit:  			if (efp->funcs.on_exit.func) { -				(efp->funcs.on_exit.func) (status, efp->funcs.on_exit.arg); +				(efp->funcs.on_exit.func)(status, efp->funcs.on_exit.arg);  			}  			break;                  case ef_cxa_atexit:                          if (efp->funcs.cxa_atexit.func) {                                  /* glibc passes status too, but that's not in the prototype */ -                                (efp->funcs.cxa_atexit.func) (efp->funcs.cxa_atexit.arg); +                                (efp->funcs.cxa_atexit.func)(efp->funcs.cxa_atexit.arg);                          }                          break;  		} @@ -303,8 +303,19 @@ void __exit_handler(int status)  #endif  #ifdef L_exit +/* Defeat compiler optimization which assumes function addresses are never NULL */ +static __always_inline int not_null_ptr(const void *p) +{ +	const void *q; +	__asm__ ("" +		: "=r" (q) /* output */ +		: "0" (p) /* input */ +	); +	return q != 0; +} +  extern void weak_function _stdio_term(void) attribute_hidden; -attribute_hidden void (*__exit_cleanup) (int) = 0; +attribute_hidden void (*__exit_cleanup)(int) = 0;  __UCLIBC_MUTEX_INIT(__atexit_lock, PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP);  extern void __uClibc_fini(void); @@ -317,19 +328,19 @@ void exit(int rv)  {  	/* Perform exit-specific cleanup (atexit and on_exit) */  	__UCLIBC_MUTEX_LOCK(__atexit_lock); -	if (__exit_cleanup) { +	if (not_null_ptr(__exit_cleanup)) {  		__exit_cleanup(rv);  	}  	__UCLIBC_MUTEX_UNLOCK(__atexit_lock);  	__uClibc_fini(); -    /* If we are using stdio, try to shut it down.  At the very least, +	/* If we are using stdio, try to shut it down.  At the very least,  	 * this will attempt to commit all buffered writes.  It may also  	 * unbuffer all writable files, or close them outright.  	 * Check the stdio routines for details. */ -	if (_stdio_term) -	    _stdio_term(); +	if (not_null_ptr(_stdio_term)) +		_stdio_term();  	_exit(rv);  } diff --git a/libc/stdlib/abort.c b/libc/stdlib/abort.c index 3cc796370..a5bac461a 100644 --- a/libc/stdlib/abort.c +++ b/libc/stdlib/abort.c @@ -28,6 +28,17 @@ Cambridge, MA 02139, USA.  */ +/* Defeat compiler optimization which assumes function addresses are never NULL */ +static __always_inline int not_null_ptr(const void *p) +{ +	const void *q; +	__asm__ ("" +		: "=r" (q) /* output */ +		: "0" (p) /* input */ +	); +	return q != 0; +} +  /* Our last ditch effort to commit suicide */  #ifdef __UCLIBC_ABORT_INSTRUCTION__  # define ABORT_INSTRUCTION __asm__(__UCLIBC_ABORT_INSTRUCTION__) @@ -68,7 +79,7 @@ void abort(void)  			 * this will attempt to commit all buffered writes.  It may also  			 * unbuffer all writable files, or close them outright.  			 * Check the stdio routines for details. */ -			if (_stdio_term) { +			if (not_null_ptr(_stdio_term)) {  				_stdio_term();  			}  #endif  | 
