ARM: Fix signal restart issues with NX and OABI compat
The signal restarting code was placed on the user stack when OABI compatibility is enabled. Unfortunately, with an EABI NX executable, this results in an attempt to run code from the non-executable stack, which segfaults the application. Fix this by placing the code in the vectors page, along side the signal return code, and directing the application to that code. Reported-by: saeed bishara <saeed.bishara@gmail.com> Tested-by: saeed bishara <saeed.bishara@gmail.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
This commit is contained in:
parent
0996391139
commit
ab72b00734
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* linux/arch/arm/kernel/signal.c
|
* linux/arch/arm/kernel/signal.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 1995-2002 Russell King
|
* Copyright (C) 1995-2009 Russell King
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
@ -29,6 +29,7 @@
|
||||||
*/
|
*/
|
||||||
#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE))
|
#define SWI_SYS_SIGRETURN (0xef000000|(__NR_sigreturn)|(__NR_OABI_SYSCALL_BASE))
|
||||||
#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE))
|
#define SWI_SYS_RT_SIGRETURN (0xef000000|(__NR_rt_sigreturn)|(__NR_OABI_SYSCALL_BASE))
|
||||||
|
#define SWI_SYS_RESTART (0xef000000|__NR_restart_syscall|__NR_OABI_SYSCALL_BASE)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* With EABI, the syscall number has to be loaded into r7.
|
* With EABI, the syscall number has to be loaded into r7.
|
||||||
|
@ -48,6 +49,18 @@ const unsigned long sigreturn_codes[7] = {
|
||||||
MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
|
MOV_R7_NR_RT_SIGRETURN, SWI_SYS_RT_SIGRETURN, SWI_THUMB_RT_SIGRETURN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Either we support OABI only, or we have EABI with the OABI
|
||||||
|
* compat layer enabled. In the later case we don't know if
|
||||||
|
* user space is EABI or not, and if not we must not clobber r7.
|
||||||
|
* Always using the OABI syscall solves that issue and works for
|
||||||
|
* all those cases.
|
||||||
|
*/
|
||||||
|
const unsigned long syscall_restart_code[2] = {
|
||||||
|
SWI_SYS_RESTART, /* swi __NR_restart_syscall */
|
||||||
|
0xe49df004, /* ldr pc, [sp], #4 */
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* atomically swap in the new signal mask, and wait for a signal.
|
* atomically swap in the new signal mask, and wait for a signal.
|
||||||
*/
|
*/
|
||||||
|
@ -645,32 +658,12 @@ static void do_signal(struct pt_regs *regs, int syscall)
|
||||||
regs->ARM_pc -= 4;
|
regs->ARM_pc -= 4;
|
||||||
#else
|
#else
|
||||||
u32 __user *usp;
|
u32 __user *usp;
|
||||||
u32 swival = __NR_restart_syscall;
|
|
||||||
|
|
||||||
regs->ARM_sp -= 12;
|
regs->ARM_sp -= 4;
|
||||||
usp = (u32 __user *)regs->ARM_sp;
|
usp = (u32 __user *)regs->ARM_sp;
|
||||||
|
|
||||||
/*
|
put_user(regs->ARM_pc, usp);
|
||||||
* Either we supports OABI only, or we have
|
regs->ARM_pc = KERN_RESTART_CODE;
|
||||||
* EABI with the OABI compat layer enabled.
|
|
||||||
* In the later case we don't know if user
|
|
||||||
* space is EABI or not, and if not we must
|
|
||||||
* not clobber r7. Always using the OABI
|
|
||||||
* syscall solves that issue and works for
|
|
||||||
* all those cases.
|
|
||||||
*/
|
|
||||||
swival = swival - __NR_SYSCALL_BASE + __NR_OABI_SYSCALL_BASE;
|
|
||||||
|
|
||||||
put_user(regs->ARM_pc, &usp[0]);
|
|
||||||
/* swi __NR_restart_syscall */
|
|
||||||
put_user(0xef000000 | swival, &usp[1]);
|
|
||||||
/* ldr pc, [sp], #12 */
|
|
||||||
put_user(0xe49df00c, &usp[2]);
|
|
||||||
|
|
||||||
flush_icache_range((unsigned long)usp,
|
|
||||||
(unsigned long)(usp + 3));
|
|
||||||
|
|
||||||
regs->ARM_pc = regs->ARM_sp + 4;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
/*
|
/*
|
||||||
* linux/arch/arm/kernel/signal.h
|
* linux/arch/arm/kernel/signal.h
|
||||||
*
|
*
|
||||||
* Copyright (C) 2005 Russell King.
|
* Copyright (C) 2005-2009 Russell King.
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License version 2 as
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
* published by the Free Software Foundation.
|
* published by the Free Software Foundation.
|
||||||
*/
|
*/
|
||||||
#define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500)
|
#define KERN_SIGRETURN_CODE (CONFIG_VECTORS_BASE + 0x00000500)
|
||||||
|
#define KERN_RESTART_CODE (KERN_SIGRETURN_CODE + sizeof(sigreturn_codes))
|
||||||
|
|
||||||
extern const unsigned long sigreturn_codes[7];
|
extern const unsigned long sigreturn_codes[7];
|
||||||
|
extern const unsigned long syscall_restart_code[2];
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* linux/arch/arm/kernel/traps.c
|
* linux/arch/arm/kernel/traps.c
|
||||||
*
|
*
|
||||||
* Copyright (C) 1995-2002 Russell King
|
* Copyright (C) 1995-2009 Russell King
|
||||||
* Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
|
* Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
@ -751,6 +751,8 @@ void __init early_trap_init(void)
|
||||||
*/
|
*/
|
||||||
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
|
memcpy((void *)KERN_SIGRETURN_CODE, sigreturn_codes,
|
||||||
sizeof(sigreturn_codes));
|
sizeof(sigreturn_codes));
|
||||||
|
memcpy((void *)KERN_RESTART_CODE, syscall_restart_code,
|
||||||
|
sizeof(syscall_restart_code));
|
||||||
|
|
||||||
flush_icache_range(vectors, vectors + PAGE_SIZE);
|
flush_icache_range(vectors, vectors + PAGE_SIZE);
|
||||||
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
|
modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
|
||||||
|
|
Loading…
Reference in New Issue