[PATCH] stdlib: Fix data race in __run_exit_handlers
Vitaly Buka
vitalybuka@google.com
Sun Sep 20 23:37:56 GMT 2020
Read f->func.cxa under the lock.
There is a clear data race:
thread 0: __run_exit_handlers unlock __exit_funcs_lock
thread 1: __internal_atexit locks __exit_funcs_lock
thread 0: f->flavor = ef_free;
thread 1: sees ef_free and use it as new
thread 1: new->func.cxa.fn = (void (*) (void *, int)) func;
thread 1: new->func.cxa.arg = arg;
thread 1: new->flavor = ef_cxa;
thread 0: cxafct = f->func.cxa.fn; // it's wrong fn!
thread 0: cxafct (f->func.cxa.arg, status); // it's wrong arg!
thread 0: goto restart;
thread 0: call the same exit_function again as it's ef_cxa
It's incredibly rare so I can't create reasonable test.
---
stdlib/exit.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)
diff --git a/stdlib/exit.c b/stdlib/exit.c
index 7bca1cdc14..9c79aa4a62 100644
--- a/stdlib/exit.c
+++ b/stdlib/exit.c
@@ -72,44 +72,52 @@ __run_exit_handlers (int status, struct exit_function_list **listp,
struct exit_function *const f = &cur->fns[--cur->idx];
const uint64_t new_exitfn_called = __new_exitfn_called;
- /* Unlock the list while we call a foreign function. */
- __libc_lock_unlock (__exit_funcs_lock);
switch (f->flavor)
{
void (*atfct) (void);
void (*onfct) (int status, void *arg);
void (*cxafct) (void *arg, int status);
+ void *arg;
case ef_free:
case ef_us:
break;
case ef_on:
onfct = f->func.on.fn;
+ arg = f->func.on.arg;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (onfct);
#endif
- onfct (status, f->func.on.arg);
+ /* Unlock the list while we call a foreign function. */
+ __libc_lock_unlock (__exit_funcs_lock);
+ onfct (status, arg);
+ __libc_lock_lock (__exit_funcs_lock);
break;
case ef_at:
atfct = f->func.at;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (atfct);
#endif
+ /* Unlock the list while we call a foreign function. */
+ __libc_lock_unlock (__exit_funcs_lock);
atfct ();
+ __libc_lock_lock (__exit_funcs_lock);
break;
case ef_cxa:
/* To avoid dlclose/exit race calling cxafct twice (BZ 22180),
we must mark this function as ef_free. */
f->flavor = ef_free;
cxafct = f->func.cxa.fn;
+ arg = f->func.cxa.arg;
#ifdef PTR_DEMANGLE
PTR_DEMANGLE (cxafct);
#endif
- cxafct (f->func.cxa.arg, status);
+ /* Unlock the list while we call a foreign function. */
+ __libc_lock_unlock (__exit_funcs_lock);
+ cxafct (arg, status);
+ __libc_lock_lock (__exit_funcs_lock);
break;
}
- /* Re-lock again before looking at global state. */
- __libc_lock_lock (__exit_funcs_lock);
if (__glibc_unlikely (new_exitfn_called != __new_exitfn_called))
/* The last exit function, or another thread, has registered
--
2.28.0.681.g6f77f65b4e-goog
More information about the Libc-alpha
mailing list