diff mbox series

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

Message ID 743fef80a8617378027d5d2b0538cfc36ea106a1.1604376407.git.pcc@google.com (mailing list archive)
State New, archived
Headers show
Series arm64: expose FAR_EL1 tag bits in siginfo | expand

Commit Message

Peter Collingbourne Nov. 3, 2020, 4:09 a.m. UTC
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.

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.

It is also possible for the kernel to inject a signal specified to
use _sigfault by calling force_sig (e.g. there are numerous calls to
force_sig(SIGSEGV)). In this case si_code is set to SI_KERNEL and the
_kill union member is used, so document that si_code must be < SI_KERNEL.

Ideally this field could have just been named si_flags, but that
name was already taken by ia64, so a different name was chosen.

I considered making ia64's si_flags a generic field and having it
appear at the end of _sigfault (in the same place as this patch has
si_faultflags) on non-ia64, keeping it in the same place on ia64. ia64's
si_flags is a 32-bit field with only one flag bit allocated, so we
would have 31 bits to use if we do this. However, it seems simplest
to avoid entangling these fields.

Signed-off-by: Peter Collingbourne <pcc@google.com>
Link: https://linux-review.googlesource.com/id/Ide155ce29366c3eab2a944ae4c51205982e5b8b2
---
v13:
- renamed si_xflags to si_faultflags
- use fallthrough macros in kernel/ptrace.c
- fixed a style warning pointed out by checkpatch.pl

v12:
- Change type of si_xflags to u32 to avoid increasing alignment
- Add si_xflags to signal_compat.c test cases

v11:
- update comment to say that si_code must > 0
- change ptrace(PTRACE_SETSIGINFO2) to take a flags argument

v10:
- make the new field compatible with the various ways
  that a siginfo can be injected from another process
- eliminate some duplication by adding a refactoring patch
  before this one

 arch/powerpc/platforms/powernv/vas-fault.c |  1 +
 arch/x86/kernel/signal_compat.c            |  7 +++--
 include/linux/compat.h                     |  2 ++
 include/linux/signal_types.h               |  2 +-
 include/uapi/asm-generic/siginfo.h         |  4 +++
 include/uapi/asm-generic/signal-defs.h     |  4 +++
 include/uapi/linux/ptrace.h                | 12 ++++++++
 kernel/ptrace.c                            | 33 ++++++++++++++++++----
 kernel/signal.c                            |  3 ++
 9 files changed, 59 insertions(+), 9 deletions(-)

Comments

Catalin Marinas Nov. 3, 2020, 5:53 p.m. UTC | #1
Hi Peter,

On Mon, Nov 02, 2020 at 08:09:43PM -0800, Peter Collingbourne wrote:
> 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.

> 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.

> v12:
> - Change type of si_xflags to u32 to avoid increasing alignment
[...]
> 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).
Peter Collingbourne Nov. 3, 2020, 6:39 p.m. UTC | #2
On Tue, Nov 3, 2020 at 9:54 AM Catalin Marinas <catalin.marinas@arm.com> wrote:
>
> Hi Peter,
>
> On Mon, Nov 02, 2020 at 08:09:43PM -0800, Peter Collingbourne wrote:
> > 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.

> > 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.

> > v12:
> > - Change type of si_xflags to u32 to avoid increasing alignment
> [...]
> > 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.

Peter
Dave Martin Nov. 4, 2020, 10:57 a.m. UTC | #3
On Tue, Nov 03, 2020 at 10:39:52AM -0800, Peter Collingbourne wrote:
> On Tue, Nov 3, 2020 at 9:54 AM Catalin Marinas <catalin.marinas@arm.com> wrote:
> >
> > Hi Peter,
> >
> > On Mon, Nov 02, 2020 at 08:09:43PM -0800, Peter Collingbourne wrote:
> > > 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.
> 
> > > 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.
> 
> > > v12:
> > > - Change type of si_xflags to u32 to avoid increasing alignment
> > [...]
> > > 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.

I suppose we could make this an int or long if that feels more natural,
but unless we have different flag definitions for 32-bit and 64-bit
platforms, it would be hard to make use of the high 32 bits on 64-bit.

Cheers
---Dave
Catalin Marinas Nov. 4, 2020, 6:23 p.m. UTC | #4
On Tue, Nov 03, 2020 at 10:39:52AM -0800, Peter Collingbourne wrote:
> On Tue, Nov 3, 2020 at 9:54 AM Catalin Marinas <catalin.marinas@arm.com> wrote:
> > On Mon, Nov 02, 2020 at 08:09:43PM -0800, Peter Collingbourne wrote:
> > > 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.

> > > 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.

> > > v12:
> > > - Change type of si_xflags to u32 to avoid increasing alignment
> > [...]
> > > 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.
Peter Collingbourne Nov. 4, 2020, 7:57 p.m. UTC | #5
On Wed, Nov 4, 2020 at 10:23 AM Catalin Marinas <catalin.marinas@arm.com> wrote:
>
> On Tue, Nov 03, 2020 at 10:39:52AM -0800, Peter Collingbourne wrote:
> > On Tue, Nov 3, 2020 at 9:54 AM Catalin Marinas <catalin.marinas@arm.com> wrote:
> > > On Mon, Nov 02, 2020 at 08:09:43PM -0800, Peter Collingbourne wrote:
> > > > 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.

That's one option, although one benefit of having this involve
sigaction is that in many cases where sigaction is wrapped or
interposed we end up with correct behavior. Imagine a wrapper that
stashes the provided struct sigaction somewhere and provides its own
struct sigaction with its own handler to the kernel, and that handler
copies siginfo field by field before calling the user's handler. In
this scenario the handler would observe an uninitialized faultflags.
With the SA_UNSUPPORTED/SA_FAULTFLAGS protocol we would detect this
scenario in the same way as an old kernel and avoid reading
faultflags.

Of course this isn't a perfect defense but it's probably the best we can do.

> > > > 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.
>
> > > > v12:
> > > > - Change type of si_xflags to u32 to avoid increasing alignment
> > > [...]
> > > > 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.

And then later once we add a 32-bit field here we use it like so?

__u32 _faultflags;
#ifdef __LP64__
__u32 _newfield;
#endif
unsigned long _addr_tag_bits, _addr_tag_bits_mask;
#ifndef __LP64__
__u32 _newfield;
#endif

Okay, I'll go ahead with that for now.

> BTW, the tags only make sense on
> 64-bit hardware, 32-bit doesn't have enough room.

From an architectural perspective it really depends on which kinds of
applications you are targeting. For example if you have something like
a 32-bit microcontroller you might not need all of the address space
for memory so it may be worthwhile to allow some bits to be used for
tags. According to the comments on [1] RISC-V is planning to have
their first implementation only support 64-bit but they haven't ruled
out 32-bit in the future.

[1] https://docs.google.com/document/d/1RZcEgljHY9ACeKKoLebBNPLqjl6nMMyG/edit#heading=h.1fob9te

Peter
diff mbox series

Patch

diff --git a/arch/powerpc/platforms/powernv/vas-fault.c b/arch/powerpc/platforms/powernv/vas-fault.c
index 3d21fce254b7..877e7d5fb4a2 100644
--- a/arch/powerpc/platforms/powernv/vas-fault.c
+++ b/arch/powerpc/platforms/powernv/vas-fault.c
@@ -154,6 +154,7 @@  static void update_csb(struct vas_window *window,
 	info.si_errno = EFAULT;
 	info.si_code = SEGV_MAPERR;
 	info.si_addr = csb_addr;
+	info.si_faultflags = 0;
 
 	/*
 	 * process will be polling on csb.flags after request is sent to
diff --git a/arch/x86/kernel/signal_compat.c b/arch/x86/kernel/signal_compat.c
index ddfd919be46c..222ff6178571 100644
--- a/arch/x86/kernel/signal_compat.c
+++ b/arch/x86/kernel/signal_compat.c
@@ -121,8 +121,8 @@  static inline void signal_compat_build_tests(void)
 #endif
 
 	CHECK_CSI_OFFSET(_sigfault);
-	CHECK_CSI_SIZE  (_sigfault, 4*sizeof(int));
-	CHECK_SI_SIZE   (_sigfault, 8*sizeof(int));
+	CHECK_CSI_SIZE  (_sigfault, 8*sizeof(int));
+	CHECK_SI_SIZE   (_sigfault, 16*sizeof(int));
 
 	BUILD_BUG_ON(offsetof(siginfo_t, si_addr) != 0x10);
 	BUILD_BUG_ON(offsetof(compat_siginfo_t, si_addr) != 0x0C);
@@ -138,6 +138,9 @@  static inline void signal_compat_build_tests(void)
 	BUILD_BUG_ON(offsetof(siginfo_t, si_pkey) != 0x20);
 	BUILD_BUG_ON(offsetof(compat_siginfo_t, si_pkey) != 0x14);
 
+	BUILD_BUG_ON(offsetof(siginfo_t, si_faultflags) != 0x48);
+	BUILD_BUG_ON(offsetof(compat_siginfo_t, si_faultflags) != 0x28);
+
 	CHECK_CSI_OFFSET(_sigpoll);
 	CHECK_CSI_SIZE  (_sigpoll, 2*sizeof(int));
 	CHECK_SI_SIZE   (_sigpoll, 4*sizeof(int));
diff --git a/include/linux/compat.h b/include/linux/compat.h
index 14d514233e1d..84d3b72be701 100644
--- a/include/linux/compat.h
+++ b/include/linux/compat.h
@@ -236,7 +236,9 @@  typedef struct compat_siginfo {
 					char _dummy_pkey[__COMPAT_ADDR_BND_PKEY_PAD];
 					u32 _pkey;
 				} _addr_pkey;
+				compat_uptr_t _pad[6];
 			};
+			u32 _faultflags;
 		} _sigfault;
 
 		/* SIGPOLL */
diff --git a/include/linux/signal_types.h b/include/linux/signal_types.h
index a7887ad84d36..7501209eae33 100644
--- a/include/linux/signal_types.h
+++ b/include/linux/signal_types.h
@@ -78,6 +78,6 @@  struct ksignal {
 
 #define UAPI_SA_FLAGS                                                          \
 	(SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART |  \
-	 SA_NODEFER | SA_RESETHAND | __ARCH_UAPI_SA_FLAGS)
+	 SA_NODEFER | SA_RESETHAND | SA_FAULTFLAGS | __ARCH_UAPI_SA_FLAGS)
 
 #endif /* _LINUX_SIGNAL_TYPES_H */
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;
 
 	/* SIGPOLL */
@@ -152,6 +154,8 @@  typedef struct siginfo {
 #define si_trapno	_sifields._sigfault._trapno
 #endif
 #define si_addr_lsb	_sifields._sigfault._addr_lsb
+/* si_faultflags is only valid if 0 < si_code < SI_KERNEL */
+#define si_faultflags	_sifields._sigfault._faultflags
 #define si_lower	_sifields._sigfault._addr_bnd._lower
 #define si_upper	_sifields._sigfault._addr_bnd._upper
 #define si_pkey		_sifields._sigfault._addr_pkey._pkey
diff --git a/include/uapi/asm-generic/signal-defs.h b/include/uapi/asm-generic/signal-defs.h
index 0126ebda4d31..e27bf959d4c4 100644
--- a/include/uapi/asm-generic/signal-defs.h
+++ b/include/uapi/asm-generic/signal-defs.h
@@ -20,6 +20,9 @@ 
  * so this bit allows flag bit support to be detected from userspace while
  * allowing an old kernel to be distinguished from a kernel that supports every
  * flag bit.
+ * SA_FAULTFLAGS indicates that the signal handler requires the siginfo.si_faultflags
+ * field to be valid. Note that if the kernel supports SA_FAULTFLAGS, the field will
+ * be valid regardless of the value of this flag.
  *
  * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
  * Unix names RESETHAND and NODEFER respectively.
@@ -49,6 +52,7 @@ 
 #define SA_RESETHAND	0x80000000
 #endif
 #define SA_UNSUPPORTED	0x00000400
+#define SA_FAULTFLAGS	0x00000800
 
 #define SA_NOMASK	SA_NODEFER
 #define SA_ONESHOT	SA_RESETHAND
diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h
index a71b6e3b03eb..722a2c8a4d3d 100644
--- a/include/uapi/linux/ptrace.h
+++ b/include/uapi/linux/ptrace.h
@@ -101,6 +101,18 @@  struct ptrace_syscall_info {
 	};
 };
 
+#define PTRACE_SETSIGINFO2	0x420f
+/*
+ * These flags are passed as the addr argument to ptrace.
+ */
+
+/*
+ * Asserts that the caller is aware of the field siginfo.si_faultflags. Prevents
+ * the kernel from automatically setting the field to 0 when the signal uses
+ * a sigfault layout.
+ */
+#define PTRACE_SIGINFO_FAULTFLAGS	0x1
+
 /*
  * These values are stored in task->ptrace_message
  * by tracehook_report_syscall_* to describe the current syscall-stop.
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 43d6179508d6..ab0618b4602c 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -687,18 +687,31 @@  static int ptrace_getsiginfo(struct task_struct *child, kernel_siginfo_t *info)
 	return error;
 }
 
-static int ptrace_setsiginfo(struct task_struct *child, const kernel_siginfo_t *info)
+static int ptrace_setsiginfo(struct task_struct *child, unsigned long flags,
+			     kernel_siginfo_t *info)
 {
-	unsigned long flags;
+	unsigned long lock_flags;
 	int error = -ESRCH;
 
-	if (lock_task_sighand(child, &flags)) {
+	if (flags & ~PTRACE_SIGINFO_FAULTFLAGS)
+		return -EINVAL;
+
+	/*
+	 * If the caller is unaware of si_faultflags and we're using a layout that
+	 * requires it, set it to 0 which means "no fields are available".
+	 */
+	if (!(flags & PTRACE_SIGINFO_FAULTFLAGS) &&
+	    siginfo_layout_is_fault(
+		    siginfo_layout(info->si_signo, info->si_code)))
+		info->si_faultflags = 0;
+
+	if (lock_task_sighand(child, &lock_flags)) {
 		error = -EINVAL;
 		if (likely(child->last_siginfo != NULL)) {
 			copy_siginfo(child->last_siginfo, info);
 			error = 0;
 		}
-		unlock_task_sighand(child, &flags);
+		unlock_task_sighand(child, &lock_flags);
 	}
 	return error;
 }
@@ -1038,9 +1051,13 @@  int ptrace_request(struct task_struct *child, long request,
 		break;
 
 	case PTRACE_SETSIGINFO:
+		addr = 0;
+		fallthrough;
+
+	case PTRACE_SETSIGINFO2:
 		ret = copy_siginfo_from_user(&siginfo, datavp);
 		if (!ret)
-			ret = ptrace_setsiginfo(child, &siginfo);
+			ret = ptrace_setsiginfo(child, addr, &siginfo);
 		break;
 
 	case PTRACE_GETSIGMASK: {
@@ -1347,10 +1364,14 @@  int compat_ptrace_request(struct task_struct *child, compat_long_t request,
 		break;
 
 	case PTRACE_SETSIGINFO:
+		addr = 0;
+		fallthrough;
+
+	case PTRACE_SETSIGINFO2:
 		ret = copy_siginfo_from_user32(
 			&siginfo, (struct compat_siginfo __user *) datap);
 		if (!ret)
-			ret = ptrace_setsiginfo(child, &siginfo);
+			ret = ptrace_setsiginfo(child, addr, &siginfo);
 		break;
 #ifdef CONFIG_HAVE_ARCH_TRACEHOOK
 	case PTRACE_GETREGSET:
diff --git a/kernel/signal.c b/kernel/signal.c
index d18930aafbf4..1fd1f0d12174 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1657,6 +1657,7 @@  static void set_sigfault_common_fields(struct kernel_siginfo *info, int sig,
 	info->si_errno = 0;
 	info->si_code = code;
 	info->si_addr = addr;
+	info->si_faultflags = 0;
 }
 
 int force_sig_fault_to_task(int sig, int code, void __user *addr
@@ -3270,6 +3271,7 @@  void copy_siginfo_to_external32(struct compat_siginfo *to,
 #ifdef __ARCH_SI_TRAPNO
 		to->si_trapno = from->si_trapno;
 #endif
+		to->si_faultflags = from->si_faultflags;
 	}
 
 	switch (layout) {
@@ -3345,6 +3347,7 @@  static int post_copy_siginfo_from_user32(kernel_siginfo_t *to,
 #ifdef __ARCH_SI_TRAPNO
 		to->si_trapno = from->si_trapno;
 #endif
+		to->si_faultflags = from->si_faultflags;
 	}
 
 	switch (layout) {