How to use backtrace() safely in signal handler.

Paul Guo paulguo@gmail.com
Fri Dec 6 02:47:00 GMT 2019


On Thu, Dec 5, 2019 at 10:35 PM Florian Weimer <fweimer@redhat.com> wrote:

> * Paul Guo:
>
> > 1. what is "init" in the AS-Unsafe words? It is not explained in the
> above
> > URL.
>
> I think it refers to the first call which performs initialization.
>
> > 2. heap
> >
> > The doc says "Functions marked with heap may call heap memory management
> > functions from the malloc/free family of functions and are only as safe
> as
> > those functions"
>
> If objects have GNU-style unwinding data and nothing calls
> __register_frame_info, the libgcc unwinder will not use malloc AFAICS.
> But I'm not entirely sure what this annotation is about.
>
> > But https://sourceware.org/ml/libc-alpha/2015-02/msg00659.html
> > <https://sourceware.org/ml/libc-alpha/2015-02/msg00659.html>says
> > backtrace() trigger malloc() only when dynamically loading libgcc. Does
> > that mean if libgcc was preloaded in the application we do not need to
> > worry about that?
>
> This no longer applies to current glibc for a whole bunch of reasons.
>

OK. Do you mean with current glibc, if a program is calling malloc() and
gets a signal,
and if the signal handler calls backtrace(), backtrace() won't be safe?


> > 2. dlopen: preloading libgcc could work around this limitation, right?
>
> No, libgcc_s calls _dl_iterate_phdr, which needs the loader lock.  I'm
> guessing the annotation refers to that.
>
> > 3. plugin: I do not know how backtrace() is affected by this but it seems
> > that it does not affect backtrace()?
>
> It refers to libgcc_s.  libgcc_s has its own locking to support
> __register_frame_info, but it is skipped if nothing calls this function.
> It does however rely on the loader lock via _dl_iterate_phdr.
>
> > 4. lock: I've seen a self deadlock (in backtrace()) case in my
> environment
> > (backtrace()-ing in one signal handler and then gets another signal and
> > that signal handler calls backtrace() again). The lock should
> > be __gthread_mutex_lock. For this we could use a wrapper of backtrace()
> to
> > prevent reentrance of backtrace() to work around this issue (Yes of
> course
> > the final backtrace() will return nothing but this scenario is rare so it
> > should be fine, right?).
>
> With __register_frame_info, you can get other deadlocks on the internal
> libgcc unwinder lock.
>
> Nowadays, the loader write lock is recursive, so if the signal handler
> interrupts certain dynamic loader operations, there won't be a deadlock
> due to _dl_iterate_phdr, but you could get crashes because the signal
> handler observes a temporarily corrupted list.
>
> To fix this, we would have to change the dynamic linker to provide an
> async-signal-safe function which returns the loaded object for an
> address and its unwinding data.  Then we'd have to patch libgcc_s to use
> that, in preference of its own caches.  This would be useful for C++
> code which wants to catch exceptions in an asynchronous signal handler,
> without letting them escape from the signal handler.
>
>
OK. Can I summarize like this after reading replies for questions 2 & 3 & 4:

If the application is preloading some libraries, then if it gets a signal
and then
backtrace() in the signal handler won't not safe. Other signal unsafe issues
include 1) reentrance of backtrace() due to various possible locks or even
data
corruption. 2) the "heap" issue(If I understand correctly. See reply above),
In other cases backtrace() is ok as a signal handler?

If this is just for a crash reporter, you really need to move this
> out-of-process because in-process crash handlers will always be prone to
> deadlocks and can obscure the actual source of the crash.
>

What is the usual "out-of-process" practice?
  /proc/sys/kernel/core_pattern + abrt kind of configuration (take
rhel/centos distribution as an example)?

Thanks you and Carlos (previous replier) for response.   -- Paul



More information about the Libc-help mailing list