aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2012-09-30 13:20:09 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2012-09-30 13:36:39 -0400
commit282124d18626379a20b41d25e0c580f290cd09d4 (patch)
tree48adb982843cbb133c8079f06abad2911e019f12
parenta3460a59747cfddfa7be4758e5ef08bf5d751d59 (diff)
generic kernel_execve()
based mostly on arm and alpha versions. Architectures can define __ARCH_WANT_KERNEL_EXECVE and use it, provided that * they have working current_pt_regs(), even for kernel threads. * kernel_thread-spawned threads do have space for pt_regs in the normal location. Normally that's as simple as switching to generic kernel_thread() and making sure that kernel threads do *not* go through return from syscall path; call the payload from equivalent of ret_from_fork if we are in a kernel thread (or just have separate ret_from_kernel_thread and make copy_thread() use it instead of ret_from_fork in kernel thread case). * they have ret_from_kernel_execve(); it is called after successful do_execve() done by kernel_execve() and gets normal pt_regs location passed to it as argument. It's essentially a longjmp() analog - it should set sp, etc. to the situation expected at the return for syscall and go there. Eventually the need for that sucker will disappear, but that'll take some surgery on kernel_thread() payloads. Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r--fs/exec.c22
-rw-r--r--include/linux/binfmts.h5
2 files changed, 27 insertions, 0 deletions
diff --git a/fs/exec.c b/fs/exec.c
index d7f9e14f8977..48c525115fe4 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -2318,3 +2318,25 @@ int dump_seek(struct file *file, loff_t off)
2318 return ret; 2318 return ret;
2319} 2319}
2320EXPORT_SYMBOL(dump_seek); 2320EXPORT_SYMBOL(dump_seek);
2321
2322#ifdef __ARCH_WANT_KERNEL_EXECVE
2323int kernel_execve(const char *filename,
2324 const char *const argv[],
2325 const char *const envp[])
2326{
2327 struct pt_regs *p = current_pt_regs();
2328 int ret;
2329
2330 ret = do_execve(filename,
2331 (const char __user *const __user *)argv,
2332 (const char __user *const __user *)envp, p);
2333 if (ret < 0)
2334 return ret;
2335
2336 /*
2337 * We were successful. We won't be returning to our caller, but
2338 * instead to user space by manipulating the kernel stack.
2339 */
2340 ret_from_kernel_execve(p);
2341}
2342#endif
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index 8938beabad7a..f9c9d08f4f7c 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -19,6 +19,7 @@ struct pt_regs;
19 19
20#ifdef __KERNEL__ 20#ifdef __KERNEL__
21#include <linux/sched.h> 21#include <linux/sched.h>
22#include <linux/unistd.h>
22#include <asm/exec.h> 23#include <asm/exec.h>
23 24
24#define CORENAME_MAX_SIZE 128 25#define CORENAME_MAX_SIZE 128
@@ -137,5 +138,9 @@ extern void do_coredump(long signr, int exit_code, struct pt_regs *regs);
137extern void set_binfmt(struct linux_binfmt *new); 138extern void set_binfmt(struct linux_binfmt *new);
138extern void free_bprm(struct linux_binprm *); 139extern void free_bprm(struct linux_binprm *);
139 140
141#ifdef __ARCH_WANT_KERNEL_EXECVE
142extern void ret_from_kernel_execve(struct pt_regs *normal) __noreturn;
143#endif
144
140#endif /* __KERNEL__ */ 145#endif /* __KERNEL__ */
141#endif /* _LINUX_BINFMTS_H */ 146#endif /* _LINUX_BINFMTS_H */