This series supersedes a prior RFC [1].
This series refactors the arm64 signal handling implementation in order
to make it easier to add support for new CPU architecture extensions,
which may require new records in the signal frame and may require the
signal frame to grow beyond its current size.
This series provides infrastructure that will be needed in order to
support the Scalable Vector Extension (SVE) [2] (patches to be posted
separately).
LTP testing is ongoing -- I will ping this series when I have results.
Changes since RFC v2:
* The frame link record has been moved back to the end of the full
signal frame, instead of fixing its position at the end of
__reserved[], after Catalin raised concerns about the space otherwise
wasted by putting the extended signal frame data after it.
Previous versions of the series had this behaviour, but concerns were
expressed by reviewers about the potential ABI impact.
This change has been informally tested for ABI impacts using the
libgcc unwinder (exercised by throwing exceptions from a signal
handler with g++ -fnon-call-exceptions) and gdb backtracing, neither
of which showed any problem. Review of the libgcc and libunwind
implementations and discussion with gdb developers suggest that no
breakage is expected as a result of the change.
(See patch 1 of this posting.)
* Patches 4 (Allocate extra sigcontext space as needed) and 5 (Parse
extra_context during sigreturn) of RFC v2 have been merged, since
they are not fully bisectable and make more sense as a single patch.
* Patch 6 (Report signal frame size to userspace via auxv) of RFC v2
has been dropped from the series, because this creates ABI that we
don't need yet, and can benefit from further discussion. This patch
will become relevant when merging support for SVE.
* struct extra_context is explicitly padded up to a multiple of 16 bytes.
This fixes a bug whereby a non-extra_context-aware unwinder would
skip the wrong amount of data after extra_context and miss the
terminator record.
There is a de facto assumption that unwinders do not need to round
_aarch64_ctx.size up to a multiple of 16 when stepping through the
records in the signal frame, though this isn't documented anywhere.
Original blurb:
An architecture advertises the maximum possible signal frame size via
the MINSIGSTKSZ #define (mandated by POSIX).
However, CPU architecture extensions may increase the amount of space
required to store the interrupted context when a signal is delivered.
Eventually the amount of space needed in the signal frame may exceed
MINSIGSTKSZ -- whether and when this happens is largely a matter of
luck, depending on the initial guess for MINSIGSTKSZ and the evolution
of that particular CPU architecture. Unfortunately MINSIGSTKSZ cannot
be changed without an ABI break, and POSIX provides no mechanism for
migration.
arm64 initially reserved 4KB of space in the signal frame for
extensions, of which about 0.5KB is allocated to the FP/SIMD registers
initially.
Depending on the vector length supported by the hardware, SVE requires
up to around 8KB of space to store the full SIMD register context, which
is too large to fit in the existing frame.
This series adds a mechanism for optionally enlarging the signal frame
(patches 4-5) and reporting the actual maximum signal frame size to
userspace (patch 6). Patches 1-3 do some refactoring to support this
change by abstracting the way signal frame records are allocated onto
the user stack.
Full backwards compatibility is not possible -- there is no way to hide
the fact that the signal frame has grown -- so it is expected that
support for new architecture extensions that can cause the signal frame
to grow will be opt-in for userspace, in addition to using the extension
mechanism defined by this series.
[1] [RFC PATCH v2 0/6] Signal frame expansion support
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-April/501163.html
[2] ARM Scalable Vector Extension
https://community.arm.com/groups/processors/blog/2016/08/22/technology-update-the-scalable-vector-extension-sve-for-the-armv8-a-architecturehttps://developer.arm.com/docs/ddi0584/latest/arm-architecture-reference-manual-supplement-the-scalable-vector-extension-sve-for-armv8-a
Dave Martin (5):
arm64: signal: split frame link record from sigcontext structure
arm64: signal: Refactor sigcontext parsing in rt_sigreturn
arm64: signal: factor frame layout and population into separate passes
arm64: signal: factor out signal frame record allocation
arm64: signal: Allow expansion of the signal frame
arch/arm64/include/uapi/asm/sigcontext.h | 55 +++++
arch/arm64/kernel/signal.c | 408 ++++++++++++++++++++++++++++---
2 files changed, 427 insertions(+), 36 deletions(-)
--
2.1.4
In order to be able to increase the amount of the data currently
written to the __reserved[] array in the signal frame, it is
necessary to overwrite the locations currently occupied by the
{fp,lr} frame link record pushed at the top of the signal stack.
In order for this to work, this patch detaches the frame link
record from struct rt_sigframe and places it separately at the top
of the signal stack. This will allow subsequent patches to insert
data between it and __reserved[].
This change relies on the non-ABI status of the placement of the
frame record with respect to struct sigframe: this status is
undocumented, but the placement is not declared or described in the
user headers, and known unwinder implementations (libgcc,
libunwind, gdb) appear not to rely on it.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kernel/signal.c | 50 +++++++++++++++++++++++++++++-----------------
1 file changed, 32 insertions(+), 18 deletions(-)
@@ -162,16 +171,17 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)return0;}-staticintsetup_sigframe(structrt_sigframe__user*sf,+staticintsetup_sigframe(structrt_sigframe_user_layout*user,structpt_regs*regs,sigset_t*set){inti,err=0;+structrt_sigframe__user*sf=user->sigframe;void*aux=sf->uc.uc_mcontext.__reserved;struct_aarch64_ctx*end;/* set up the stack frame for unwinding */-__put_user_error(regs->regs[29],&sf->fp,err);-__put_user_error(regs->regs[30],&sf->lr,err);+__put_user_error(regs->regs[29],&user->next_frame->fp,err);+__put_user_error(regs->regs[30],&user->next_frame->lr,err);for(i=0;i<31;i++)__put_user_error(regs->regs[i],&sf->uc.uc_mcontext.regs[i],
@@ -209,34 +219,36 @@ static int setup_sigframe(struct rt_sigframe __user *sf,returnerr;}-staticstructrt_sigframe__user*get_sigframe(structksignal*ksig,-structpt_regs*regs)+staticintget_sigframe(structrt_sigframe_user_layout*user,+structksignal*ksig,structpt_regs*regs){unsignedlongsp,sp_top;-structrt_sigframe__user*frame;sp=sp_top=sigsp(regs->sp,ksig);-sp=(sp-sizeof(structrt_sigframe))&~15;-frame=(structrt_sigframe__user*)sp;+sp=round_down(sp-sizeof(structframe_record),16);+user->next_frame=(structframe_record__user*)sp;++sp=round_down(sp-sizeof(structrt_sigframe),16);+user->sigframe=(structrt_sigframe__user*)sp;/**Checkthatwecanactuallywritetothesignalframe.*/-if(!access_ok(VERIFY_WRITE,frame,sp_top-sp))-frame=NULL;+if(!access_ok(VERIFY_WRITE,user->sigframe,sp_top-sp))+return-EFAULT;-returnframe;+return0;}staticvoidsetup_return(structpt_regs*regs,structk_sigaction*ka,-void__user*frame,intusig)+structrt_sigframe_user_layout*user,intusig){__sigrestore_tsigtramp;regs->regs[0]=usig;-regs->sp=(unsignedlong)frame;-regs->regs[29]=regs->sp+offsetof(structrt_sigframe,fp);+regs->sp=(unsignedlong)user->sigframe;+regs->regs[29]=(unsignedlong)&user->next_frame->fp;regs->pc=(unsignedlong)ka->sa.sa_handler;if(ka->sa.sa_flags&SA_RESTORER)
Currently, rt_sigreturn does very limited checking on the
sigcontext coming from userspace.
Future additions to the sigcontext data will increase the potential
for surprises. Also, it is not clear whether the sigcontext
extension records are supposed to occur in a particular order.
To allow the parsing code to be extended more easily, this patch
factors out the sigcontext parsing into a separate function, and
adds extra checks to validate the well-formedness of the sigcontext
structure.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kernel/signal.c | 86 ++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 80 insertions(+), 6 deletions(-)
In preparation for expanding the signal frame, this patch refactors
the signal frame setup code in setup_sigframe() into two separate
passes.
The first pass, setup_sigframe_layout(), determines the size of the
signal frame and its internal layout, including the presence and
location of optional records. The resulting knowledge is used to
allocate and locate the user stack space required for the signal
frame and to determine which optional records to include.
The second pass, setup_sigframe(), is called once the stack frame
is allocated in order to populate it with the necessary context
information.
As a result of these changes, it becomes more natural to represent
locations in the signal frame by a base pointer and an offset,
since the absolute address of each location is not known during the
layout pass. To be more consistent with this logic,
parse_user_sigframe() is refactored to describe signal frame
locations in a similar way.
This change has no effect on the signal ABI, but will make it
easier to expand the signal frame in future patches.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/kernel/signal.c | 111 +++++++++++++++++++++++++++++++++++----------
1 file changed, 88 insertions(+), 23 deletions(-)
@@ -53,8 +54,39 @@ struct frame_record {structrt_sigframe_user_layout{structrt_sigframe__user*sigframe;structframe_record__user*next_frame;++unsignedlongsize;/* size of allocated sigframe data */+unsignedlonglimit;/* largest allowed size */++unsignedlongfpsimd_offset;+unsignedlongesr_offset;+unsignedlongend_offset;};+staticvoidinit_user_layout(structrt_sigframe_user_layout*user)+{+memset(user,0,sizeof(*user));+user->size=offsetof(structrt_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 */+}++staticsize_tsigframe_size(structrt_sigframe_user_layoutconst*user)+{+returnround_up(max(user->size,sizeof(structrt_sigframe)),16);+}++staticvoid__user*apply_user_offset(+structrt_sigframe_user_layoutconst*user,unsignedlongoffset)+{+char__user*base=(char__user*)user->sigframe;++returnbase+offset;+}+staticintpreserve_fpsimd_context(structfpsimd_context__user*ctx){structfpsimd_state*fpsimd=¤t->thread.fpsimd_state;
@@ -110,26 +142,35 @@ static int parse_user_sigframe(struct user_ctxs *user,structrt_sigframe__user*sf){structsigcontext__user*constsc=&sf->uc.uc_mcontext;-struct_aarch64_ctx__user*head=-(struct_aarch64_ctx__user*)&sc->__reserved;+struct_aarch64_ctx__user*head;+char__user*base=(char__user*)&sc->__reserved;size_toffset=0;+size_tlimit=sizeof(sc->__reserved);user->fpsimd=NULL;+if(!IS_ALIGNED((unsignedlong)base,16))+gotoinvalid;+while(1){-interr;+interr=0;u32magic,size;-head=(struct_aarch64_ctx__user*)&sc->__reserved[offset];-if(!IS_ALIGNED((unsignedlong)head,16))+if(limit-offset<sizeof(*head))gotoinvalid;-err=0;+if(!IS_ALIGNED(offset,16))+gotoinvalid;++head=(struct_aarch64_ctx__user*)(base+offset);__get_user_error(magic,&head->magic,err);__get_user_error(size,&head->size,err);if(err)returnerr;+if(limit-offset<size)+gotoinvalid;+switch(magic){case0:if(size)
@@ -141,9 +182,7 @@ static int parse_user_sigframe(struct user_ctxs *user,if(user->fpsimd)gotoinvalid;-if(offset>sizeof(sc->__reserved)--sizeof(*user->fpsimd)||-size<sizeof(*user->fpsimd))+if(size<sizeof(*user->fpsimd))gotoinvalid;user->fpsimd=(structfpsimd_context__user*)head;
@@ -160,7 +199,7 @@ static int parse_user_sigframe(struct user_ctxs *user,if(size<sizeof(*head))gotoinvalid;-if(size>sizeof(sc->__reserved)-(sizeof(*head)+offset))+if(limit-offset<size)gotoinvalid;offset+=size;
@@ -245,13 +284,30 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)return0;}+/* Determine the layout of optional records in the signal frame */+staticintsetup_sigframe_layout(structrt_sigframe_user_layout*user)+{+user->fpsimd_offset=user->size;+user->size+=round_up(sizeof(structfpsimd_context),16);++/* fault information, if valid */+if(current->thread.fault_code){+user->esr_offset=user->size;+user->size+=round_up(sizeof(structesr_context),16);+}++/* set the "end" magic */+user->end_offset=user->size;++return0;+}++staticintsetup_sigframe(structrt_sigframe_user_layout*user,structpt_regs*regs,sigset_t*set){inti,err=0;structrt_sigframe__user*sf=user->sigframe;-void*aux=sf->uc.uc_mcontext.__reserved;-struct_aarch64_ctx*end;/* set up the stack frame for unwinding */__put_user_error(regs->regs[29],&user->next_frame->fp,err);
@@ -269,26 +325,29 @@ static int setup_sigframe(struct rt_sigframe_user_layout *user,err|=__copy_to_user(&sf->uc.uc_sigmask,set,sizeof(*set));if(err==0){-structfpsimd_context*fpsimd_ctx=-container_of(aux,structfpsimd_context,head);+structfpsimd_context__user*fpsimd_ctx=+apply_user_offset(user,user->fpsimd_offset);err|=preserve_fpsimd_context(fpsimd_ctx);-aux+=sizeof(*fpsimd_ctx);}/* fault information, if valid */-if(current->thread.fault_code){-structesr_context*esr_ctx=-container_of(aux,structesr_context,head);+if(err==0&&user->esr_offset){+structesr_context__user*esr_ctx=+apply_user_offset(user,user->esr_offset);+__put_user_error(ESR_MAGIC,&esr_ctx->head.magic,err);__put_user_error(sizeof(*esr_ctx),&esr_ctx->head.size,err);__put_user_error(current->thread.fault_code,&esr_ctx->esr,err);-aux+=sizeof(*esr_ctx);}/* set the "end" magic */-end=aux;-__put_user_error(0,&end->magic,err);-__put_user_error(0,&end->size,err);+if(err==0){+struct_aarch64_ctx__user*end=+apply_user_offset(user,user->end_offset);++__put_user_error(0,&end->magic,err);+__put_user_error(0,&end->size,err);+}returnerr;}
@@ -297,13 +356,19 @@ static int get_sigframe(struct rt_sigframe_user_layout *user,structksignal*ksig,structpt_regs*regs){unsignedlongsp,sp_top;+interr;++init_user_layout(user);+err=setup_sigframe_layout(user);+if(err)+returnerr;sp=sp_top=sigsp(regs->sp,ksig);sp=round_down(sp-sizeof(structframe_record),16);user->next_frame=(structframe_record__user*)sp;-sp=round_down(sp-sizeof(structrt_sigframe),16);+sp=round_down(sp,16)-sigframe_size(user);user->sigframe=(structrt_sigframe__user*)sp;/*
This patch factors out the allocator for signal frame optional
records into a separate function, to ensure consistency and
facilitate later expansion.
No overrun checking is currently done, because the allocation is in
user memory and anyway the kernel never tries to allocate enough
space in the signal frame yet for an overrun to occur. This
behaviour will be refined in future patches.
The approach taken in this patch to allocation of the terminator
record is not very clean: this will also be replaced in subsequent
patches.
For future extension, a comment is added in sigcontext.h
documenting the current static allocations in __reserved[]. This
will be important for determining under what circumstances
userspace may or may not see an expanded signal frame.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/uapi/asm/sigcontext.h | 19 ++++++++++++++
arch/arm64/kernel/signal.c | 43 ++++++++++++++++++++++++++------
2 files changed, 55 insertions(+), 7 deletions(-)
@@ -287,19 +303,32 @@ asmlinkage long sys_rt_sigreturn(struct pt_regs *regs)/* Determine the layout of optional records in the signal frame */staticintsetup_sigframe_layout(structrt_sigframe_user_layout*user){-user->fpsimd_offset=user->size;-user->size+=round_up(sizeof(structfpsimd_context),16);+interr;++err=sigframe_alloc(user,&user->fpsimd_offset,+sizeof(structfpsimd_context));+if(err)+returnerr;/* fault information, if valid */if(current->thread.fault_code){-user->esr_offset=user->size;-user->size+=round_up(sizeof(structesr_context),16);+err=sigframe_alloc(user,&user->esr_offset,+sizeof(structesr_context));+if(err)+returnerr;}-/* set the "end" magic */-user->end_offset=user->size;+/*+*Allocatespacefortheterminatorrecord.+*HACK:hereweundothereservationofspacefortheendrecord.+*Thisbodgeshouldbereplacedwithacleanerapproachlateron.+*/+user->limit=offsetof(structrt_sigframe,uc.uc_mcontext.__reserved)++sizeof(user->sigframe->uc.uc_mcontext.__reserved);-return0;+err=sigframe_alloc(user,&user->end_offset,+sizeof(struct_aarch64_ctx));+returnerr;}
This patch defines an extra_context signal frame record that can be
used to describe an expanded signal frame, and modifies the context
block allocator and signal frame setup and parsing code to create,
populate, parse and decode this block as necessary.
To avoid abuse by userspace, parse_user_sigframe() attempts to
ensure that:
* no more than one extra_context is accepted;
* the extra context data is a sensible size, and properly placed
and aligned.
The extra_context data is required to start at the first 16-byte
aligned address immediately after the dummy terminator record
following extra_context in rt_sigframe.__reserved[] (as ensured
during signal delivery). This serves as a sanity-check that the
signal frame has not been moved or copied without taking the extra
data into account.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
arch/arm64/include/uapi/asm/sigcontext.h | 38 ++++++-
arch/arm64/kernel/signal.c | 190 ++++++++++++++++++++++++++++---
2 files changed, 209 insertions(+), 19 deletions(-)
@@ -80,4 +81,39 @@ struct esr_context {__u64esr;};+/*+*extra_context:describesextraspaceinthesignalframefor+*additionalstructuresthatdon'tfitinsigcontext.__reserved[].+*+*Note:+*+*1)fpsimd_context,esr_contextandextra_contextmustbeplacedin+*sigcontext.__reserved[]ifpresent.Theycannotbeplacedinthe+*extraspace.Anyotherrecordcanbeplacedeitherintheextra+*spaceorinsigcontext.__reserved[],unlessotherwisespecifiedin+*thisfile.+*+*2)Theremustnotbemorethanoneextra_context.+*+*3)Ifextra_contextispresent,itmustbefollowedimmediatelyin+*sigcontext.__reserved[]bytheterminatingnull_aarch64_ctx.+*+*4)Theextraspacetowhichdatapointsmuststartatthefirst+*16-bytealignedaddressimmediatelyaftertheterminatingnull+*_aarch64_ctxthatfollowstheextra_contextstructurein+*__reserved[].Theextraspacemayoverruntheendof__reserved[],+*asindicatedbyasufficientlylargevalueforthesizefield.+*+*5)Theextraspacemustitselfbeterminatedwithanull+*_aarch64_ctx.+*/+#define EXTRA_MAGIC 0x45585401++structextra_context{+struct_aarch64_ctxhead;+void__user*data;/* 16-byte aligned pointer to extra space */+__u32size;/* size in bytes of the extra space */+__u32__reserved[3];+};+#endif /* _UAPI__ASM_SIGCONTEXT_H */
@@ -60,18 +61,27 @@ struct rt_sigframe_user_layout {unsignedlongfpsimd_offset;unsignedlongesr_offset;+unsignedlongextra_offset;unsignedlongend_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)+staticvoidinit_user_layout(structrt_sigframe_user_layout*user){+constsize_treserved_size=+sizeof(user->sigframe->uc.uc_mcontext.__reserved);+memset(user,0,sizeof(*user));user->size=offsetof(structrt_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 ^ */}staticsize_tsigframe_size(structrt_sigframe_user_layoutconst*user)
@@ -80,6 +90,52 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)}/*+*Sanitylimitontheapproximatemaximumsizeofsignalframewe'll+*trytogenerate.Stackalignmentpaddingandtheframerecordare+*nottakenintoaccount.Thislimitisnotaguaranteeandis+*NOTABI.+*/+#define SIGFRAME_MAXSZ SZ_64K++staticint__sigframe_alloc(structrt_sigframe_user_layout*user,+unsignedlong*offset,size_tsize,boolextend)+{+size_tpadded_size=round_up(size,16);++if(padded_size>user->limit-user->size&&+!user->extra_offset&&+extend){+intret;++user->limit+=EXTRA_CONTEXT_SIZE;+ret=__sigframe_alloc(user,&user->extra_offset,+sizeof(structextra_context),false);+if(ret){+user->limit-=EXTRA_CONTEXT_SIZE;+returnret;+}++/* Reserve space for the __reserved[] terminator */+user->size+=TERMINATOR_SIZE;++/*+*AllowexpansionuptoSIGFRAME_MAXSZ,ensuringspacefor+*theterminator:+*/+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;++return0;+}++/**Allocatespaceforanoptionalrecordof<size>bytesintheuser*signalframe.Theoffsetfromthesignalframebaseaddresstothe*allocatedblockisassignedto*offset.
@@ -87,11 +143,24 @@ static size_t sigframe_size(struct rt_sigframe_user_layout const *user)staticintsigframe_alloc(structrt_sigframe_user_layout*user,unsignedlong*offset,size_tsize){-size_tpadded_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 */+staticintsigframe_alloc_end(structrt_sigframe_user_layout*user)+{+intret;++/* 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)+returnret;+/* Prevent further allocation: */+user->limit=user->size;return0;}
@@ -162,6 +231,8 @@ static int parse_user_sigframe(struct user_ctxs *user,char__user*base=(char__user*)&sc->__reserved;size_toffset=0;size_tlimit=sizeof(sc->__reserved);+boolhave_extra_context=false;+charconst__user*constsfp=(charconst__user*)sf;user->fpsimd=NULL;
@@ -171,6 +242,12 @@ static int parse_user_sigframe(struct user_ctxs *user,while(1){interr=0;u32magic,size;+charconst__user*userp;+structextra_contextconst__user*extra;+void__user*extra_data;+u32extra_size;+struct_aarch64_ctxconst__user*end;+u32end_magic,end_size;if(limit-offset<sizeof(*head))gotoinvalid;
@@ -208,6 +285,64 @@ static int parse_user_sigframe(struct user_ctxs *user,/* ignore */break;+caseEXTRA_MAGIC:+if(have_extra_context)+gotoinvalid;++if(size<sizeof(*extra))+gotoinvalid;++userp=(charconst__user*)head;++extra=(structextra_contextconst__user*)userp;+userp+=size;++__get_user_error(extra_data,&extra->data,err);+__get_user_error(extra_size,&extra->size,err);+if(err)+returnerr;++/* Check for the dummy terminator in __reserved[]: */++if(limit-offset-size<TERMINATOR_SIZE)+gotoinvalid;++end=(struct_aarch64_ctxconst__user*)userp;+userp+=TERMINATOR_SIZE;++__get_user_error(end_magic,&end->magic,err);+__get_user_error(end_size,&end->size,err);+if(err)+returnerr;++if(end_magic||end_size)+gotoinvalid;++/* Prevent looping/repeated parsing of extra_context */+have_extra_context=true;++base=extra_data;+if(!IS_ALIGNED((unsignedlong)base,16))+gotoinvalid;++if(!IS_ALIGNED(extra_size,16))+gotoinvalid;++if(extra_data!=userp)+gotoinvalid;++/* Reject "unreasonably large" frames: */+if(extra_size>sfp+SIGFRAME_MAXSZ-userp)+gotoinvalid;++/*+*Ignoretrailingterminatorin__reserved[]+*andstartparsingextra_data:+*/+offset=0;+limit=extra_size;+continue;+default:gotoinvalid;}
@@ -318,17 +453,7 @@ static int setup_sigframe_layout(struct rt_sigframe_user_layout *user)returnerr;}-/*-*Allocatespacefortheterminatorrecord.-*HACK:hereweundothereservationofspacefortheendrecord.-*Thisbodgeshouldbereplacedwithacleanerapproachlateron.-*/-user->limit=offsetof(structrt_sigframe,uc.uc_mcontext.__reserved)+-sizeof(user->sigframe->uc.uc_mcontext.__reserved);--err=sigframe_alloc(user,&user->end_offset,-sizeof(struct_aarch64_ctx));-returnerr;+returnsigframe_alloc_end(user);}
@@ -369,6 +494,35 @@ 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){+char__user*sfp=(char__user*)user->sigframe;+char__user*userp=+apply_user_offset(user,user->extra_offset);++structextra_context__user*extra;+struct_aarch64_ctx__user*end;+void__user*extra_data;+u32extra_size;++extra=(structextra_context__user*)userp;+userp+=EXTRA_CONTEXT_SIZE;++end=(struct_aarch64_ctx__user*)userp;+userp+=TERMINATOR_SIZE;++extra_data=userp;+extra_size=sfp+round_up(user->size,16)-userp;++__put_user_error(EXTRA_MAGIC,&extra->head.magic,err);+__put_user_error(EXTRA_CONTEXT_SIZE,&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=
On Thu, Jun 15, 2017 at 03:03:38PM +0100, Dave P Martin wrote:
In order to be able to increase the amount of the data currently
written to the __reserved[] array in the signal frame, it is
necessary to overwrite the locations currently occupied by the
{fp,lr} frame link record pushed at the top of the signal stack.
In order for this to work, this patch detaches the frame link
record from struct rt_sigframe and places it separately at the top
of the signal stack. This will allow subsequent patches to insert
data between it and __reserved[].
This change relies on the non-ABI status of the placement of the
frame record with respect to struct sigframe: this status is
undocumented, but the placement is not declared or described in the
user headers, and known unwinder implementations (libgcc,
libunwind, gdb) appear not to rely on it.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
I'm fine with this approach:
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
On Thu, Jun 15, 2017 at 03:03:39PM +0100, Dave P Martin wrote:
Currently, rt_sigreturn does very limited checking on the
sigcontext coming from userspace.
Future additions to the sigcontext data will increase the potential
for surprises. Also, it is not clear whether the sigcontext
extension records are supposed to occur in a particular order.
To allow the parsing code to be extended more easily, this patch
factors out the sigcontext parsing into a separate function, and
adds extra checks to validate the well-formedness of the sigcontext
structure.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
On Thu, Jun 15, 2017 at 03:03:40PM +0100, Dave P Martin wrote:
In preparation for expanding the signal frame, this patch refactors
the signal frame setup code in setup_sigframe() into two separate
passes.
The first pass, setup_sigframe_layout(), determines the size of the
signal frame and its internal layout, including the presence and
location of optional records. The resulting knowledge is used to
allocate and locate the user stack space required for the signal
frame and to determine which optional records to include.
The second pass, setup_sigframe(), is called once the stack frame
is allocated in order to populate it with the necessary context
information.
As a result of these changes, it becomes more natural to represent
locations in the signal frame by a base pointer and an offset,
since the absolute address of each location is not known during the
layout pass. To be more consistent with this logic,
parse_user_sigframe() is refactored to describe signal frame
locations in a similar way.
This change has no effect on the signal ABI, but will make it
easier to expand the signal frame in future patches.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
On Thu, Jun 15, 2017 at 03:03:41PM +0100, Dave P Martin wrote:
This patch factors out the allocator for signal frame optional
records into a separate function, to ensure consistency and
facilitate later expansion.
No overrun checking is currently done, because the allocation is in
user memory and anyway the kernel never tries to allocate enough
space in the signal frame yet for an overrun to occur. This
behaviour will be refined in future patches.
The approach taken in this patch to allocation of the terminator
record is not very clean: this will also be replaced in subsequent
patches.
For future extension, a comment is added in sigcontext.h
documenting the current static allocations in __reserved[]. This
will be important for determining under what circumstances
userspace may or may not see an expanded signal frame.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
On Thu, Jun 15, 2017 at 03:03:42PM +0100, Dave P Martin wrote:
This patch defines an extra_context signal frame record that can be
used to describe an expanded signal frame, and modifies the context
block allocator and signal frame setup and parsing code to create,
populate, parse and decode this block as necessary.
To avoid abuse by userspace, parse_user_sigframe() attempts to
ensure that:
* no more than one extra_context is accepted;
* the extra context data is a sensible size, and properly placed
and aligned.
The extra_context data is required to start at the first 16-byte
aligned address immediately after the dummy terminator record
following extra_context in rt_sigframe.__reserved[] (as ensured
during signal delivery). This serves as a sanity-check that the
signal frame has not been moved or copied without taking the extra
data into account.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
From: Dave.Martin@arm.com (Dave P Martin) Date: 2017-06-16 09:30:50
On Thu, Jun 15, 2017 at 05:37:47PM +0100, Catalin Marinas wrote:
On Thu, Jun 15, 2017 at 03:03:38PM +0100, Dave P Martin wrote:
quoted
In order to be able to increase the amount of the data currently
written to the __reserved[] array in the signal frame, it is
necessary to overwrite the locations currently occupied by the
{fp,lr} frame link record pushed at the top of the signal stack.
In order for this to work, this patch detaches the frame link
record from struct rt_sigframe and places it separately at the top
of the signal stack. This will allow subsequent patches to insert
data between it and __reserved[].
This change relies on the non-ABI status of the placement of the
frame record with respect to struct sigframe: this status is
undocumented, but the placement is not declared or described in the
user headers, and known unwinder implementations (libgcc,
libunwind, gdb) appear not to rely on it.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
I'm fine with this approach:
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Thanks for the review.
Final testing should be complete early next week.
Cheers
---Dave
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.
@@ -80,4 +81,39 @@ struct esr_context {__u64esr;};+/*+*extra_context:describesextraspaceinthesignalframefor+*additionalstructuresthatdon'tfitinsigcontext.__reserved[].+*+*Note:+*+*1)fpsimd_context,esr_contextandextra_contextmustbeplacedin+*sigcontext.__reserved[]ifpresent.Theycannotbeplacedinthe+*extraspace.Anyotherrecordcanbeplacedeitherintheextra+*spaceorinsigcontext.__reserved[],unlessotherwisespecifiedin+*thisfile.+*+*2)Theremustnotbemorethanoneextra_context.+*+*3)Ifextra_contextispresent,itmustbefollowedimmediatelyin+*sigcontext.__reserved[]bytheterminatingnull_aarch64_ctx.+*+*4)Theextraspacetowhichdatapointsmuststartatthefirst+*16-bytealignedaddressimmediatelyaftertheterminatingnull+*_aarch64_ctxthatfollowstheextra_contextstructurein+*__reserved[].Theextraspacemayoverruntheendof__reserved[],+*asindicatedbyasufficientlylargevalueforthesizefield.+*+*5)Theextraspacemustitselfbeterminatedwithanull+*_aarch64_ctx.+*/+#define EXTRA_MAGIC 0x45585401++structextra_context{+struct_aarch64_ctxhead;+void__user*data;/* 16-byte aligned pointer to extra space */+__u32size;/* size in bytes of the extra space */+__u32__reserved[3];+};
Some more thoughts on the type of "data" above. It's the first time we
introduce pointers in sigcontext and wonder whether __u64 would make
more sense (invariable size with the ABI).
Also for discussion - whether using an offset vs absolute address would
be better.
--
Catalin
@@ -80,4 +81,39 @@ struct esr_context {__u64esr;};+/*+*extra_context:describesextraspaceinthesignalframefor+*additionalstructuresthatdon'tfitinsigcontext.__reserved[].+*+*Note:+*+*1)fpsimd_context,esr_contextandextra_contextmustbeplacedin+*sigcontext.__reserved[]ifpresent.Theycannotbeplacedinthe+*extraspace.Anyotherrecordcanbeplacedeitherintheextra+*spaceorinsigcontext.__reserved[],unlessotherwisespecifiedin+*thisfile.+*+*2)Theremustnotbemorethanoneextra_context.+*+*3)Ifextra_contextispresent,itmustbefollowedimmediatelyin+*sigcontext.__reserved[]bytheterminatingnull_aarch64_ctx.+*+*4)Theextraspacetowhichdatapointsmuststartatthefirst+*16-bytealignedaddressimmediatelyaftertheterminatingnull+*_aarch64_ctxthatfollowstheextra_contextstructurein+*__reserved[].Theextraspacemayoverruntheendof__reserved[],+*asindicatedbyasufficientlylargevalueforthesizefield.+*+*5)Theextraspacemustitselfbeterminatedwithanull+*_aarch64_ctx.+*/+#define EXTRA_MAGIC 0x45585401++structextra_context{+struct_aarch64_ctxhead;+void__user*data;/* 16-byte aligned pointer to extra space */+__u32size;/* size in bytes of the extra space */+__u32__reserved[3];+};
Some more thoughts on the type of "data" above. It's the first time we
introduce pointers in sigcontext and wonder whether __u64 would make
more sense (invariable size with the ABI).
I'm happy to change this if you want -- it can be a __u64 without
impacting the semantics.
Also for discussion - whether using an offset vs absolute address would
be better.
My rationale here related to the ucontext semantics associated with
the signal frame, rather than signal handling itself.
Consider:
void signal_handler(int n, siginfo_t *si, void *uc)
{
*signal_context = *(ucontext_t *)uc;
}
Will this "work"? On most machines, yes it will.
With an expanded signal frame on arm64, it won't work: the copied
context will be truncated at the end of __reserved[]. If extra_data
uses an offset to locate the additional data then someone parsing
*signal_context won't be able to detect the error and will overrun:
this includes rt_sigreturn (if that is run on a stack that has the
copied context).
If a pointer is used in extra_context, this provides some chance of
handling this: naive code like the above will result in a ucontext_t
whose extra_context data pointer is "wrong": extra_context aware
parsers can detect that and error out or ignore the remaining data.
extra_context aware code that wants to copy the signal context can walk
the signal frame to determine its true size, allocate memory, copy, and
then update the extra_context data pointer in the new copy.
This isn't foolproof, but it at least gives us some protection.
Cheers
---Dave
On Mon, Jun 19, 2017 at 11:12:29AM +0100, Dave P Martin wrote:
On Fri, Jun 16, 2017 at 11:47:41AM +0100, Catalin Marinas wrote:
quoted
On Thu, Jun 15, 2017 at 03:03:42PM +0100, Dave P Martin wrote:
quoted
+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 */
+ __u32 __reserved[3];
+};
Some more thoughts on the type of "data" above. It's the first time we
introduce pointers in sigcontext and wonder whether __u64 would make
more sense (invariable size with the ABI).
I'm happy to change this if you want -- it can be a __u64 without
impacting the semantics.
I was thinking in the context of ILP32 but we can address this
separately for the exported kernel headers and glibc definitions.
Looking at this structure from an LP64 perspective, it indeed makes
sense to keep it as void *. We have other UAPI structures with user
pointers already.
quoted
Also for discussion - whether using an offset vs absolute address would
be better.
My rationale here related to the ucontext semantics associated with
the signal frame, rather than signal handling itself.
Consider:
void signal_handler(int n, siginfo_t *si, void *uc)
{
*signal_context = *(ucontext_t *)uc;
}
Will this "work"? On most machines, yes it will.
With an expanded signal frame on arm64, it won't work: the copied
context will be truncated at the end of __reserved[]. If extra_data
uses an offset to locate the additional data then someone parsing
*signal_context won't be able to detect the error and will overrun:
this includes rt_sigreturn (if that is run on a stack that has the
copied context).
If a pointer is used in extra_context, this provides some chance of
handling this: naive code like the above will result in a ucontext_t
whose extra_context data pointer is "wrong": extra_context aware
parsers can detect that and error out or ignore the remaining data.
extra_context aware code that wants to copy the signal context can walk
the signal frame to determine its true size, allocate memory, copy, and
then update the extra_context data pointer in the new copy.
Thanks for the explanation, it makes sense to keep an absolute pointer
here rather than offset. So no further changes needed to this patch.
--
Catalin
On Mon, Jun 19, 2017 at 12:03:47PM +0100, Catalin Marinas wrote:
On Mon, Jun 19, 2017 at 11:12:29AM +0100, Dave P Martin wrote:
quoted
On Fri, Jun 16, 2017 at 11:47:41AM +0100, Catalin Marinas wrote:
quoted
On Thu, Jun 15, 2017 at 03:03:42PM +0100, Dave P Martin wrote:
quoted
+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 */
+ __u32 __reserved[3];
+};
Some more thoughts on the type of "data" above. It's the first time we
introduce pointers in sigcontext and wonder whether __u64 would make
more sense (invariable size with the ABI).
I'm happy to change this if you want -- it can be a __u64 without
impacting the semantics.
I was thinking in the context of ILP32 but we can address this
separately for the exported kernel headers and glibc definitions.
Looking at this structure from an LP64 perspective, it indeed makes
sense to keep it as void *. We have other UAPI structures with user
pointers already.
OK -- I'll reply with an additional fixup that can be folded in if you
do want the u64.
quoted
quoted
Also for discussion - whether using an offset vs absolute address would
be better.
My rationale here related to the ucontext semantics associated with
the signal frame, rather than signal handling itself.
Consider:
void signal_handler(int n, siginfo_t *si, void *uc)
{
*signal_context = *(ucontext_t *)uc;
}
Will this "work"? On most machines, yes it will.
With an expanded signal frame on arm64, it won't work: the copied
context will be truncated at the end of __reserved[]. If extra_data
uses an offset to locate the additional data then someone parsing
*signal_context won't be able to detect the error and will overrun:
this includes rt_sigreturn (if that is run on a stack that has the
copied context).
If a pointer is used in extra_context, this provides some chance of
handling this: naive code like the above will result in a ucontext_t
whose extra_context data pointer is "wrong": extra_context aware
parsers can detect that and error out or ignore the remaining data.
extra_context aware code that wants to copy the signal context can walk
the signal frame to determine its true size, allocate memory, copy, and
then update the extra_context data pointer in the new copy.
Thanks for the explanation, it makes sense to keep an absolute pointer
here rather than offset. So no further changes needed to this patch.
Convert extra_context.data to a u64 for better ABI independence.
data is renamed to "datap" to give a stronger clue that it is
a pointer.
The binary interface is unchanged.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
---
I've build-tested this fixup only.
The only change here is to replace the name "data" with "datap" in
appropriate places and change void __user * to u64 appropriately, with
casts in the two places where this value enters and exits the kernel.
arch/arm64/include/uapi/asm/sigcontext.h | 6 +++---
arch/arm64/kernel/signal.c | 16 ++++++++--------
2 files changed, 11 insertions(+), 11 deletions(-)
@@ -111,8 +111,8 @@ struct esr_context {structextra_context{struct_aarch64_ctxhead;-void__user*data;/* 16-byte aligned pointer to extra space */-__u32size;/* size in bytes of the extra space */+__u64datap;/* 16-byte aligned pointer to extra space cast to __u64 */+__u32size;/* size in bytes of the extra space */__u32__reserved[3];};
Hi Dave,
It seems that your series conflicts with one of patches of my ILP32:
https://patchwork.kernel.org/patch/9599015/
Now I try to rebase ilp32 on your patchset, and maybe will have some
questions. The first question is below. If you have comments on my
series - please share it.
Yury
On Thu, Jun 15, 2017 at 03:03:39PM +0100, Dave Martin wrote:
Currently, rt_sigreturn does very limited checking on the
sigcontext coming from userspace.
Future additions to the sigcontext data will increase the potential
for surprises. Also, it is not clear whether the sigcontext
extension records are supposed to occur in a particular order.
To allow the parsing code to be extended more easily, this patch
factors out the sigcontext parsing into a separate function, and
adds extra checks to validate the well-formedness of the sigcontext
structure.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
+static int parse_user_sigframe(struct user_ctxs *user,
+ struct rt_sigframe __user *sf)
You parse sigcontext here in fact. At least you take the pointer to
the sigcontext from rt_sigframe, and never refer it anymore. rt_sigframe
is different for lp64 and ilp32, but sigcontext is the same. So if you'll
rename the function to parse_user_sigcontext, and will pass sigcontext
to it directly, I will reuse it in ilp32 code.
--
2.1.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
On Thu, Jun 22, 2017 at 10:32:35PM +0300, Yury Norov wrote:
Hi Dave,
It seems that your series conflicts with one of patches of my ILP32:
https://patchwork.kernel.org/patch/9599015/
Now I try to rebase ilp32 on your patchset, and maybe will have some
questions. The first question is below. If you have comments on my
series - please share it.
Yury
On Thu, Jun 15, 2017 at 03:03:39PM +0100, Dave Martin wrote:
quoted
Currently, rt_sigreturn does very limited checking on the
sigcontext coming from userspace.
Future additions to the sigcontext data will increase the potential
for surprises. Also, it is not clear whether the sigcontext
extension records are supposed to occur in a particular order.
To allow the parsing code to be extended more easily, this patch
factors out the sigcontext parsing into a separate function, and
adds extra checks to validate the well-formedness of the sigcontext
structure.
Signed-off-by: Dave Martin <Dave.Martin@arm.com>
quoted
+static int parse_user_sigframe(struct user_ctxs *user,
+ struct rt_sigframe __user *sf)
You parse sigcontext here in fact. At least you take the pointer to
That's a fair point...
the sigcontext from rt_sigframe, and never refer it anymore. rt_sigframe
...however, the final patch [1] (which has not appeared in next yet,
I need to chase it) does refer to the sigframe base in order to
determine when the signal frame exceeds the maximum allowed size.
is different for lp64 and ilp32, but sigcontext is the same. So if you'll
rename the function to parse_user_sigcontext, and will pass sigcontext
to it directly, I will reuse it in ilp32 code.
Because the rt_sigframe pointer is _only_ used as a base address, the
rt_sigframe definition is not relevant. So a refactoring along lines of
[2] might work.
Other things that may need attention are the handling of the extra_data
pointer [2] (I changed this from void __user * to u64 after Catalin
pointed out the ABI dependency here), and the way the {fp,lr} frame
record is handled.
I think the RT_SIGFRAME_FP_POS() logic can probably be simplified/
dropped: this is now handled through
rt_sigframe_user_layout.next_frame. ILP32 would need its own version
of sigframe_size(), or that function should test for current being
ILP32, but the ILP32 equivalent of get_sigframe() would probably look
very similar to the native version after my patches.
(Note that I've not understood the proposed ILP32 signal changes in
detail at this point.)
Cheers
---Dave
[1] [PATCH] arm64: signal: Allow expansion of the signal frame
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-June/514699.html
[2] [RFC PATCH] arm64: signal: Make parse_user_sigframe() independent of rt_sigframe layout
http://lists.infradead.org/pipermail/linux-arm-kernel/2017-June/515361.html
--
2.1.4
_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel at lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel