Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+105 -20
+1
arch/arm/include/asm/ucontext.h
··· 2 2 #define _ASMARM_UCONTEXT_H 3 3 4 4 #include <asm/fpstate.h> 5 + #include <asm/user.h> 5 6 6 7 /* 7 8 * struct sigcontext only has room for the basic registers, but struct
+4
arch/arm/kernel/asm-offsets.c
··· 28 28 #include <asm/vdso_datapage.h> 29 29 #include <asm/hardware/cache-l2x0.h> 30 30 #include <linux/kbuild.h> 31 + #include "signal.h" 31 32 32 33 /* 33 34 * Make sure that the compiler and target are compatible. ··· 112 111 DEFINE(SVC_DACR, offsetof(struct svc_pt_regs, dacr)); 113 112 DEFINE(SVC_ADDR_LIMIT, offsetof(struct svc_pt_regs, addr_limit)); 114 113 DEFINE(SVC_REGS_SIZE, sizeof(struct svc_pt_regs)); 114 + BLANK(); 115 + DEFINE(SIGFRAME_RC3_OFFSET, offsetof(struct sigframe, retcode[3])); 116 + DEFINE(RT_SIGFRAME_RC3_OFFSET, offsetof(struct rt_sigframe, sig.retcode[3])); 115 117 BLANK(); 116 118 #ifdef CONFIG_CACHE_L2X0 117 119 DEFINE(L2X0_R_PHY_BASE, offsetof(struct l2x0_regs, phy_base));
+38 -15
arch/arm/kernel/signal.c
··· 18 18 #include <asm/elf.h> 19 19 #include <asm/cacheflush.h> 20 20 #include <asm/traps.h> 21 - #include <asm/ucontext.h> 22 21 #include <asm/unistd.h> 23 22 #include <asm/vfp.h> 24 23 25 - extern const unsigned long sigreturn_codes[7]; 24 + #include "signal.h" 25 + 26 + extern const unsigned long sigreturn_codes[17]; 26 27 27 28 static unsigned long signal_return_offset; 28 29 ··· 172 171 /* 173 172 * Do a signal return; undo the signal stack. These are aligned to 64-bit. 174 173 */ 175 - struct sigframe { 176 - struct ucontext uc; 177 - unsigned long retcode[2]; 178 - }; 179 - 180 - struct rt_sigframe { 181 - struct siginfo info; 182 - struct sigframe sig; 183 - }; 184 174 185 175 static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf) 186 176 { ··· 357 365 unsigned long __user *rc, void __user *frame) 358 366 { 359 367 unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler; 368 + unsigned long handler_fdpic_GOT = 0; 360 369 unsigned long retcode; 361 - int thumb = 0; 370 + unsigned int idx, thumb = 0; 362 371 unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT); 372 + bool fdpic = IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) && 373 + (current->personality & FDPIC_FUNCPTRS); 374 + 375 + if (fdpic) { 376 + unsigned long __user *fdpic_func_desc = 377 + (unsigned long __user *)handler; 378 + if (__get_user(handler, &fdpic_func_desc[0]) || 379 + __get_user(handler_fdpic_GOT, &fdpic_func_desc[1])) 380 + return 1; 381 + } 363 382 364 383 cpsr |= PSR_ENDSTATE; 365 384 ··· 410 407 411 408 if (ksig->ka.sa.sa_flags & SA_RESTORER) { 412 409 retcode = (unsigned long)ksig->ka.sa.sa_restorer; 410 + if (fdpic) { 411 + /* 412 + * We need code to load the function descriptor. 413 + * That code follows the standard sigreturn code 414 + * (6 words), and is made of 3 + 2 words for each 415 + * variant. The 4th copied word is the actual FD 416 + * address that the assembly code expects. 417 + */ 418 + idx = 6 + thumb * 3; 419 + if (ksig->ka.sa.sa_flags & SA_SIGINFO) 420 + idx += 5; 421 + if (__put_user(sigreturn_codes[idx], rc ) || 422 + __put_user(sigreturn_codes[idx+1], rc+1) || 423 + __put_user(sigreturn_codes[idx+2], rc+2) || 424 + __put_user(retcode, rc+3)) 425 + return 1; 426 + goto rc_finish; 427 + } 413 428 } else { 414 - unsigned int idx = thumb << 1; 415 - 429 + idx = thumb << 1; 416 430 if (ksig->ka.sa.sa_flags & SA_SIGINFO) 417 431 idx += 3; 418 432 ··· 441 421 __put_user(sigreturn_codes[idx+1], rc+1)) 442 422 return 1; 443 423 424 + rc_finish: 444 425 #ifdef CONFIG_MMU 445 426 if (cpsr & MODE32_BIT) { 446 427 struct mm_struct *mm = current->mm; ··· 461 440 * the return code written onto the stack. 462 441 */ 463 442 flush_icache_range((unsigned long)rc, 464 - (unsigned long)(rc + 2)); 443 + (unsigned long)(rc + 3)); 465 444 466 445 retcode = ((unsigned long)rc) + thumb; 467 446 } ··· 471 450 regs->ARM_sp = (unsigned long)frame; 472 451 regs->ARM_lr = retcode; 473 452 regs->ARM_pc = handler; 453 + if (fdpic) 454 + regs->ARM_r9 = handler_fdpic_GOT; 474 455 regs->ARM_cpsr = cpsr; 475 456 476 457 return 0;
+11
arch/arm/kernel/signal.h
··· 1 + #include <asm/ucontext.h> 2 + 3 + struct sigframe { 4 + struct ucontext uc; 5 + unsigned long retcode[4]; 6 + }; 7 + 8 + struct rt_sigframe { 9 + struct siginfo info; 10 + struct sigframe sig; 11 + };
+51 -5
arch/arm/kernel/sigreturn_codes.S
··· 14 14 * GNU General Public License for more details. 15 15 */ 16 16 17 + #include <asm/assembler.h> 18 + #include <asm/asm-offsets.h> 17 19 #include <asm/unistd.h> 18 20 19 21 /* ··· 52 50 .org sigreturn_codes + 12 * (\n) + 8 53 51 .thumb 54 52 .endm 53 + 54 + .macro arm_fdpic_slot n 55 + .org sigreturn_codes + 24 + 20 * (\n) 56 + ARM_OK( .arm ) 57 + .endm 58 + 59 + .macro thumb_fdpic_slot n 60 + .org sigreturn_codes + 24 + 20 * (\n) + 12 61 + .thumb 62 + .endm 63 + 55 64 56 65 #if __LINUX_ARM_ARCH__ <= 4 57 66 /* ··· 103 90 movs r7, #(__NR_rt_sigreturn - __NR_SYSCALL_BASE) 104 91 swi #0 105 92 93 + /* ARM sigreturn restorer FDPIC bounce code snippet */ 94 + arm_fdpic_slot 0 95 + ARM_OK( ldr r3, [sp, #SIGFRAME_RC3_OFFSET] ) 96 + ARM_OK( ldmia r3, {r3, r9} ) 97 + #ifdef CONFIG_ARM_THUMB 98 + ARM_OK( bx r3 ) 99 + #else 100 + ARM_OK( ret r3 ) 101 + #endif 102 + 103 + /* Thumb sigreturn restorer FDPIC bounce code snippet */ 104 + thumb_fdpic_slot 0 105 + ldr r3, [sp, #SIGFRAME_RC3_OFFSET] 106 + ldmia r3, {r2, r3} 107 + mov r9, r3 108 + bx r2 109 + 110 + /* ARM sigreturn_rt restorer FDPIC bounce code snippet */ 111 + arm_fdpic_slot 1 112 + ARM_OK( ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET] ) 113 + ARM_OK( ldmia r3, {r3, r9} ) 114 + #ifdef CONFIG_ARM_THUMB 115 + ARM_OK( bx r3 ) 116 + #else 117 + ARM_OK( ret r3 ) 118 + #endif 119 + 120 + /* Thumb sigreturn_rt restorer FDPIC bounce code snippet */ 121 + thumb_fdpic_slot 1 122 + ldr r3, [sp, #RT_SIGFRAME_RC3_OFFSET] 123 + ldmia r3, {r2, r3} 124 + mov r9, r3 125 + bx r2 126 + 106 127 /* 107 - * Note on addtional space: setup_return in signal.c 108 - * algorithm uses two words copy regardless whether 109 - * it is thumb case or not, so we need additional 110 - * word after real last entry. 128 + * Note on additional space: setup_return in signal.c 129 + * always copies the same number of words regardless whether 130 + * it is thumb case or not, so we need one additional padding 131 + * word after the last entry. 111 132 */ 112 - arm_slot 2 113 133 .space 4 114 134 115 135 .size sigreturn_codes, . - sigreturn_codes