aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNicolas Pitre <nicolas.pitre@linaro.org>2017-08-09 23:42:51 -0400
committerNicolas Pitre <nicolas.pitre@linaro.org>2017-09-10 19:31:46 -0400
commit5c16595353e0743af99294db48549c3145e3a5ad (patch)
treef3235a25b45bf879e523ac56037a06e8325f961f
parent5e5881143297c97207486349ec1cf9fd833b1510 (diff)
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>
-rw-r--r--arch/arm/include/asm/ucontext.h1
-rw-r--r--arch/arm/kernel/asm-offsets.c4
-rw-r--r--arch/arm/kernel/signal.c53
-rw-r--r--arch/arm/kernel/signal.h11
-rw-r--r--arch/arm/kernel/sigreturn_codes.S56
5 files changed, 105 insertions, 20 deletions
diff --git a/arch/arm/include/asm/ucontext.h b/arch/arm/include/asm/ucontext.h
index 921d8274855c..b42c75ae0d19 100644
--- a/arch/arm/include/asm/ucontext.h
+++ b/arch/arm/include/asm/ucontext.h
@@ -2,6 +2,7 @@
2#define _ASMARM_UCONTEXT_H 2#define _ASMARM_UCONTEXT_H
3 3
4#include <asm/fpstate.h> 4#include <asm/fpstate.h>
5#include <asm/user.h>
5 6
6/* 7/*
7 * struct sigcontext only has room for the basic registers, but struct 8 * 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 608008229c7d..13c155850822 100644
--- a/arch/arm/kernel/asm-offsets.c
+++ b/arch/arm/kernel/asm-offsets.c
@@ -28,6 +28,7 @@
28#include <asm/vdso_datapage.h> 28#include <asm/vdso_datapage.h>
29#include <asm/hardware/cache-l2x0.h> 29#include <asm/hardware/cache-l2x0.h>
30#include <linux/kbuild.h> 30#include <linux/kbuild.h>
31#include "signal.h"
31 32
32/* 33/*
33 * Make sure that the compiler and target are compatible. 34 * Make sure that the compiler and target are compatible.
@@ -112,6 +113,9 @@ int main(void)
112 DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit)); 113 DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit));
113 DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs)); 114 DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs));
114 BLANK(); 115 BLANK();
116 DEFINE(SIGFRAME_RC3_OFFSET, offsetof(struct sigframe, retcode[3]));
117 DEFINE(RT_SIGFRAME_RC3_OFFSET, offsetof(struct rt_sigframe, sig.retcode[3]));
118 BLANK();
115#ifdef CONFIG_CACHE_L2X0 119#ifdef CONFIG_CACHE_L2X0
116 DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base)); 120 DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base));
117 DEFINE(L2X0_R_AUX_CTRL, offsetof(struct l2x0_regs, aux_ctrl)); 121 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 5814298ef0b7..1f3574ec4fad 100644
--- a/arch/arm/kernel/signal.c
+++ b/arch/arm/kernel/signal.c
@@ -18,11 +18,12 @@
18#include <asm/elf.h> 18#include <asm/elf.h>
19#include <asm/cacheflush.h> 19#include <asm/cacheflush.h>
20#include <asm/traps.h> 20#include <asm/traps.h>
21#include <asm/ucontext.h>
22#include <asm/unistd.h> 21#include <asm/unistd.h>
23#include <asm/vfp.h> 22#include <asm/vfp.h>
24 23
25extern const unsigned long sigreturn_codes[7]; 24#include "signal.h"
25
26extern const unsigned long sigreturn_codes[17];
26 27
27static unsigned long signal_return_offset; 28static unsigned long signal_return_offset;
28 29
@@ -171,15 +172,6 @@ static int restore_vfp_context(char __user **auxp)
171/* 172/*
172 * Do a signal return; undo the signal stack. These are aligned to 64-bit. 173 * Do a signal return; undo the signal stack. These are aligned to 64-bit.
173 */ 174 */
174struct sigframe {
175 struct ucontext uc;
176 unsigned long retcode[2];
177};
178
179struct rt_sigframe {
180 struct siginfo info;
181 struct sigframe sig;
182};
183 175
184static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf) 176static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
185{ 177{
@@ -365,9 +357,20 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
365 unsigned long __user *rc, void __user *frame) 357 unsigned long __user *rc, void __user *frame)
366{ 358{
367 unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler; 359 unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler;
360 unsigned long handler_fdpic_GOT = 0;
368 unsigned long retcode; 361 unsigned long retcode;
369 int thumb = 0; 362 unsigned int idx, thumb = 0;
370 unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT); 363 unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT);
364 bool fdpic = IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) &&
365 (current->personality & FDPIC_FUNCPTRS);
366
367 if (fdpic) {
368 unsigned long __user *fdpic_func_desc =
369 (unsigned long __user *)handler;
370 if (__get_user(handler, &fdpic_func_desc[0]) ||
371 __get_user(handler_fdpic_GOT, &fdpic_func_desc[1]))
372 return 1;
373 }
371 374
372 cpsr |= PSR_ENDSTATE; 375 cpsr |= PSR_ENDSTATE;
373 376
@@ -407,9 +410,26 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
407 410
408 if (ksig->ka.sa.sa_flags & SA_RESTORER) { 411 if (ksig->ka.sa.sa_flags & SA_RESTORER) {
409 retcode = (unsigned long)ksig->ka.sa.sa_restorer; 412 retcode = (unsigned long)ksig->ka.sa.sa_restorer;
413 if (fdpic) {
414 /*
415 * We need code to load the function descriptor.
416 * That code follows the standard sigreturn code
417 * (6 words), and is made of 3 + 2 words for each
418 * variant. The 4th copied word is the actual FD
419 * address that the assembly code expects.
420 */
421 idx = 6 + thumb * 3;
422 if (ksig->ka.sa.sa_flags & SA_SIGINFO)
423 idx += 5;
424 if (__put_user(sigreturn_codes[idx], rc ) ||
425 __put_user(sigreturn_codes[idx+1], rc+1) ||
426 __put_user(sigreturn_codes[idx+2], rc+2) ||
427 __put_user(retcode, rc+3))
428 return 1;
429 goto rc_finish;
430 }
410 } else { 431 } else {
411 unsigned int idx = thumb << 1; 432 idx = thumb << 1;
412
413 if (ksig->ka.sa.sa_flags & SA_SIGINFO) 433 if (ksig->ka.sa.sa_flags & SA_SIGINFO)
414 idx += 3; 434 idx += 3;
415 435
@@ -421,6 +441,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
421 __put_user(sigreturn_codes[idx+1], rc+1)) 441 __put_user(sigreturn_codes[idx+1], rc+1))
422 return 1; 442 return 1;
423 443
444rc_finish:
424#ifdef CONFIG_MMU 445#ifdef CONFIG_MMU
425 if (cpsr & MODE32_BIT) { 446 if (cpsr & MODE32_BIT) {
426 struct mm_struct *mm = current->mm; 447 struct mm_struct *mm = current->mm;
@@ -440,7 +461,7 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
440 * the return code written onto the stack. 461 * the return code written onto the stack.
441 */ 462 */
442 flush_icache_range((unsigned long)rc, 463 flush_icache_range((unsigned long)rc,
443 (unsigned long)(rc + 2)); 464 (unsigned long)(rc + 3));
444 465
445 retcode = ((unsigned long)rc) + thumb; 466 retcode = ((unsigned long)rc) + thumb;
446 } 467 }
@@ -450,6 +471,8 @@ setup_return(struct pt_regs *regs, struct ksignal *ksig,
450 regs->ARM_sp = (unsigned long)frame; 471 regs->ARM_sp = (unsigned long)frame;
451 regs->ARM_lr = retcode; 472 regs->ARM_lr = retcode;
452 regs->ARM_pc = handler; 473 regs->ARM_pc = handler;
474 if (fdpic)
475 regs->ARM_r9 = handler_fdpic_GOT;
453 regs->ARM_cpsr = cpsr; 476 regs->ARM_cpsr = cpsr;
454 477
455 return 0; 478 return 0;
diff --git a/arch/arm/kernel/signal.h b/arch/arm/kernel/signal.h
new file mode 100644
index 000000000000..b7b838b05229
--- /dev/null
+++ b/arch/arm/kernel/signal.h
@@ -0,0 +1,11 @@
1#include <asm/ucontext.h>
2
3struct sigframe {
4 struct ucontext uc;
5 unsigned long retcode[4];
6};
7
8struct rt_sigframe {
9 struct siginfo info;
10 struct sigframe sig;
11};
diff --git a/arch/arm/kernel/sigreturn_codes.S b/arch/arm/kernel/sigreturn_codes.S
index b84d0cb13682..2c7b22e32152 100644
--- a/arch/arm/kernel/sigreturn_codes.S
+++ b/arch/arm/kernel/sigreturn_codes.S
@@ -14,6 +14,8 @@
14 * GNU General Public License for more details. 14 * GNU General Public License for more details.
15 */ 15 */
16 16
17#include <asm/assembler.h>
18#include <asm/asm-offsets.h>
17#include <asm/unistd.h> 19#include <asm/unistd.h>
18 20
19/* 21/*
@@ -51,6 +53,17 @@ ARM_OK( .arm )
51 .thumb 53 .thumb
52 .endm 54 .endm
53 55
56 .macro arm_fdpic_slot n
57 .org sigreturn_codes + 24 + 20 * (\n)
58ARM_OK( .arm )
59 .endm
60
61 .macro thumb_fdpic_slot n
62 .org sigreturn_codes + 24 + 20 * (\n) + 12
63 .thumb
64 .endm
65
66
54#if __LINUX_ARM_ARCH__ <= 4 67#if __LINUX_ARM_ARCH__ <= 4
55 /* 68 /*
56 * Note we manually set minimally required arch that supports 69 * Note we manually set minimally required arch that supports
@@ -90,13 +103,46 @@ ARM_OK( swi #(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE) )
90 movs r7, #(__NR_rt_sigreturn - __NR_SYSCALL_BASE) 103 movs r7, #(__NR_rt_sigreturn - __NR_SYSCALL_BASE)
91 swi #0 104 swi #0
92 105
106 /* ARM sigreturn restorer FDPIC bounce code snippet */
107 arm_fdpic_slot 0
108ARM_OK( ldr r3, [sp, #SIGFRAME_RC3_OFFSET] )
109ARM_OK( ldmia r3, {r3, r9} )
110#ifdef CONFIG_ARM_THUMB
111ARM_OK( bx r3 )
112#else
113ARM_OK( ret r3 )
114#endif
115
116 /* Thumb sigreturn restorer FDPIC bounce code snippet */
117 thumb_fdpic_slot 0
118 ldr r3, [sp, #SIGFRAME_RC3_OFFSET]
119 ldmia r3, {r2, r3}
120 mov r9, r3
121 bx r2
122
123 /* ARM sigreturn_rt restorer FDPIC bounce code snippet */
124 arm_fdpic_slot 1
125ARM_OK( ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET] )
126ARM_OK( ldmia r3, {r3, r9} )
127#ifdef CONFIG_ARM_THUMB
128ARM_OK( bx r3 )
129#else
130ARM_OK( ret r3 )
131#endif
132
133 /* Thumb sigreturn_rt restorer FDPIC bounce code snippet */
134 thumb_fdpic_slot 1
135 ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET]
136 ldmia r3, {r2, r3}
137 mov r9, r3
138 bx r2
139
93 /* 140 /*
94 * Note on addtional space: setup_return in signal.c 141 * Note on additional space: setup_return in signal.c
95 * algorithm uses two words copy regardless whether 142 * always copies the same number of words regardless whether
96 * it is thumb case or not, so we need additional 143 * it is thumb case or not, so we need one additional padding
97 * word after real last entry. 144 * word after the last entry.
98 */ 145 */
99 arm_slot 2
100 .space 4 146 .space 4
101 147
102 .size sigreturn_codes, . - sigreturn_codes 148 .size sigreturn_codes, . - sigreturn_codes