diff mbox

[RFC,v2,4/6] arm64: signal: Allocate extra sigcontext space as needed

Message ID 1492016495-19689-4-git-send-email-Dave.Martin@arm.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dave Martin April 12, 2017, 5:01 p.m. UTC
This patch modifies the context block allocator to create an
extra_context expansion block as necessary, and adds the necessary
code to populate, parse and decode this block.

Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
 arch/arm64/include/uapi/asm/sigcontext.h |  27 ++++++++
 arch/arm64/kernel/signal.c               | 111 ++++++++++++++++++++++++++-----
 2 files changed, 120 insertions(+), 18 deletions(-)

Comments

Catalin Marinas May 12, 2017, 4:57 p.m. UTC | #1
Hi Dave,

On Wed, Apr 12, 2017 at 06:01:13PM +0100, Dave P Martin wrote:
> --- a/arch/arm64/include/uapi/asm/sigcontext.h
> +++ b/arch/arm64/include/uapi/asm/sigcontext.h
> @@ -80,4 +80,31 @@ struct esr_context {
>  	__u64 esr;
>  };
>  
> +/*
> + * Pointer to extra space for additional structures that don't fit in
> + * sigcontext.__reserved[].  Note:
> + *
> + * 1) fpsimd_context, esr_context and extra_context must be placed in
> + * sigcontext.__reserved[] if present.  They cannot be placed in the
> + * extra space.  Any other record can be placed either in the extra
> + * space or in sigcontext.__reserved[].
> + *
> + * 2) There must not be more than one extra_context.
> + *
> + * 3) If extra_context is present, it must be followed immediately in
> + * sigcontext.__reserved[] by the terminating null _aarch64_ctx (i.e.,
> + * extra_context must be the last record in sigcontext.__reserved[]
> + * except for the terminator).
> + *
> + * 4) The extra space must itself be terminated with a null
> + * _aarch64_ctx.
> + */

IIUC, if we need to save some state that doesn't fit in what's left of
sigcontext.__reserved[] (e.g. SVE with 1024-bit vector length), we
ignore the available space and go for a memory block following the end
of sigcontext.__reserved[] + 16. Is there a reason we can't store the
new state across the end of sigcontext.__reserved[] and move fp/lr at
the end of the new frame? I'm not sure the fp/lr position immediately
after __reserved[] counts as ABI.

> +#define EXTRA_MAGIC	0x45585401
> +
> +struct extra_context {
> +	struct _aarch64_ctx head;
> +	void __user *data;	/* 16-byte aligned pointer to extra space */
"__user" is a kernel-only attribute, we shouldn't expose it in a uapi
header.

> +	__u32 size;		/* size in bytes of the extra space */
> +};

Do we need the size of the extra space? Can we not infer it anyway by
walking the contexts save there? Surely we don't expect more than one
extra context.
Dave Martin May 15, 2017, 1:24 p.m. UTC | #2
On Fri, May 12, 2017 at 05:57:24PM +0100, Catalin Marinas wrote:
> Hi Dave,
> 
> On Wed, Apr 12, 2017 at 06:01:13PM +0100, Dave P Martin wrote:
> > --- a/arch/arm64/include/uapi/asm/sigcontext.h
> > +++ b/arch/arm64/include/uapi/asm/sigcontext.h
> > @@ -80,4 +80,31 @@ struct esr_context {
> >  	__u64 esr;
> >  };
> >  
> > +/*
> > + * Pointer to extra space for additional structures that don't fit in
> > + * sigcontext.__reserved[].  Note:
> > + *
> > + * 1) fpsimd_context, esr_context and extra_context must be placed in
> > + * sigcontext.__reserved[] if present.  They cannot be placed in the
> > + * extra space.  Any other record can be placed either in the extra
> > + * space or in sigcontext.__reserved[].
> > + *
> > + * 2) There must not be more than one extra_context.
> > + *
> > + * 3) If extra_context is present, it must be followed immediately in
> > + * sigcontext.__reserved[] by the terminating null _aarch64_ctx (i.e.,
> > + * extra_context must be the last record in sigcontext.__reserved[]
> > + * except for the terminator).
> > + *
> > + * 4) The extra space must itself be terminated with a null
> > + * _aarch64_ctx.
> > + */
> 
> IIUC, if we need to save some state that doesn't fit in what's left of
> sigcontext.__reserved[] (e.g. SVE with 1024-bit vector length), we
> ignore the available space and go for a memory block following the end
> of sigcontext.__reserved[] + 16. Is there a reason we can't store the
> new state across the end of sigcontext.__reserved[] and move fp/lr at
> the end of the new frame? I'm not sure the fp/lr position immediately
> after __reserved[] counts as ABI.

This was my original view.

Originally I preferred not to waste the space and did move fp/lr to the
end, but someone (I think you or Will) expressed concern that the fp/lr
position relative to the signal frame _might_ count as ABI.

I think it's not that likely that software will be relying on this,
since it appears easier just to follow the frame chain than to treat
this as a special case.

But it's hard to be certain.  It comes down to a judgement call.

We could also move fp/lr later, if we require sigframe parsers to be
a bit more permissive about where extra_context starts.

> > +#define EXTRA_MAGIC	0x45585401
> > +
> > +struct extra_context {
> > +	struct _aarch64_ctx head;
> > +	void __user *data;	/* 16-byte aligned pointer to extra space */
> "__user" is a kernel-only attribute, we shouldn't expose it in a uapi
> header.

This is filtered out by headers_install, just like #ifdef __KERNEL__.

data really is a pointer to a user address.  Compare for example struct
iovec in include/uapi/linux/uio.h.

I think the __user is required to keep sparse happy, otherwise some
additional casting is needed to bless a valid read from this field
before passing it to get/put_user.  Or there may be some other reason
I've forgotten...

> 
> > +	__u32 size;		/* size in bytes of the extra space */
> > +};
> 
> Do we need the size of the extra space? Can we not infer it anyway by
> walking the contexts save there? Surely we don't expect more than one
> extra context.

Strictly speaking we don't need it.  When userspace parses a signal
frame generated by the kernel, it can trust the kernel to write a well-
formed signal frame.

In sigreturn it allows us to retain a sanity-check on overall size
similar to what sizeof(__reserved) gives us.  This "feels cleaner"
to me, but the value of it is debatable, since we can still apply
SIGFRAME_MAXSZ and uaccess should protect us against gross overruns.

The size parameter *may* provide some limited defence against
sigreturn-mediated stack smashing in userspace, but it wasn't designed
for this purpose and again the value is debatable.

We can likely live without it if you're not keen on it.

Cheers
---Dave
Catalin Marinas May 23, 2017, 11:30 a.m. UTC | #3
On Mon, May 15, 2017 at 02:24:45PM +0100, Dave P Martin wrote:
> On Fri, May 12, 2017 at 05:57:24PM +0100, Catalin Marinas wrote:
> > On Wed, Apr 12, 2017 at 06:01:13PM +0100, Dave P Martin wrote:
> > > --- a/arch/arm64/include/uapi/asm/sigcontext.h
> > > +++ b/arch/arm64/include/uapi/asm/sigcontext.h
> > > @@ -80,4 +80,31 @@ struct esr_context {
> > >  	__u64 esr;
> > >  };
> > >  
> > > +/*
> > > + * Pointer to extra space for additional structures that don't fit in
> > > + * sigcontext.__reserved[].  Note:
> > > + *
> > > + * 1) fpsimd_context, esr_context and extra_context must be placed in
> > > + * sigcontext.__reserved[] if present.  They cannot be placed in the
> > > + * extra space.  Any other record can be placed either in the extra
> > > + * space or in sigcontext.__reserved[].
> > > + *
> > > + * 2) There must not be more than one extra_context.
> > > + *
> > > + * 3) If extra_context is present, it must be followed immediately in
> > > + * sigcontext.__reserved[] by the terminating null _aarch64_ctx (i.e.,
> > > + * extra_context must be the last record in sigcontext.__reserved[]
> > > + * except for the terminator).
> > > + *
> > > + * 4) The extra space must itself be terminated with a null
> > > + * _aarch64_ctx.
> > > + */
> > 
> > IIUC, if we need to save some state that doesn't fit in what's left of
> > sigcontext.__reserved[] (e.g. SVE with 1024-bit vector length), we
> > ignore the available space and go for a memory block following the end
> > of sigcontext.__reserved[] + 16. Is there a reason we can't store the
> > new state across the end of sigcontext.__reserved[] and move fp/lr at
> > the end of the new frame? I'm not sure the fp/lr position immediately
> > after __reserved[] counts as ABI.
> 
> This was my original view.
> 
> Originally I preferred not to waste the space and did move fp/lr to the
> end, but someone (I think you or Will) expressed concern that the fp/lr
> position relative to the signal frame _might_ count as ABI.
> 
> I think it's not that likely that software will be relying on this,
> since it appears easier just to follow the frame chain than to treat
> this as a special case.
> 
> But it's hard to be certain.  It comes down to a judgement call.

I would not consider this ABI. The ABI part is that the fp register
points to where fp/lr were saved.

> > > +#define EXTRA_MAGIC	0x45585401
> > > +
> > > +struct extra_context {
> > > +	struct _aarch64_ctx head;
> > > +	void __user *data;	/* 16-byte aligned pointer to extra space */
> > "__user" is a kernel-only attribute, we shouldn't expose it in a uapi
> > header.
> 
> This is filtered out by headers_install, just like #ifdef __KERNEL__.

Ah, ok, I missed this.

> > > +	__u32 size;		/* size in bytes of the extra space */
> > > +};
> > 
> > Do we need the size of the extra space? Can we not infer it anyway by
> > walking the contexts save there? Surely we don't expect more than one
> > extra context.
> 
> Strictly speaking we don't need it.  When userspace parses a signal
> frame generated by the kernel, it can trust the kernel to write a well-
> formed signal frame.
> 
> In sigreturn it allows us to retain a sanity-check on overall size
> similar to what sizeof(__reserved) gives us.  This "feels cleaner"
> to me, but the value of it is debatable, since we can still apply
> SIGFRAME_MAXSZ and uaccess should protect us against gross overruns.

I'm not keen on the size information, it seems superfluous.

BTW, does SIGFRAME_MAXSZ now become ABI? Or the user only needs to
interrogate the frame size and we keep this internal to the kernel?
Dave Martin May 26, 2017, 11:37 a.m. UTC | #4
On Tue, May 23, 2017 at 12:30:19PM +0100, Catalin Marinas wrote:
> On Mon, May 15, 2017 at 02:24:45PM +0100, Dave P Martin wrote:
> > On Fri, May 12, 2017 at 05:57:24PM +0100, Catalin Marinas wrote:
> > > On Wed, Apr 12, 2017 at 06:01:13PM +0100, Dave P Martin wrote:

[...]

> > Originally I preferred not to waste the space and did move fp/lr to the
> > end, but someone (I think you or Will) expressed concern that the fp/lr
> > position relative to the signal frame _might_ count as ABI.
> > 
> > I think it's not that likely that software will be relying on this,
> > since it appears easier just to follow the frame chain than to treat
> > this as a special case.
> > 
> > But it's hard to be certain.  It comes down to a judgement call.
> 
> I would not consider this ABI. The ABI part is that the fp register
> points to where fp/lr were saved.

Fair enough, I'm happy to move it back.  (That was my preferred design
in the first place anyway).

> > > > +#define EXTRA_MAGIC	0x45585401
> > > > +
> > > > +struct extra_context {
> > > > +	struct _aarch64_ctx head;
> > > > +	void __user *data;	/* 16-byte aligned pointer to extra space */
> > > "__user" is a kernel-only attribute, we shouldn't expose it in a uapi
> > > header.
> > 
> > This is filtered out by headers_install, just like #ifdef __KERNEL__.
> 
> Ah, ok, I missed this.

It was a surprise to me too...

> > > > +	__u32 size;		/* size in bytes of the extra space */
> > > > +};
> > > 
> > > Do we need the size of the extra space? Can we not infer it anyway by
> > > walking the contexts save there? Surely we don't expect more than one
> > > extra context.
> > 
> > Strictly speaking we don't need it.  When userspace parses a signal
> > frame generated by the kernel, it can trust the kernel to write a well-
> > formed signal frame.
> > 
> > In sigreturn it allows us to retain a sanity-check on overall size
> > similar to what sizeof(__reserved) gives us.  This "feels cleaner"
> > to me, but the value of it is debatable, since we can still apply
> > SIGFRAME_MAXSZ and uaccess should protect us against gross overruns.
> 
> I'm not keen on the size information, it seems superfluous.

Are you referring to the fact that fp will point to the end of
extra_context, or simply that we don't really need to know the size?


The first is only true of the signal frame.  For a sigcontext on the
heap (e.g., getcontext/setcontext) the frame record has no role and
nothing would point to it, so it's no use for locating the end of
extra_context.  In practice there won't be a frame record in this
situation.

The second is a valid point, just as we don't need to know the size of
the destination buffer of strcpy() or sprintf().  The programmer can
ensure that it's big enough.  Moreover, from the kernel we're also
protected by uaccess.

It's easy to screw up here though.  setcontext assumes that mcontext_t
is fixed-size, so if there is no embedded size information then there
is no way to protect against overruns.  In userspace, there is no
uaccess protection and we can simply walk across the end of a heap
block etc.

> BTW, does SIGFRAME_MAXSZ now become ABI? Or the user only needs to
> interrogate the frame size and we keep this internal to the kernel?

If the kernel rejects extra_contexts that cause this limit to be
exceeded, then yes -- though it will rarely be relevant except in the
case of memory corruption, or if architecture extensions eventually
require a larger frame.

(sve_context could theoretically grow larger then SIGFRAME_MAXSZ all by
itself, but that's unlikely to happen any time soon.)


Userspace could hit SIGFRAME_MAXSZ by constructing a valid sequence of
records that is ridiculously large, by padding out the records: common
sense suggests not to do this, but it's never been documented or
enforced.  I didn't feel comfortable changing the behaviour here to be
more strict.


So, SIGFRAME_MAXSZ should either be given a larger, more future-proof
value ... or otherwise we should perhaps get rid of it entirely.

?

Cheers
---Dave
Catalin Marinas June 5, 2017, 2:17 p.m. UTC | #5
On Fri, May 26, 2017 at 12:37:32PM +0100, Dave P Martin wrote:
> On Tue, May 23, 2017 at 12:30:19PM +0100, Catalin Marinas wrote:
> > On Mon, May 15, 2017 at 02:24:45PM +0100, Dave P Martin wrote:
> > > On Fri, May 12, 2017 at 05:57:24PM +0100, Catalin Marinas wrote:
> > > > On Wed, Apr 12, 2017 at 06:01:13PM +0100, Dave P Martin wrote:
> > > > > +	__u32 size;		/* size in bytes of the extra space */
> > > > > +};
> > > > 
> > > > Do we need the size of the extra space? Can we not infer it anyway by
> > > > walking the contexts save there? Surely we don't expect more than one
> > > > extra context.
> > > 
> > > Strictly speaking we don't need it.  When userspace parses a signal
> > > frame generated by the kernel, it can trust the kernel to write a well-
> > > formed signal frame.
> > > 
> > > In sigreturn it allows us to retain a sanity-check on overall size
> > > similar to what sizeof(__reserved) gives us.  This "feels cleaner"
> > > to me, but the value of it is debatable, since we can still apply
> > > SIGFRAME_MAXSZ and uaccess should protect us against gross overruns.
> > 
> > I'm not keen on the size information, it seems superfluous.
> 
> Are you referring to the fact that fp will point to the end of
> extra_context, or simply that we don't really need to know the size?

The latter, I don't think we need to know the size explicitly, we can
add up the chained structures to calculate it.

> The second is a valid point, just as we don't need to know the size of
> the destination buffer of strcpy() or sprintf().  The programmer can
> ensure that it's big enough.  Moreover, from the kernel we're also
> protected by uaccess.
> 
> It's easy to screw up here though.  setcontext assumes that mcontext_t
> is fixed-size, so if there is no embedded size information then there
> is no way to protect against overruns.  In userspace, there is no
> uaccess protection and we can simply walk across the end of a heap
> block etc.

So you intend size to be the maximum size, not the actual size of the
chained contexts? If that's the case, I think we can keep it, only that
I'd rather have the time size_t or __u64 to avoid implicit padding.

> > BTW, does SIGFRAME_MAXSZ now become ABI? Or the user only needs to
> > interrogate the frame size and we keep this internal to the kernel?
> 
> If the kernel rejects extra_contexts that cause this limit to be
> exceeded, then yes -- though it will rarely be relevant except in the
> case of memory corruption, or if architecture extensions eventually
> require a larger frame.
> 
> (sve_context could theoretically grow larger then SIGFRAME_MAXSZ all by
> itself, but that's unlikely to happen any time soon.)
> 
> Userspace could hit SIGFRAME_MAXSZ by constructing a valid sequence of
> records that is ridiculously large, by padding out the records: common
> sense suggests not to do this, but it's never been documented or
> enforced.  I didn't feel comfortable changing the behaviour here to be
> more strict.
> 
> So, SIGFRAME_MAXSZ should either be given a larger, more future-proof
> value ... or otherwise we should perhaps get rid of it entirely.

If we can, yes, I would get rid of it.
Dave Martin June 6, 2017, 11:37 a.m. UTC | #6
On Mon, Jun 05, 2017 at 03:17:44PM +0100, Catalin Marinas wrote:
> On Fri, May 26, 2017 at 12:37:32PM +0100, Dave P Martin wrote:
> > On Tue, May 23, 2017 at 12:30:19PM +0100, Catalin Marinas wrote:
> > > On Mon, May 15, 2017 at 02:24:45PM +0100, Dave P Martin wrote:
> > > > On Fri, May 12, 2017 at 05:57:24PM +0100, Catalin Marinas wrote:
> > > > > On Wed, Apr 12, 2017 at 06:01:13PM +0100, Dave P Martin wrote:
> > > > > > +	__u32 size;		/* size in bytes of the extra space */
> > > > > > +};
> > > > > 
> > > > > Do we need the size of the extra space? Can we not infer it anyway by
> > > > > walking the contexts save there? Surely we don't expect more than one
> > > > > extra context.
> > > > 
> > > > Strictly speaking we don't need it.  When userspace parses a signal
> > > > frame generated by the kernel, it can trust the kernel to write a well-
> > > > formed signal frame.
> > > > 
> > > > In sigreturn it allows us to retain a sanity-check on overall size
> > > > similar to what sizeof(__reserved) gives us.  This "feels cleaner"
> > > > to me, but the value of it is debatable, since we can still apply
> > > > SIGFRAME_MAXSZ and uaccess should protect us against gross overruns.
> > > 
> > > I'm not keen on the size information, it seems superfluous.
> > 
> > Are you referring to the fact that fp will point to the end of
> > extra_context, or simply that we don't really need to know the size?
> 
> The latter, I don't think we need to know the size explicitly, we can
> add up the chained structures to calculate it.
> 
> > The second is a valid point, just as we don't need to know the size of
> > the destination buffer of strcpy() or sprintf().  The programmer can
> > ensure that it's big enough.  Moreover, from the kernel we're also
> > protected by uaccess.
> > 
> > It's easy to screw up here though.  setcontext assumes that mcontext_t
> > is fixed-size, so if there is no embedded size information then there
> > is no way to protect against overruns.  In userspace, there is no
> > uaccess protection and we can simply walk across the end of a heap
> > block etc.
> 
> So you intend size to be the maximum size, not the actual size of the

Yes, except that when the signal frame is generated by the kernel those
will be the same, since the kernel generates a frame that is only as
large as needed for the records dumped.

I'm thinking of the case where userspace, or a debugger, modifies a
frame obtained from a signal, or similar.  This might involve deleting
or resizing records.  Encoding the original size of the frame may help
code defend against overrun bugs, though obviously it's no guarantee.

> chained contexts? If that's the case, I think we can keep it, only that
> I'd rather have the time size_t or __u64 to avoid implicit padding.

Sure, it can be a u64.  I wanted to avoid the suggestion that the frame
should be that large, but 32 bits already allows it to be crazy large
anyway, so I don't think making it 32-bit helps.

> > > BTW, does SIGFRAME_MAXSZ now become ABI? Or the user only needs to
> > > interrogate the frame size and we keep this internal to the kernel?
> > 
> > If the kernel rejects extra_contexts that cause this limit to be
> > exceeded, then yes -- though it will rarely be relevant except in the
> > case of memory corruption, or if architecture extensions eventually
> > require a larger frame.
> > 
> > (sve_context could theoretically grow larger then SIGFRAME_MAXSZ all by
> > itself, but that's unlikely to happen any time soon.)
> > 
> > Userspace could hit SIGFRAME_MAXSZ by constructing a valid sequence of
> > records that is ridiculously large, by padding out the records: common
> > sense suggests not to do this, but it's never been documented or
> > enforced.  I didn't feel comfortable changing the behaviour here to be
> > more strict.
> > 
> > So, SIGFRAME_MAXSZ should either be given a larger, more future-proof
> > value ... or otherwise we should perhaps get rid of it entirely.
> 
> If we can, yes, I would get rid of it.

If the size field is retained I prefer to keep this, but it's
deliberately not in any header.  This allows the kernel to have a
stricter idea about what is sane, without it formally being ABI.

This is supposed to be a deterrent against people writing signal frame
code manipulation code in a stupid way.  SIGFRAME_MAXSZ should only
ever be increased during maintenance -- it's probably worth adding a
comment on that point.

Cheers
---Dave
Dave Martin June 6, 2017, 1:58 p.m. UTC | #7
On Tue, Jun 06, 2017 at 12:37:53PM +0100, Dave Martin wrote:
> On Mon, Jun 05, 2017 at 03:17:44PM +0100, Catalin Marinas wrote:

[...]

[extra_context.size]

> > I'd rather have the time size_t or __u64 to avoid implicit padding.
> 
> Sure, it can be a u64.  I wanted to avoid the suggestion that the frame
> should be that large, but 32 bits already allows it to be crazy large
> anyway, so I don't think making it 32-bit helps.

Actually, there is still implicit padding even with u64, since the total
size is 16 bytes + sizeof(extra_context.size).

Since u64 is much bigger then we'd ever want, and to avoid introducing
new bugs, do you object to keeping size as u32 and adding explicit
padding instead?

Cheers
---Dave
Catalin Marinas June 6, 2017, 4:15 p.m. UTC | #8
On Tue, Jun 06, 2017 at 02:58:18PM +0100, Dave P Martin wrote:
> On Tue, Jun 06, 2017 at 12:37:53PM +0100, Dave Martin wrote:
> > On Mon, Jun 05, 2017 at 03:17:44PM +0100, Catalin Marinas wrote:
> 
> [...]
> 
> [extra_context.size]
> 
> > > I'd rather have the time size_t or __u64 to avoid implicit padding.
> > 
> > Sure, it can be a u64.  I wanted to avoid the suggestion that the frame
> > should be that large, but 32 bits already allows it to be crazy large
> > anyway, so I don't think making it 32-bit helps.
> 
> Actually, there is still implicit padding even with u64, since the total
> size is 16 bytes + sizeof(extra_context.size).
> 
> Since u64 is much bigger then we'd ever want, and to avoid introducing
> new bugs, do you object to keeping size as u32 and adding explicit
> padding instead?

I missed the extra padding to 16-byte alignment. In this case, I'm fine
with u32.
Catalin Marinas June 6, 2017, 4:15 p.m. UTC | #9
On Tue, Jun 06, 2017 at 12:37:53PM +0100, Dave P Martin wrote:
> On Mon, Jun 05, 2017 at 03:17:44PM +0100, Catalin Marinas wrote:
> > On Fri, May 26, 2017 at 12:37:32PM +0100, Dave P Martin wrote:
> > > On Tue, May 23, 2017 at 12:30:19PM +0100, Catalin Marinas wrote:
> > > > BTW, does SIGFRAME_MAXSZ now become ABI? Or the user only needs to
> > > > interrogate the frame size and we keep this internal to the kernel?
> > > 
> > > If the kernel rejects extra_contexts that cause this limit to be
> > > exceeded, then yes -- though it will rarely be relevant except in the
> > > case of memory corruption, or if architecture extensions eventually
> > > require a larger frame.
> > > 
> > > (sve_context could theoretically grow larger then SIGFRAME_MAXSZ all by
> > > itself, but that's unlikely to happen any time soon.)
> > > 
> > > Userspace could hit SIGFRAME_MAXSZ by constructing a valid sequence of
> > > records that is ridiculously large, by padding out the records: common
> > > sense suggests not to do this, but it's never been documented or
> > > enforced.  I didn't feel comfortable changing the behaviour here to be
> > > more strict.
> > > 
> > > So, SIGFRAME_MAXSZ should either be given a larger, more future-proof
> > > value ... or otherwise we should perhaps get rid of it entirely.
> > 
> > If we can, yes, I would get rid of it.
> 
> If the size field is retained I prefer to keep this, but it's
> deliberately not in any header.  This allows the kernel to have a
> stricter idea about what is sane, without it formally being ABI.
> 
> This is supposed to be a deterrent against people writing signal frame
> code manipulation code in a stupid way.  SIGFRAME_MAXSZ should only
> ever be increased during maintenance -- it's probably worth adding a
> comment on that point.

Fine by me.
Dave Martin June 8, 2017, 8:46 a.m. UTC | #10
On Tue, May 23, 2017 at 12:30:19PM +0100, Catalin Marinas wrote:
> On Mon, May 15, 2017 at 02:24:45PM +0100, Dave P Martin wrote:
> > On Fri, May 12, 2017 at 05:57:24PM +0100, Catalin Marinas wrote:
> > > On Wed, Apr 12, 2017 at 06:01:13PM +0100, Dave P Martin wrote:
> > > > --- a/arch/arm64/include/uapi/asm/sigcontext.h
> > > > +++ b/arch/arm64/include/uapi/asm/sigcontext.h
> > > > @@ -80,4 +80,31 @@ struct esr_context {
> > > >  	__u64 esr;
> > > >  };
> > > >  
> > > > +/*
> > > > + * Pointer to extra space for additional structures that don't fit in
> > > > + * sigcontext.__reserved[].  Note:
> > > > + *
> > > > + * 1) fpsimd_context, esr_context and extra_context must be placed in
> > > > + * sigcontext.__reserved[] if present.  They cannot be placed in the
> > > > + * extra space.  Any other record can be placed either in the extra
> > > > + * space or in sigcontext.__reserved[].
> > > > + *
> > > > + * 2) There must not be more than one extra_context.
> > > > + *
> > > > + * 3) If extra_context is present, it must be followed immediately in
> > > > + * sigcontext.__reserved[] by the terminating null _aarch64_ctx (i.e.,
> > > > + * extra_context must be the last record in sigcontext.__reserved[]
> > > > + * except for the terminator).
> > > > + *
> > > > + * 4) The extra space must itself be terminated with a null
> > > > + * _aarch64_ctx.
> > > > + */
> > > 
> > > IIUC, if we need to save some state that doesn't fit in what's left of
> > > sigcontext.__reserved[] (e.g. SVE with 1024-bit vector length), we
> > > ignore the available space and go for a memory block following the end
> > > of sigcontext.__reserved[] + 16. Is there a reason we can't store the
> > > new state across the end of sigcontext.__reserved[] and move fp/lr at
> > > the end of the new frame? I'm not sure the fp/lr position immediately
> > > after __reserved[] counts as ABI.
> > 
> > This was my original view.
> > 
> > Originally I preferred not to waste the space and did move fp/lr to the
> > end, but someone (I think you or Will) expressed concern that the fp/lr
> > position relative to the signal frame _might_ count as ABI.
> > 
> > I think it's not that likely that software will be relying on this,
> > since it appears easier just to follow the frame chain than to treat
> > this as a special case.
> > 
> > But it's hard to be certain.  It comes down to a judgement call.
> 
> I would not consider this ABI. The ABI part is that the fp register
> points to where fp/lr were saved.

On this point, it looks like the libgcc unwinder

https://gcc.gnu.org/git/?p=gcc.git;a=blob;f=libgcc/config/aarch64/linux-unwind.h;h=d5d6980442fd47b1f1e499e99cb25b5fffbdbeb3;hb=HEAD

doesn't rely on the frame record location.  It clones the (internal)
struct rt_sigframe definition from v3.7, which doesn't include any frame
record, and mines fp and lr out of the signal frame AFAICT.

It appears that gdb and libunwind likely take the same approach, but
I've not looked closely yet.

The frame record in rt_sigframe was added in by Will in 304ef4e83672
("arm64: signal: push the unwinding prologue on the signal stack"),
which changes from pushing the frame record onto the interrupted stack
(which may be inaccessible for a SEGV), to pushing onto the signal
stack.


Even with the frame record split from rt_sigframe, I've not seen any
failed backtrace in gdb.  Throwing an exception from a SEGV handler in
C++ (with -fnon-call-exceptions) also appears to work reliably with
that change, even when the signal frame grows.

In any case, there is no ABI break unless there is extra_context, so
it shouldn't impact current userspace.

Cheers
---Dave
diff mbox

Patch

diff --git a/arch/arm64/include/uapi/asm/sigcontext.h b/arch/arm64/include/uapi/asm/sigcontext.h
index 1328a2c..b5e2523 100644
--- a/arch/arm64/include/uapi/asm/sigcontext.h
+++ b/arch/arm64/include/uapi/asm/sigcontext.h
@@ -80,4 +80,31 @@  struct esr_context {
 	__u64 esr;
 };
 
+/*
+ * Pointer to extra space for additional structures that don't fit in
+ * sigcontext.__reserved[].  Note:
+ *
+ * 1) fpsimd_context, esr_context and extra_context must be placed in
+ * sigcontext.__reserved[] if present.  They cannot be placed in the
+ * extra space.  Any other record can be placed either in the extra
+ * space or in sigcontext.__reserved[].
+ *
+ * 2) There must not be more than one extra_context.
+ *
+ * 3) If extra_context is present, it must be followed immediately in
+ * sigcontext.__reserved[] by the terminating null _aarch64_ctx (i.e.,
+ * extra_context must be the last record in sigcontext.__reserved[]
+ * except for the terminator).
+ *
+ * 4) The extra space must itself be terminated with a null
+ * _aarch64_ctx.
+ */
+#define EXTRA_MAGIC	0x45585401
+
+struct extra_context {
+	struct _aarch64_ctx head;
+	void __user *data;	/* 16-byte aligned pointer to extra space */
+	__u32 size;		/* size in bytes of the extra space */
+};
+
 #endif /* _UAPI__ASM_SIGCONTEXT_H */
diff --git a/arch/arm64/kernel/signal.c b/arch/arm64/kernel/signal.c
index 47592af..95547e1 100644
--- a/arch/arm64/kernel/signal.c
+++ b/arch/arm64/kernel/signal.c
@@ -25,6 +25,7 @@ 
 #include <linux/freezer.h>
 #include <linux/stddef.h>
 #include <linux/uaccess.h>
+#include <linux/sizes.h>
 #include <linux/string.h>
 #include <linux/tracehook.h>
 #include <linux/ratelimit.h>
@@ -56,18 +57,27 @@  struct rt_sigframe_user_layout {
 
 	unsigned long fpsimd_offset;
 	unsigned long esr_offset;
+	unsigned long extra_offset;
 	unsigned long end_offset;
 };
 
+#define BASE_SIGFRAME_SIZE round_up(sizeof(struct rt_sigframe), 16)
+#define TERMINATOR_SIZE round_up(sizeof(struct _aarch64_ctx), 16)
+#define EXTRA_CONTEXT_SIZE round_up(sizeof(struct extra_context), 16)
+
 static void init_user_layout(struct rt_sigframe_user_layout *user)
 {
+	const size_t reserved_size =
+		sizeof(user->sigframe->uc.uc_mcontext.__reserved);
+
 	memset(user, 0, sizeof(*user));
 	user->size = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved);
 
-	user->limit = user->size +
-		sizeof(user->sigframe->uc.uc_mcontext.__reserved) -
-		round_up(sizeof(struct _aarch64_ctx), 16);
-		/* ^ reserve space for terminator */
+	user->limit = user->size + reserved_size;
+
+	user->limit -= TERMINATOR_SIZE;
+	user->limit -= EXTRA_CONTEXT_SIZE;
+	/* Reserve space for extension and terminator ^ */
 }
 
 static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
@@ -75,6 +85,48 @@  static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
 	return round_up(max(user->size, sizeof(struct rt_sigframe)), 16);
 }
 
+/* Sanity limit on the maximum size of signal frame we'll try to generate. */
+/* This is NOT ABI. */
+#define SIGFRAME_MAXSZ SZ_64K
+
+static int __sigframe_alloc(struct rt_sigframe_user_layout *user,
+			    unsigned long *offset, size_t size, bool extend)
+{
+	size_t padded_size = round_up(size, 16);
+
+	if (padded_size > user->limit - user->size &&
+	    !user->extra_offset &&
+	    extend) {
+		int ret;
+
+		ret = __sigframe_alloc(user, &user->extra_offset,
+				       sizeof(struct extra_context), false);
+		if (ret)
+			return ret;
+
+		/*
+		 * Further allocations must go after the fixed-size
+		 * part of the signal frame:
+		 */
+		user->size = BASE_SIGFRAME_SIZE;
+
+		/*
+		 * Allow expansion up to SIGFRAME_MAXSZ, ensuring space for
+		 * the terminator:
+		 */
+		user->limit = SIGFRAME_MAXSZ - TERMINATOR_SIZE;
+	}
+
+	/* Still not enough space?  Bad luck! */
+	if (padded_size > user->limit - user->size)
+		return -ENOMEM;
+
+	*offset = user->size;
+	user->size += padded_size;
+
+	return 0;
+}
+
 /*
  * Allocate space for an optional record of <size> bytes in the user
  * signal frame.  The offset from the signal frame base address to the
@@ -83,11 +135,24 @@  static size_t sigframe_size(struct rt_sigframe_user_layout const *user)
 static int sigframe_alloc(struct rt_sigframe_user_layout *user,
 			  unsigned long *offset, size_t size)
 {
-	size_t padded_size = round_up(size, 16);
+	return __sigframe_alloc(user, offset, size, true);
+}
 
-	*offset = user->size;
-	user->size += padded_size;
+/* Allocate the null terminator record and prevent further allocations */
+static int sigframe_alloc_end(struct rt_sigframe_user_layout *user)
+{
+	int ret;
+
+	/* Un-reserve the space reserved for the terminator: */
+	user->limit += TERMINATOR_SIZE;
+
+	ret = sigframe_alloc(user, &user->end_offset,
+			     sizeof(struct _aarch64_ctx));
+	if (ret)
+		return ret;
 
+	/* Prevent further allocation: */
+	user->limit = user->size;
 	return 0;
 }
 
@@ -314,17 +379,7 @@  static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)
 			return err;
 	}
 
-	/*
-	 * Allocate space for the terminator record.
-	 * HACK: here we undo the reservation of space for the end record.
-	 * This bodge should be replaced with a cleaner approach later on.
-	 */
-	user->limit = offsetof(struct rt_sigframe, uc.uc_mcontext.__reserved) +
-		sizeof(user->sigframe->uc.uc_mcontext.__reserved);
-
-	err = sigframe_alloc(user, &user->end_offset,
-			     sizeof(struct _aarch64_ctx));
-	return err;
+	return sigframe_alloc_end(user);
 }
 
 
@@ -365,6 +420,26 @@  static int setup_sigframe(struct rt_sigframe_user_layout *user,
 		__put_user_error(current->thread.fault_code, &esr_ctx->esr, err);
 	}
 
+	if (err == 0 && user->extra_offset) {
+		struct extra_context __user *extra =
+			apply_user_offset(user, user->extra_offset);
+		struct _aarch64_ctx __user *end =
+			(struct _aarch64_ctx __user *)((char __user *)extra +
+				round_up(sizeof(*extra), 16));
+		void __user *extra_data =
+			apply_user_offset(user, BASE_SIGFRAME_SIZE);
+		u32 extra_size = round_up(user->size, 16) - BASE_SIGFRAME_SIZE;
+
+		__put_user_error(EXTRA_MAGIC, &extra->head.magic, err);
+		__put_user_error(sizeof(*extra), &extra->head.size, err);
+		__put_user_error(extra_data, &extra->data, err);
+		__put_user_error(extra_size, &extra->size, err);
+
+		/* Add the terminator */
+		__put_user_error(0, &end->magic, err);
+		__put_user_error(0, &end->size, err);
+	}
+
 	/* set the "end" magic */
 	if (err == 0) {
 		struct _aarch64_ctx __user *end =