Re: [PATCH] newlib: Unlock the mutex while calling atexit()'ed functions
Corinna Vinschen <[email protected]> Mon, 1 Dec 2025 17:48:44 +0100
| Newsgroups | gmane.comp.lib.newlib |
|---|---|
| Message-ID | <[email protected]> |
Comparing with GLibc, this looks right to me. Ok with you, Sebastian?
Thanks,
Corinna
On Nov 29 20:38, Takashi Yano wrote:
> Hi Corinna, Sebasitian,
>
> On Fri, 28 Nov 2025 13:21:35 +0100
> Corinna Vinschen wrote:
> > Hi Takashi, Hi Sebastian,
> >
> > did you discuss this in PM, by any chance? Is there any difference in
> > GLibc or Darwin which explains how this potential problem is fixed
> > there?
>
> In glibc, the value of fnargs, etc is just copyied to local variable.
> So, I think the followign patch is also OK for newlib.
>
> diff --git a/newlib/libc/stdlib/__call_atexit.c b/newlib/libc/stdlib/__call_atexit.c
> index 44f1f6acc..15ecda343 100644
> --- a/newlib/libc/stdlib/__call_atexit.c
> +++ b/newlib/libc/stdlib/__call_atexit.c
> @@ -93,6 +93,8 @@ __call_exitprocs (int code, void *d)
> for (n = p->_ind - 1; n >= 0; n--)
> {
> int ind;
> + __ULong fntypes, is_cxa;
> + void *fnarg;
>
> i = 1 << n;
>
> @@ -114,18 +116,22 @@ __call_exitprocs (int code, void *d)
>
> ind = p->_ind;
>
> + fntypes = args->_fntypes;
> + is_cxa = args->_is_cxa;
> + fnarg = args->_fnargs[n];
> +
> #ifndef __SINGLE_THREAD__
> /* Unlock __atexit_recursive_mutex; otherwise, the function fn() may
> deadlock if it waits for another thread which calls atexit(). */
> __lock_release_recursive(__atexit_recursive_mutex);
> #endif
> /* Call the function. */
> - if (!args || (args->_fntypes & i) == 0)
> + if (!args || (fntypes & i) == 0)
> fn ();
> - else if ((args->_is_cxa & i) == 0)
> - (*((void (*)(int, void *)) fn))(code, args->_fnargs[n]);
> + else if ((is_cxa & i) == 0)
> + (*((void (*)(int, void *)) fn))(code, fnarg);
> else
> - (*((void (*)(void *)) fn))(args->_fnargs[n]);
> + (*((void (*)(void *)) fn))(fnarg);
> #ifndef __SINGLE_THREAD__
> __lock_acquire_recursive(__atexit_recursive_mutex);
> #endif
>
>
> > On Nov 26 05:17, Sebastian Huber wrote:
> > > Hello Takashi Yano,
> > >
> > > I have some questions to the change.
> > >
> > > ----- Am 18. Nov 2025 um 14:31 schrieb Takashi Yano [email protected]:
> > >
> > > > The atexit()'ed function may deadlock if it waits for another thread
> > > > which calls atexit() in the current __call_atexit.c code. This patch
> > > > unlock __atexit_recursive_mutex while calling atexit()'ed functions
> > > > to avoid the deadlock mentioned above. glibc and Darwin do the same,
> > > > so it sounds reasonable.
> > > >
> > > > Addresses: https://cygwin.com/pipermail/cygwin/2025-October/258930.html
> > > > Reported-by: Tomohiro Kashiwada <[email protected]>
> > > > Reviewed-by:
> > > > Signed-off-by: Takashi Yano <[email protected]>
> > > > ---
> > > > newlib/libc/stdlib/__call_atexit.c | 8 ++++++++
> > > > 1 file changed, 8 insertions(+)
> > > >
> > > > diff --git a/newlib/libc/stdlib/__call_atexit.c
> > > > b/newlib/libc/stdlib/__call_atexit.c
> > > > index 710440389..44f1f6acc 100644
> > > > --- a/newlib/libc/stdlib/__call_atexit.c
> > > > +++ b/newlib/libc/stdlib/__call_atexit.c
> > > > @@ -114,6 +114,11 @@ __call_exitprocs (int code, void *d)
> > > >
> > > > ind = p->_ind;
> > > >
> > > > +#ifndef __SINGLE_THREAD__
> > > > + /* Unlock __atexit_recursive_mutex; otherwise, the function fn() may
> > > > + deadlock if it waits for another thread which calls atexit(). */
> > > > + __lock_release_recursive(__atexit_recursive_mutex);
> > > > +#endif
> > >
> > > Here, a potentially shared structure is used through the args pointer. If some other thread calls exit() or atexit() concurrently, then this could result in a use of altered or freed memory. The data of the structure referenced by args should be first copied to local variables. Also I think that the deallocation should be done before we release the lock. We probably also have to restart the process after each handler call unconditionally.
>
> Thanks for pointing this out.
>
> What do you think of the patch above?
>
> > > > /* Call the function. */
> > > > if (!args || (args->_fntypes & i) == 0)
> > > > fn ();
> > > > @@ -121,6 +126,9 @@ __call_exitprocs (int code, void *d)
> > > > (*((void (*)(int, void *)) fn))(code, args->_fnargs[n]);
> > > > else
> > > > (*((void (*)(void *)) fn))(args->_fnargs[n]);
> > > > +#ifndef __SINGLE_THREAD__
> > > > + __lock_acquire_recursive(__atexit_recursive_mutex);
> > > > +#endif
> > > >
> > > > /* The function we called call atexit and registered another
> > > > function (or functions). Call these new functions before
> > > > --
> > > > 2.51.0
>
> --
> Takashi Yano <[email protected]>