ARM: signal handling support for FDPIC_FUNCPTRS functions
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@linaro.org> Acked-by: Mickael GUENE <mickael.guene@st.com> Tested-by: Vincent Abriou <vincent.abriou@st.com> Tested-by: Andras Szemzo <szemzo.andras@gmail.com>
This commit is contained in:
parent
5e58811432
commit
5c16595353
5 changed files with 105 additions and 20 deletions
|
@ -2,6 +2,7 @@
|
||||||
#define _ASMARM_UCONTEXT_H
|
#define _ASMARM_UCONTEXT_H
|
||||||
|
|
||||||
#include <asm/fpstate.h>
|
#include <asm/fpstate.h>
|
||||||
|
#include <asm/user.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* struct sigcontext only has room for the basic registers, but struct
|
* struct sigcontext only has room for the basic registers, but struct
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include <asm/vdso_datapage.h>
|
#include <asm/vdso_datapage.h>
|
||||||
#include <asm/hardware/cache-l2x0.h>
|
#include <asm/hardware/cache-l2x0.h>
|
||||||
#include <linux/kbuild.h>
|
#include <linux/kbuild.h>
|
||||||
|
#include "signal.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make sure that the compiler and target are compatible.
|
* 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_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit));
|
||||||
DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs));
|
DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs));
|
||||||
BLANK();
|
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
|
#ifdef CONFIG_CACHE_L2X0
|
||||||
DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base));
|
DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base));
|
||||||
DEFINE(L2X0_R_AUX_CTRL, offsetof(struct l2x0_regs, aux_ctrl));
|
DEFINE(L2X0_R_AUX_CTRL, offsetof(struct l2x0_regs, aux_ctrl));
|
||||||
|
|
|
@ -18,11 +18,12 @@
|
||||||
#include <asm/elf.h>
|
#include <asm/elf.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <asm/traps.h>
|
#include <asm/traps.h>
|
||||||
#include <asm/ucontext.h>
|
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
#include <asm/vfp.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;
|
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.
|
* 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)
|
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 __user *rc, void __user *frame)
|
||||||
{
|
{
|
||||||
unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler;
|
unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler;
|
||||||
|
unsigned long handler_fdpic_GOT = 0;
|
||||||
unsigned long retcode;
|
unsigned long retcode;
|
||||||
int thumb = 0;
|
unsigned int idx, thumb = 0;
|
||||||
unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT);
|
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;
|
cpsr |= PSR_ENDSTATE;
|
||||||
|
|
||||||
|
@ -407,9 +410,26 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
|
||||||
|
|
||||||
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
|
if (ksig->ka.sa.sa_flags & SA_RESTORER) {
|
||||||
retcode = (unsigned long)ksig->ka.sa.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 {
|
} else {
|
||||||
unsigned int idx = thumb << 1;
|
idx = thumb << 1;
|
||||||
|
|
||||||
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
|
if (ksig->ka.sa.sa_flags & SA_SIGINFO)
|
||||||
idx += 3;
|
idx += 3;
|
||||||
|
|
||||||
|
@ -421,6 +441,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
|
||||||
__put_user(sigreturn_codes[idx+1], rc+1))
|
__put_user(sigreturn_codes[idx+1], rc+1))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
rc_finish:
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
if (cpsr & MODE32_BIT) {
|
if (cpsr & MODE32_BIT) {
|
||||||
struct mm_struct *mm = current->mm;
|
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.
|
* the return code written onto the stack.
|
||||||
*/
|
*/
|
||||||
flush_icache_range((unsigned long)rc,
|
flush_icache_range((unsigned long)rc,
|
||||||
(unsigned long)(rc + 2));
|
(unsigned long)(rc + 3));
|
||||||
|
|
||||||
retcode = ((unsigned long)rc) + thumb;
|
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_sp = (unsigned long)frame;
|
||||||
regs->ARM_lr = retcode;
|
regs->ARM_lr = retcode;
|
||||||
regs->ARM_pc = handler;
|
regs->ARM_pc = handler;
|
||||||
|
if (fdpic)
|
||||||
|
regs->ARM_r9 = handler_fdpic_GOT;
|
||||||
regs->ARM_cpsr = cpsr;
|
regs->ARM_cpsr = cpsr;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
11
arch/arm/kernel/signal.h
Normal file
11
arch/arm/kernel/signal.h
Normal file
|
@ -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;
|
||||||
|
};
|
|
@ -14,6 +14,8 @@
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <asm/assembler.h>
|
||||||
|
#include <asm/asm-offsets.h>
|
||||||
#include <asm/unistd.h>
|
#include <asm/unistd.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -51,6 +53,17 @@ ARM_OK( .arm )
|
||||||
.thumb
|
.thumb
|
||||||
.endm
|
.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
|
#if __LINUX_ARM_ARCH__ <= 4
|
||||||
/*
|
/*
|
||||||
* Note we manually set minimally required arch that supports
|
* 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)
|
movs r7, #(__NR_rt_sigreturn - __NR_SYSCALL_BASE)
|
||||||
swi #0
|
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
|
* Note on additional space: setup_return in signal.c
|
||||||
* algorithm uses two words copy regardless whether
|
* always copies the same number of words regardless whether
|
||||||
* it is thumb case or not, so we need additional
|
* it is thumb case or not, so we need one additional padding
|
||||||
* word after real last entry.
|
* word after the last entry.
|
||||||
*/
|
*/
|
||||||
arm_slot 2
|
|
||||||
.space 4
|
.space 4
|
||||||
|
|
||||||
.size sigreturn_codes, . - sigreturn_codes
|
.size sigreturn_codes, . - sigreturn_codes
|
||||||
|
|
Loading…
Reference in a new issue