Thread (18 messages) 18 messages, 3 authors, 2020-11-05

Re: [PATCH v13 7/8] signal: define the field siginfo.si_faultflags

From: Catalin Marinas <catalin.marinas@arm.com>
Date: 2020-11-04 18:24:00
Also in: linux-api

On Tue, Nov 03, 2020 at 10:39:52AM -0800, Peter Collingbourne wrote:
On Tue, Nov 3, 2020 at 9:54 AM Catalin Marinas [off-list ref] wrote:
quoted
On Mon, Nov 02, 2020 at 08:09:43PM -0800, Peter Collingbourne wrote:
quoted
This field will contain flags that may be used by signal handlers to
determine whether other fields in the _sigfault portion of siginfo are
valid. An example use case is the following patch, which introduces
the si_addr_tag_bits{,_mask} fields.

A new sigcontext flag, SA_FAULTFLAGS, is introduced in order to allow
a signal handler to require the kernel to set the field (but note
that the field will be set anyway if the kernel supports the flag,
regardless of its value). In combination with the previous patches,
this allows a userspace program to determine whether the kernel will
set the field.
As per patch 5, a user is supposed to call sigaction() twice to figure
out whether _faultflags is meaningful. That's the part I'm not
particularly fond of. Are the unused parts of siginfo always zeroed when
the kernel delivers a signal? If yes, we could simply check the new
field for non-zero bits.
The unused parts of siginfo are zeroed in current kernels, but
unfortunately not in older kernels. The zeroing behavior was
introduced in commit c999b933faa5e281e3af2e110eccaf91698b0a81 which
first appeared in kernel version 4.18, and at least in Android land we
do need to support kernel versions older than that.
I see. I was hoping for an easy way out.

Now, with always populating the si_faultflags field, you are going back
to writing non-zero stuff in siginfo for unaware apps. I don't think
that's an issue (the alternative is to only write it of SA_FAULTFLAGS
was set).

Yet another option would be to pass a new AT_ZEROED_SI via AT_FLAGS (we
don't use them for anything) so that the user can infer whether
si_faultflags has meaningful information without two sigaction() calls.
quoted
quoted
It is possible for an si_faultflags-unaware program to cause a signal
handler in an si_faultflags-aware program to be called with a provided
siginfo data structure by using one of the following syscalls:

- ptrace(PTRACE_SETSIGINFO)
- pidfd_send_signal
- rt_sigqueueinfo
- rt_tgsigqueueinfo

So we need to prevent the si_faultflags-unaware program from causing an
uninitialized read of si_faultflags in the si_faultflags-aware program when
it uses one of these syscalls.

The last three cases can be handled by observing that each of these
syscalls fails if si_code >= 0. We also observe that kill(2) and
tgkill(2) may be used to send a signal where si_code == 0 (SI_USER),
so we define si_faultflags to only be valid if si_code > 0.

There is no such check on si_code in ptrace(PTRACE_SETSIGINFO), so
we make ptrace(PTRACE_SETSIGINFO) clear the si_faultflags field if it
detects that the signal would use the _sigfault layout, and introduce
a new ptrace request type, PTRACE_SETSIGINFO2, that a si_faultflags-aware
program may use to opt out of this behavior.
I find this pretty fragile but maybe I have to read it a few more times
to fully understand the implications ;).

Could we instead copy all the fields, potentially uninitialised, and
instead filter them when delivering the signal based on the
SA_FAULTFLAGS? That means that the kernel only writes si_faultflags if
the user requested it.
I don't see how that would help. The goal is to protect new signal
handlers from old signal "injectors" that will have potentially
uninitialized data where the si_faultflags field is. The new signal
handler will have SA_FAULTFLAGS set so that wouldn't prevent the
signal handler from seeing the uninitialized data.
You are right, it doesn't help if the handler will have set
SA_FAULTFLAGS.
quoted
quoted
v12:
- Change type of si_xflags to u32 to avoid increasing alignment
[...]
quoted
diff --git a/include/uapi/asm-generic/siginfo.h b/include/uapi/asm-generic/siginfo.h
index 7aacf9389010..f43778355b77 100644
--- a/include/uapi/asm-generic/siginfo.h
+++ b/include/uapi/asm-generic/siginfo.h
@@ -91,7 +91,9 @@ union __sifields {
                              char _dummy_pkey[__ADDR_BND_PKEY_PAD];
                              __u32 _pkey;
                      } _addr_pkey;
+                     void *_pad[6];
              };
+             __u32 _faultflags;
 } _sigfault;
Sorry, I haven't checked the previous discussion on alignment here but
don't we already require 64-bit alignment because of other members in
the _sigfault union? We already have void * throughout this and with the
next patch we just have a gap (unless I miscalculated the offsets).
This is about avoiding increasing alignment on 32-bit platforms.
Currently the alignment is 4 but a u64 field would bump it to 8.

Unfortunately we can't do much about the gap on 64-bit platforms. This
was previously a uintptr_t but that would mean that the upper 32 bits
cannot be used safely on all platforms so we would effectively end up
with a gap anyway.
We could add a dummy pad on 64-bit. BTW, the tags only make sense on
64-bit hardware, 32-bit doesn't have enough room.

-- 
Catalin

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help