From: Al Viro Date: Thu, 2 Aug 2012 07:46:39 +0000 (+0400) Subject: arm: introduce ret_from_kernel_execve(), switch to generic kernel_execve() X-Git-Tag: next-20120910~4^2~35 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=c4c29b2a764a07c0b1895692488ae7fea392f8b4;p=karo-tx-linux.git arm: introduce ret_from_kernel_execve(), switch to generic kernel_execve() ret_from_kernel_execve() is the asm tail of kernel_execve(), essentially; the rest became shiny new generic implementation, right in fs/exec.c. An architecture can provide ret_from_kernel_execve() and define __ARCH_WANT_KERNEL_EXECVE; then it'll get the generic one. Another new helper: current_pt_regs(). Default is task_pt_regs(current), and it's perfectly fine to just leave it as is. However, an architecture might choose to provide an optimized variant in its ptrace.h Signed-off-by: Al Viro --- diff --git a/arch/arm/include/asm/unistd.h b/arch/arm/include/asm/unistd.h index 0cab47d4a83f..2c9b7a87e64b 100644 --- a/arch/arm/include/asm/unistd.h +++ b/arch/arm/include/asm/unistd.h @@ -469,6 +469,7 @@ #define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_SYS_SOCKETCALL #endif +#define __ARCH_WANT_KERNEL_EXECVE /* * "Conditional" syscalls diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 978eac57e04a..08c682af5e52 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -91,6 +91,22 @@ ENTRY(ret_from_fork) b ret_slow_syscall ENDPROC(ret_from_fork) +/* + * turn a kernel thread into userland process + * use: ret_from_kernel_execve(struct pt_regs *normal, struct pt_regs *new) + */ +ENTRY(ret_from_kernel_execve) + mov why, #0 @ not a syscall + str why, [r1, #S_R0] @ ... and we want 0 in ->ARM_r0 as well + mov r2, #S_FRAME_SIZE + bl memmove @ copy regs to normal location; return + @ value is in r0 and it's the same as + @ the first argument, so r0 is unchanged. + get_thread_info tsk @ thread structure + mov sp, r0 @ stack pointer just under pt_regs + b ret_slow_syscall +ENDPROC(ret_from_kernel_execve) + .equ NR_syscalls,0 #define CALL(x) .equ NR_syscalls,NR_syscalls+1 #include "calls.S" diff --git a/arch/arm/kernel/sys_arm.c b/arch/arm/kernel/sys_arm.c index 3ba62e3cd1a2..c8e729efc187 100644 --- a/arch/arm/kernel/sys_arm.c +++ b/arch/arm/kernel/sys_arm.c @@ -79,48 +79,6 @@ out: return error; } -int kernel_execve(const char *filename, - const char *const argv[], - const char *const envp[]) -{ - struct pt_regs regs; - int ret; - - memset(®s, 0, sizeof(struct pt_regs)); - ret = do_execve(filename, - (const char __user *const __user *)argv, - (const char __user *const __user *)envp, ®s); - if (ret < 0) - goto out; - - /* - * Save argc to the register structure for userspace. - */ - regs.ARM_r0 = ret; - - /* - * We were successful. We won't be returning to our caller, but - * instead to user space by manipulating the kernel stack. - */ - asm( "add r0, %0, %1\n\t" - "mov r1, %2\n\t" - "mov r2, %3\n\t" - "bl memmove\n\t" /* copy regs to top of stack */ - "mov r8, #0\n\t" /* not a syscall */ - "mov r9, %0\n\t" /* thread structure */ - "mov sp, r0\n\t" /* reposition stack pointer */ - "b ret_to_user" - : - : "r" (current_thread_info()), - "Ir" (THREAD_START_SP - sizeof(regs)), - "r" (®s), - "Ir" (sizeof(regs)) - : "r0", "r1", "r2", "r3", "r8", "r9", "ip", "lr", "memory"); - - out: - return ret; -} - /* * Since loff_t is a 64 bit type we avoid a lot of ABI hassle * with a different argument ordering. diff --git a/fs/exec.c b/fs/exec.c index d7f9e14f8977..85652ce1ecfe 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -2318,3 +2318,26 @@ int dump_seek(struct file *file, loff_t off) return ret; } EXPORT_SYMBOL(dump_seek); + +#ifdef __ARCH_WANT_KERNEL_EXECVE +int kernel_execve(const char *filename, + const char *const argv[], + const char *const envp[]) +{ + struct pt_regs regs; + int ret; + + memset(®s, 0, sizeof(struct pt_regs)); + ret = do_execve(filename, + (const char __user *const __user *)argv, + (const char __user *const __user *)envp, ®s); + if (ret < 0) + return ret; + + /* + * We were successful. We won't be returning to our caller, but + * instead to user space by manipulating the kernel stack. + */ + ret_from_kernel_execve(current_pt_regs(), ®s); +} +#endif diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 8938beabad7a..d708558d96bd 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -19,6 +19,7 @@ struct pt_regs; #ifdef __KERNEL__ #include +#include #include #define CORENAME_MAX_SIZE 128 @@ -137,5 +138,10 @@ extern void do_coredump(long signr, int exit_code, struct pt_regs *regs); extern void set_binfmt(struct linux_binfmt *new); extern void free_bprm(struct linux_binprm *); +#ifdef __ARCH_WANT_KERNEL_EXECVE +extern void ret_from_kernel_execve(struct pt_regs *normal, struct pt_regs *new) + __noreturn; +#endif + #endif /* __KERNEL__ */ #endif /* _LINUX_BINFMTS_H */ diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 597e4fdb97fe..07fd922d6928 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -403,6 +403,10 @@ static inline void user_single_step_siginfo(struct task_struct *tsk, #define arch_ptrace_stop(code, info) do { } while (0) #endif +#ifndef current_pt_regs +#define current_pt_regs() task_pt_regs(current) +#endif + extern int task_current_syscall(struct task_struct *target, long *callno, unsigned long args[6], unsigned int maxargs, unsigned long *sp, unsigned long *pc);