[v2,4/8] ARM: signal handling support for FDPIC_FUNCPTRS functions
Vincent ABRIOU
vincent.abriou at st.com
Mon Aug 28 07:07:23 PDT 2017
Hi Nicolas,
On 08/17/2017 09:23 AM, Nicolas Pitre wrote:
> Signal handlers are not direct function pointers but pointers to function
> descriptor in that case. Therefore we must retrieve the actual function
> address and load the GOT value into r9 from the descriptor before branching
> to the actual handler.
>
> If a restorer is provided, we also have to load its address and GOT from
> its descriptor. That descriptor address and the code to load it is pushed
> onto the stack to be executed as soon as the signal handler returns.
>
> However, to be compatible with NX stacks, the FDPIC bounce code is also
> copied to the signal page along with the other code stubs. Therefore this
> code must get at the descriptor address whether it executes from the stack
> or the signal page. To do so we use the stack pointer which points at the
> signal stack frame where the descriptor address was stored. Because the
> rt signal frame is different from the simpler frame, two versions of the
> bounce code are needed, and two variants (ARM and Thumb) as well. The
> asm-offsets facility is used to determine the actual offset in the signal
> frame for each version, meaning that struct sigframe and rt_sigframe had
> to be moved to a separate file.
>
> Signed-off-by: Nicolas Pitre <nico at linaro.org>
Tested-by: Vincent Abriou <vincent.abriou at st.com>
BR
Vincent
> ---
> arch/arm/include/asm/ucontext.h | 1 +
> arch/arm/kernel/asm-offsets.c | 4 +++
> arch/arm/kernel/signal.c | 53 +++++++++++++++++++++++++-----------
> arch/arm/kernel/signal.h | 11 ++++++++
> arch/arm/kernel/sigreturn_codes.S | 56 +++++++++++++++++++++++++++++++++++----
> 5 files changed, 105 insertions(+), 20 deletions(-)
> create mode 100644 arch/arm/kernel/signal.h
>
> diff --git a/arch/arm/include/asm/ucontext.h b/arch/arm/include/asm/ucontext.h
> index 921d827485..b42c75ae0d 100644
> --- a/arch/arm/include/asm/ucontext.h
> +++ b/arch/arm/include/asm/ucontext.h
> @@ -2,6 +2,7 @@
> #define _ASMARM_UCONTEXT_H
>
> #include <asm/fpstate.h>
> +#include <asm/user.h>
>
> /*
> * struct sigcontext only has room for the basic registers, but struct
> diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c
> index 608008229c..13c1558508 100644
> --- a/arch/arm/kernel/asm-offsets.c
> +++ b/arch/arm/kernel/asm-offsets.c
> @@ -28,6 +28,7 @@
> #include <asm/vdso_datapage.h>
> #include <asm/hardware/cache-l2x0.h>
> #include <linux/kbuild.h>
> +#include "signal.h"
>
> /*
> * Make sure that the compiler and target are compatible.
> @@ -112,6 +113,9 @@ int main(void)
> DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit));
> DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs));
> BLANK();
> + DEFINE(SIGFRAME_RC3_OFFSET, offsetof(struct sigframe, retcode[3]));
> + DEFINE(RT_SIGFRAME_RC3_OFFSET, offsetof(struct rt_sigframe, sig.retcode[3]));
> + BLANK();
> #ifdef CONFIG_CACHE_L2X0
> DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base));
> DEFINE(L2X0_R_AUX_CTRL, offsetof(struct l2x0_regs, aux_ctrl));
> diff --git a/arch/arm/kernel/signal.c b/arch/arm/kernel/signal.c
> index 5814298ef0..1f3574ec4f 100644
> --- a/arch/arm/kernel/signal.c
> +++ b/arch/arm/kernel/signal.c
> @@ -18,11 +18,12 @@
> #include <asm/elf.h>
> #include <asm/cacheflush.h>
> #include <asm/traps.h>
> -#include <asm/ucontext.h>
> #include <asm/unistd.h>
> #include <asm/vfp.h>
>
> -extern const unsigned long sigreturn_codes[7];
> +#include "signal.h"
> +
> +extern const unsigned long sigreturn_codes[17];
>
> static unsigned long signal_return_offset;
>
> @@ -171,15 +172,6 @@ static int restore_vfp_context(char __user **auxp)
> /*
> * Do a signal return; undo the signal stack. These are aligned to 64-bit.
> */
> -struct sigframe {
> - struct ucontext uc;
> - unsigned long retcode[2];
> -};
> -
> -struct rt_sigframe {
> - struct siginfo info;
> - struct sigframe sig;
> -};
>
> static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
> {
> @@ -365,9 +357,20 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
> unsigned long __user *rc, void __user *frame)
> {
> unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler;
> + unsigned long handler_fdpic_GOT = 0;
> unsigned long retcode;
> - int thumb = 0;
> + unsigned int idx, thumb = 0;
> unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT);
> + bool fdpic = IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) &&
> + (current->personality & FDPIC_FUNCPTRS);
> +
> + if (fdpic) {
> + unsigned long __user *fdpic_func_desc =
> + (unsigned long __user *)handler;
> + if (__get_user(handler, &fdpic_func_desc[0]) ||
> + __get_user(handler_fdpic_GOT, &fdpic_func_desc[1]))
> + return 1;
> + }
>
> cpsr |= PSR_ENDSTATE;
>
> @@ -407,9 +410,26 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
>
> if (ksig->ka.sa.sa_flags & SA_RESTORER) {
> retcode = (unsigned long)ksig->ka.sa.sa_restorer;
> + if (fdpic) {
> + /*
> + * We need code to load the function descriptor.
> + * That code follows the standard sigreturn code
> + * (6 words), and is made of 3 + 2 words for each
> + * variant. The 4th copied word is the actual FD
> + * address that the assembly code expects.
> + */
> + idx = 6 + thumb * 3;
> + if (ksig->ka.sa.sa_flags & SA_SIGINFO)
> + idx += 5;
> + if (__put_user(sigreturn_codes[idx], rc ) ||
> + __put_user(sigreturn_codes[idx+1], rc+1) ||
> + __put_user(sigreturn_codes[idx+2], rc+2) ||
> + __put_user(retcode, rc+3))
> + return 1;
> + goto rc_finish;
> + }
> } else {
> - unsigned int idx = thumb << 1;
> -
> + idx = thumb << 1;
> if (ksig->ka.sa.sa_flags & SA_SIGINFO)
> idx += 3;
>
> @@ -421,6 +441,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
> __put_user(sigreturn_codes[idx+1], rc+1))
> return 1;
>
> +rc_finish:
> #ifdef CONFIG_MMU
> if (cpsr & MODE32_BIT) {
> struct mm_struct *mm = current->mm;
> @@ -440,7 +461,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
> * the return code written onto the stack.
> */
> flush_icache_range((unsigned long)rc,
> - (unsigned long)(rc + 2));
> + (unsigned long)(rc + 3));
>
> retcode = ((unsigned long)rc) + thumb;
> }
> @@ -450,6 +471,8 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
> regs->ARM_sp = (unsigned long)frame;
> regs->ARM_lr = retcode;
> regs->ARM_pc = handler;
> + if (fdpic)
> + regs->ARM_r9 = handler_fdpic_GOT;
> regs->ARM_cpsr = cpsr;
>
> return 0;
> diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h
> new file mode 100644
> index 0000000000..b7b838b052
> --- /dev/null
> +++ b/arch/arm/kernel/signal.h
> @@ -0,0 +1,11 @@
> +#include <asm/ucontext.h>
> +
> +struct sigframe {
> + struct ucontext uc;
> + unsigned long retcode[4];
> +};
> +
> +struct rt_sigframe {
> + struct siginfo info;
> + struct sigframe sig;
> +};
> diff --git a/arch/arm/kernel/sigreturn_codes.S b/arch/arm/kernel/sigreturn_codes.S
> index b84d0cb136..2c7b22e321 100644
> --- a/arch/arm/kernel/sigreturn_codes.S
> +++ b/arch/arm/kernel/sigreturn_codes.S
> @@ -14,6 +14,8 @@
> * GNU General Public License for more details.
> */
>
> +#include <asm/assembler.h>
> +#include <asm/asm-offsets.h>
> #include <asm/unistd.h>
>
> /*
> @@ -51,6 +53,17 @@ ARM_OK( .arm )
> .thumb
> .endm
>
> + .macro arm_fdpic_slot n
> + .org sigreturn_codes + 24 + 20 * (\n)
> +ARM_OK( .arm )
> + .endm
> +
> + .macro thumb_fdpic_slot n
> + .org sigreturn_codes + 24 + 20 * (\n) + 12
> + .thumb
> + .endm
> +
> +
> #if __LINUX_ARM_ARCH__ <= 4
> /*
> * Note we manually set minimally required arch that supports
> @@ -90,13 +103,46 @@ ARM_OK( swi #(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE) )
> movs r7, #(__NR_rt_sigreturn - __NR_SYSCALL_BASE)
> swi #0
>
> + /* ARM sigreturn restorer FDPIC bounce code snippet */
> + arm_fdpic_slot 0
> +ARM_OK( ldr r3, [sp, #SIGFRAME_RC3_OFFSET] )
> +ARM_OK( ldmia r3, {r3, r9} )
> +#ifdef CONFIG_ARM_THUMB
> +ARM_OK( bx r3 )
> +#else
> +ARM_OK( ret r3 )
> +#endif
> +
> + /* Thumb sigreturn restorer FDPIC bounce code snippet */
> + thumb_fdpic_slot 0
> + ldr r3, [sp, #SIGFRAME_RC3_OFFSET]
> + ldmia r3, {r2, r3}
> + mov r9, r3
> + bx r2
> +
> + /* ARM sigreturn_rt restorer FDPIC bounce code snippet */
> + arm_fdpic_slot 1
> +ARM_OK( ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET] )
> +ARM_OK( ldmia r3, {r3, r9} )
> +#ifdef CONFIG_ARM_THUMB
> +ARM_OK( bx r3 )
> +#else
> +ARM_OK( ret r3 )
> +#endif
> +
> + /* Thumb sigreturn_rt restorer FDPIC bounce code snippet */
> + thumb_fdpic_slot 1
> + ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET]
> + ldmia r3, {r2, r3}
> + mov r9, r3
> + bx r2
> +
> /*
> - * Note on addtional space: setup_return in signal.c
> - * algorithm uses two words copy regardless whether
> - * it is thumb case or not, so we need additional
> - * word after real last entry.
> + * Note on additional space: setup_return in signal.c
> + * always copies the same number of words regardless whether
> + * it is thumb case or not, so we need one additional padding
> + * word after the last entry.
> */
> - arm_slot 2
> .space 4
>
> .size sigreturn_codes, . - sigreturn_codes
>
More information about the linux-arm-kernel
mailing list