mailing list of musl libc
 help / color / mirror / code / Atom feed
* Conditional signal safety?
@ 2019-06-29  5:54 Markus Wichmann
  2019-06-29  9:33 ` Szabolcs Nagy
                   ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Markus Wichmann @ 2019-06-29  5:54 UTC (permalink / raw)
  To: musl

Hi all,

at work yesterday I had to build an exception handler (a signal handler
for SIGSEGV, SIGBUS, SIGILL, and SIGFPE). For my purposes, it was really
convenient to just use dladdr() to find out at least what module and
function PC and LR were pointing to when the exception happened, so I
used that function.

Now, dladdr() is not on the list of signal safe functions, but then,
dladdr() is a GNU extension. I wondered if it is signal safe and noticed
that at least musl's implementation is, provided that dlopen() was not
the function that was pre-empted. That got me thinking: Is there such a
thing as "conditional signal safety"?

dladdr() takes a rwlock in read mode. At the moment, this means it can
only block if the lock is write locked, which only dlopen() will ever
do. dladdr() does nothing else that would impede signal safety. But of
course, these are implementation details. What is actually defined about
the interface?

Ciao,
Markus


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Conditional signal safety?
  2019-06-29  5:54 Conditional signal safety? Markus Wichmann
@ 2019-06-29  9:33 ` Szabolcs Nagy
  2019-06-29 16:49 ` Rich Felker
  2019-07-01  4:21 ` Florian Weimer
  2 siblings, 0 replies; 7+ messages in thread
From: Szabolcs Nagy @ 2019-06-29  9:33 UTC (permalink / raw)
  To: musl

* Markus Wichmann <nullplan@gmx.net> [2019-06-29 07:54:05 +0200]:
> Hi all,
> 
> at work yesterday I had to build an exception handler (a signal handler
> for SIGSEGV, SIGBUS, SIGILL, and SIGFPE). For my purposes, it was really
> convenient to just use dladdr() to find out at least what module and
> function PC and LR were pointing to when the exception happened, so I
> used that function.
> 
> Now, dladdr() is not on the list of signal safe functions, but then,
> dladdr() is a GNU extension. I wondered if it is signal safe and noticed
> that at least musl's implementation is, provided that dlopen() was not
> the function that was pre-empted. That got me thinking: Is there such a
> thing as "conditional signal safety"?
> 
> dladdr() takes a rwlock in read mode. At the moment, this means it can
> only block if the lock is write locked, which only dlopen() will ever
> do. dladdr() does nothing else that would impede signal safety. But of
> course, these are implementation details. What is actually defined about
> the interface?

note that the signals you handle (SIGSEGV, SIGBUS, SIGILL, SIGFPE)
are usually not asynchronous but happen at particular instructions.

dlopen does not hold locks while it runs user code, so you only
have issues if the dlopen code itself faults (which can happen e.g.
when invalid arguments are passed to it) so indeed in practice you
may get away with dladdr in the signal handler (e.g. if you know
dlopen won't fault).

in theory this does not help: the only concept the libc defines and
guarantees is async-signal-safety and dladdr is not as-safe so it
may do arbitrary non-as-safe operations, not just taking a dlopen
lock, and conversely arbitrary non-as-safe libc apis may take the
dlopen lock internally.

(btw this is why unwinding from a signal handler does not work
reliably even if there are async unwind tables in the binary: the
unwinder has to look up those tables for a particular elf module
the pc is in and this mechanism needs to synchronize with dlopen
which is currently not lock free and thus can deadlock.)

> 
> Ciao,
> Markus


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Conditional signal safety?
  2019-06-29  5:54 Conditional signal safety? Markus Wichmann
  2019-06-29  9:33 ` Szabolcs Nagy
@ 2019-06-29 16:49 ` Rich Felker
  2019-07-01  4:21 ` Florian Weimer
  2 siblings, 0 replies; 7+ messages in thread
From: Rich Felker @ 2019-06-29 16:49 UTC (permalink / raw)
  To: musl

On Sat, Jun 29, 2019 at 07:54:05AM +0200, Markus Wichmann wrote:
> Hi all,
> 
> at work yesterday I had to build an exception handler (a signal handler
> for SIGSEGV, SIGBUS, SIGILL, and SIGFPE). For my purposes, it was really
> convenient to just use dladdr() to find out at least what module and
> function PC and LR were pointing to when the exception happened, so I
> used that function.

This is convenient for debugging, but I would strongly discourage its
use in deployment. Attempts to intercept and introspectively report
(or even worse, patch up and continue after) memory-safety UB almost
always provide tools for an attacker to turn an unexploitable or
difficult-to-exploit error into one they can exploit. This is inherent
in continuing to run and make calls that might make use of compromised
pointers.

> Now, dladdr() is not on the list of signal safe functions, but then,
> dladdr() is a GNU extension. I wondered if it is signal safe and noticed
> that at least musl's implementation is, provided that dlopen() was not
> the function that was pre-empted. That got me thinking: Is there such a
> thing as "conditional signal safety"?

There's not, because it requires too fine-grained constraint of
implementation internals; my understanding is that this is the reason
both on the standards side (where they're rightfully opposed to
specifying anything about the interaction of internals) and on the
musl implementation side (where we don't want to preclude interactions
that have no obvious reason to exist but that are needed to fix subtle
problems -- see for example the interaction between sigaction and
abort).

> dladdr() takes a rwlock in read mode. At the moment, this means it can
> only block if the lock is write locked, which only dlopen() will ever
> do. dladdr() does nothing else that would impede signal safety. But of
> course, these are implementation details. What is actually defined about
> the interface?

Nothing further. Documenting the behavior of nonstandard extension
functions musl supports is on the agenda, but I don't think
documenting properties that are consequences of implementation
internals would be part of it except possibly as part of a "hacking"
document for use in debugging with tools that aren't stable interface
guarantees.

Rich


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Conditional signal safety?
  2019-06-29  5:54 Conditional signal safety? Markus Wichmann
  2019-06-29  9:33 ` Szabolcs Nagy
  2019-06-29 16:49 ` Rich Felker
@ 2019-07-01  4:21 ` Florian Weimer
  2019-07-01 14:06   ` Rich Felker
  2 siblings, 1 reply; 7+ messages in thread
From: Florian Weimer @ 2019-07-01  4:21 UTC (permalink / raw)
  To: Markus Wichmann; +Cc: musl

* Markus Wichmann:

> at work yesterday I had to build an exception handler (a signal handler
> for SIGSEGV, SIGBUS, SIGILL, and SIGFPE). For my purposes, it was really
> convenient to just use dladdr() to find out at least what module and
> function PC and LR were pointing to when the exception happened, so I
> used that function.

Are these signals generated synchronously, by running code?  Then the
rules regarding asynchronous signal safety do not apply.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Conditional signal safety?
  2019-07-01  4:21 ` Florian Weimer
@ 2019-07-01 14:06   ` Rich Felker
  2019-07-01 15:55     ` Florian Weimer
  0 siblings, 1 reply; 7+ messages in thread
From: Rich Felker @ 2019-07-01 14:06 UTC (permalink / raw)
  To: musl

On Mon, Jul 01, 2019 at 06:21:11AM +0200, Florian Weimer wrote:
> * Markus Wichmann:
> 
> > at work yesterday I had to build an exception handler (a signal handler
> > for SIGSEGV, SIGBUS, SIGILL, and SIGFPE). For my purposes, it was really
> > convenient to just use dladdr() to find out at least what module and
> > function PC and LR were pointing to when the exception happened, so I
> > used that function.
> 
> Are these signals generated synchronously, by running code?  Then the
> rules regarding asynchronous signal safety do not apply.

That's a meaningful distinction if they're generated by accesses in
the application code. If they're generated by accesses from within
standard library functions (e.g. because you passed an invalid pointer
or one to memory that was intentionally setup to generate them) to a
stdlib function, it's just UB, and if you were going to define it,
it'd still be an async signal context just because it's async with
respect to the interrupted state of the stdlib function being
unspecified/unspecifiable.

Rich


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Conditional signal safety?
  2019-07-01 14:06   ` Rich Felker
@ 2019-07-01 15:55     ` Florian Weimer
  2019-07-01 16:13       ` Rich Felker
  0 siblings, 1 reply; 7+ messages in thread
From: Florian Weimer @ 2019-07-01 15:55 UTC (permalink / raw)
  To: Rich Felker; +Cc: musl

* Rich Felker:

> On Mon, Jul 01, 2019 at 06:21:11AM +0200, Florian Weimer wrote:
>> * Markus Wichmann:
>> 
>> > at work yesterday I had to build an exception handler (a signal handler
>> > for SIGSEGV, SIGBUS, SIGILL, and SIGFPE). For my purposes, it was really
>> > convenient to just use dladdr() to find out at least what module and
>> > function PC and LR were pointing to when the exception happened, so I
>> > used that function.
>> 
>> Are these signals generated synchronously, by running code?  Then the
>> rules regarding asynchronous signal safety do not apply.
>
> That's a meaningful distinction if they're generated by accesses in
> the application code. If they're generated by accesses from within
> standard library functions (e.g. because you passed an invalid pointer
> or one to memory that was intentionally setup to generate them) to a
> stdlib function, it's just UB, and if you were going to define it,
> it'd still be an async signal context just because it's async with
> respect to the interrupted state of the stdlib function being
> unspecified/unspecifiable.

Right, but if libc code traps without violating preconditions, that's
generally a bug.  And if you violate preconditions, than *that* already
triggers undefined behavior, and not the trap later on.  (For example,
the compiler uses the knowledge of well-known functions and optimizes
accordingly.)

Thanks,
Florian


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: Conditional signal safety?
  2019-07-01 15:55     ` Florian Weimer
@ 2019-07-01 16:13       ` Rich Felker
  0 siblings, 0 replies; 7+ messages in thread
From: Rich Felker @ 2019-07-01 16:13 UTC (permalink / raw)
  To: musl

On Mon, Jul 01, 2019 at 05:55:07PM +0200, Florian Weimer wrote:
> * Rich Felker:
> 
> > On Mon, Jul 01, 2019 at 06:21:11AM +0200, Florian Weimer wrote:
> >> * Markus Wichmann:
> >> 
> >> > at work yesterday I had to build an exception handler (a signal handler
> >> > for SIGSEGV, SIGBUS, SIGILL, and SIGFPE). For my purposes, it was really
> >> > convenient to just use dladdr() to find out at least what module and
> >> > function PC and LR were pointing to when the exception happened, so I
> >> > used that function.
> >> 
> >> Are these signals generated synchronously, by running code?  Then the
> >> rules regarding asynchronous signal safety do not apply.
> >
> > That's a meaningful distinction if they're generated by accesses in
> > the application code. If they're generated by accesses from within
> > standard library functions (e.g. because you passed an invalid pointer
> > or one to memory that was intentionally setup to generate them) to a
> > stdlib function, it's just UB, and if you were going to define it,
> > it'd still be an async signal context just because it's async with
> > respect to the interrupted state of the stdlib function being
> > unspecified/unspecifiable.
> 
> Right, but if libc code traps without violating preconditions, that's
> generally a bug.

Yes. If any of these signals are generated in libc without the
preconditions of the interface having been violated, that's a bug in
libc. For appropriate notions of what the preconditions are. It's
clear for stuff like invalid pointers, but less obvious when you're
dealing with things like memory setup explicitly to trap. My view is
that such memory still does not constitute the regular C object the
function requires, and thus the standard doesn't define any behavior
for it. I think it would be hard to specify any particular behavior
without also specifying a lot of the library internals -- either you
have to specify that the signal context is an async one, or you have
to place constraints on how internal locking and resource usage works
and what locks can possibly be held by what interfaces.

So, IMO you have to treat it as "at best an async signal context; at
worst, UB and thereby completely undefined program state".

> And if you violate preconditions, than *that* already
> triggers undefined behavior, and not the trap later on.  (For example,
> the compiler uses the knowledge of well-known functions and optimizes
> accordingly.)

Yes.

Rich


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2019-07-01 16:13 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-06-29  5:54 Conditional signal safety? Markus Wichmann
2019-06-29  9:33 ` Szabolcs Nagy
2019-06-29 16:49 ` Rich Felker
2019-07-01  4:21 ` Florian Weimer
2019-07-01 14:06   ` Rich Felker
2019-07-01 15:55     ` Florian Weimer
2019-07-01 16:13       ` Rich Felker

Code repositories for project(s) associated with this public inbox

	https://git.vuxu.org/mirror/musl/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).