aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-02-05 15:54:53 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2014-02-05 15:54:53 -0500
commitc4ad8f98bef77c7356aa6a9ad9188a6acc6b849d (patch)
tree16117463e3106b2be026afbe843019cf5d3c3270
parent878a876b2e10888afe53766dcca33f723ae20edc (diff)
execve: use 'struct filename *' for executable name passing
This changes 'do_execve()' to get the executable name as a 'struct filename', and to free it when it is done. This is what the normal users want, and it simplifies and streamlines their error handling. The controlled lifetime of the executable name also fixes a use-after-free problem with the trace_sched_process_exec tracepoint: the lifetime of the passed-in string for kernel users was not at all obvious, and the user-mode helper code used UMH_WAIT_EXEC to serialize the pathname allocation lifetime with the execve() having finished, which in turn meant that the trace point that happened after mm_release() of the old process VM ended up using already free'd memory. To solve the kernel string lifetime issue, this simply introduces "getname_kernel()" that works like the normal user-space getname() function, except with the source coming from kernel memory. As Oleg points out, this also means that we could drop the tcomm[] array from 'struct linux_binprm', since the pathname lifetime now covers setup_new_exec(). That would be a separate cleanup. Reported-by: Igor Zhbanov <i.zhbanov@samsung.com> Tested-by: Steven Rostedt <rostedt@goodmis.org> Cc: Oleg Nesterov <oleg@redhat.com> Cc: Al Viro <viro@zeniv.linux.org.uk> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--arch/parisc/hpux/fs.c15
-rw-r--r--fs/exec.c45
-rw-r--r--fs/namei.c30
-rw-r--r--include/linux/binfmts.h1
-rw-r--r--include/linux/fs.h1
-rw-r--r--include/linux/sched.h3
-rw-r--r--init/main.c2
-rw-r--r--kernel/auditsc.c2
-rw-r--r--kernel/kmod.c2
9 files changed, 58 insertions, 43 deletions
diff --git a/arch/parisc/hpux/fs.c b/arch/parisc/hpux/fs.c
index 88d0962de65a..2bedafea3d94 100644
--- a/arch/parisc/hpux/fs.c
+++ b/arch/parisc/hpux/fs.c
@@ -33,22 +33,9 @@
33 33
34int hpux_execve(struct pt_regs *regs) 34int hpux_execve(struct pt_regs *regs)
35{ 35{
36 int error; 36 return do_execve(getname((const char __user *) regs->gr[26]),
37 struct filename *filename;
38
39 filename = getname((const char __user *) regs->gr[26]);
40 error = PTR_ERR(filename);
41 if (IS_ERR(filename))
42 goto out;
43
44 error = do_execve(filename->name,
45 (const char __user *const __user *) regs->gr[25], 37 (const char __user *const __user *) regs->gr[25],
46 (const char __user *const __user *) regs->gr[24]); 38 (const char __user *const __user *) regs->gr[24]);
47
48 putname(filename);
49
50out:
51 return error;
52} 39}
53 40
54struct hpux_dirent { 41struct hpux_dirent {
diff --git a/fs/exec.c b/fs/exec.c
index e1529b4c79b1..3d78fccdd723 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -748,11 +748,10 @@ EXPORT_SYMBOL(setup_arg_pages);
748 748
749#endif /* CONFIG_MMU */ 749#endif /* CONFIG_MMU */
750 750
751struct file *open_exec(const char *name) 751static struct file *do_open_exec(struct filename *name)
752{ 752{
753 struct file *file; 753 struct file *file;
754 int err; 754 int err;
755 struct filename tmp = { .name = name };
756 static const struct open_flags open_exec_flags = { 755 static const struct open_flags open_exec_flags = {
757 .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC, 756 .open_flag = O_LARGEFILE | O_RDONLY | __FMODE_EXEC,
758 .acc_mode = MAY_EXEC | MAY_OPEN, 757 .acc_mode = MAY_EXEC | MAY_OPEN,
@@ -760,7 +759,7 @@ struct file *open_exec(const char *name)
760 .lookup_flags = LOOKUP_FOLLOW, 759 .lookup_flags = LOOKUP_FOLLOW,
761 }; 760 };
762 761
763 file = do_filp_open(AT_FDCWD, &tmp, &open_exec_flags); 762 file = do_filp_open(AT_FDCWD, name, &open_exec_flags);
764 if (IS_ERR(file)) 763 if (IS_ERR(file))
765 goto out; 764 goto out;
766 765
@@ -784,6 +783,12 @@ exit:
784 fput(file); 783 fput(file);
785 return ERR_PTR(err); 784 return ERR_PTR(err);
786} 785}
786
787struct file *open_exec(const char *name)
788{
789 struct filename tmp = { .name = name };
790 return do_open_exec(&tmp);
791}
787EXPORT_SYMBOL(open_exec); 792EXPORT_SYMBOL(open_exec);
788 793
789int kernel_read(struct file *file, loff_t offset, 794int kernel_read(struct file *file, loff_t offset,
@@ -1162,7 +1167,7 @@ int prepare_bprm_creds(struct linux_binprm *bprm)
1162 return -ENOMEM; 1167 return -ENOMEM;
1163} 1168}
1164 1169
1165void free_bprm(struct linux_binprm *bprm) 1170static void free_bprm(struct linux_binprm *bprm)
1166{ 1171{
1167 free_arg_pages(bprm); 1172 free_arg_pages(bprm);
1168 if (bprm->cred) { 1173 if (bprm->cred) {
@@ -1432,7 +1437,7 @@ static int exec_binprm(struct linux_binprm *bprm)
1432/* 1437/*
1433 * sys_execve() executes a new program. 1438 * sys_execve() executes a new program.
1434 */ 1439 */
1435static int do_execve_common(const char *filename, 1440static int do_execve_common(struct filename *filename,
1436 struct user_arg_ptr argv, 1441 struct user_arg_ptr argv,
1437 struct user_arg_ptr envp) 1442 struct user_arg_ptr envp)
1438{ 1443{
@@ -1441,6 +1446,9 @@ static int do_execve_common(const char *filename,
1441 struct files_struct *displaced; 1446 struct files_struct *displaced;
1442 int retval; 1447 int retval;
1443 1448
1449 if (IS_ERR(filename))
1450 return PTR_ERR(filename);
1451
1444 /* 1452 /*
1445 * We move the actual failure in case of RLIMIT_NPROC excess from 1453 * We move the actual failure in case of RLIMIT_NPROC excess from
1446 * set*uid() to execve() because too many poorly written programs 1454 * set*uid() to execve() because too many poorly written programs
@@ -1473,7 +1481,7 @@ static int do_execve_common(const char *filename,
1473 check_unsafe_exec(bprm); 1481 check_unsafe_exec(bprm);
1474 current->in_execve = 1; 1482 current->in_execve = 1;
1475 1483
1476 file = open_exec(filename); 1484 file = do_open_exec(filename);
1477 retval = PTR_ERR(file); 1485 retval = PTR_ERR(file);
1478 if (IS_ERR(file)) 1486 if (IS_ERR(file))
1479 goto out_unmark; 1487 goto out_unmark;
@@ -1481,8 +1489,7 @@ static int do_execve_common(const char *filename,
1481 sched_exec(); 1489 sched_exec();
1482 1490
1483 bprm->file = file; 1491 bprm->file = file;
1484 bprm->filename = filename; 1492 bprm->filename = bprm->interp = filename->name;
1485 bprm->interp = filename;
1486 1493
1487 retval = bprm_mm_init(bprm); 1494 retval = bprm_mm_init(bprm);
1488 if (retval) 1495 if (retval)
@@ -1523,6 +1530,7 @@ static int do_execve_common(const char *filename,
1523 acct_update_integrals(current); 1530 acct_update_integrals(current);
1524 task_numa_free(current); 1531 task_numa_free(current);
1525 free_bprm(bprm); 1532 free_bprm(bprm);
1533 putname(filename);
1526 if (displaced) 1534 if (displaced)
1527 put_files_struct(displaced); 1535 put_files_struct(displaced);
1528 return retval; 1536 return retval;
@@ -1544,10 +1552,11 @@ out_files:
1544 if (displaced) 1552 if (displaced)
1545 reset_files_struct(displaced); 1553 reset_files_struct(displaced);
1546out_ret: 1554out_ret:
1555 putname(filename);
1547 return retval; 1556 return retval;
1548} 1557}
1549 1558
1550int do_execve(const char *filename, 1559int do_execve(struct filename *filename,
1551 const char __user *const __user *__argv, 1560 const char __user *const __user *__argv,
1552 const char __user *const __user *__envp) 1561 const char __user *const __user *__envp)
1553{ 1562{
@@ -1557,7 +1566,7 @@ int do_execve(const char *filename,
1557} 1566}
1558 1567
1559#ifdef CONFIG_COMPAT 1568#ifdef CONFIG_COMPAT
1560static int compat_do_execve(const char *filename, 1569static int compat_do_execve(struct filename *filename,
1561 const compat_uptr_t __user *__argv, 1570 const compat_uptr_t __user *__argv,
1562 const compat_uptr_t __user *__envp) 1571 const compat_uptr_t __user *__envp)
1563{ 1572{
@@ -1607,25 +1616,13 @@ SYSCALL_DEFINE3(execve,
1607 const char __user *const __user *, argv, 1616 const char __user *const __user *, argv,
1608 const char __user *const __user *, envp) 1617 const char __user *const __user *, envp)
1609{ 1618{
1610 struct filename *path = getname(filename); 1619 return do_execve(getname(filename), argv, envp);
1611 int error = PTR_ERR(path);
1612 if (!IS_ERR(path)) {
1613 error = do_execve(path->name, argv, envp);
1614 putname(path);
1615 }
1616 return error;
1617} 1620}
1618#ifdef CONFIG_COMPAT 1621#ifdef CONFIG_COMPAT
1619asmlinkage long compat_sys_execve(const char __user * filename, 1622asmlinkage long compat_sys_execve(const char __user * filename,
1620 const compat_uptr_t __user * argv, 1623 const compat_uptr_t __user * argv,
1621 const compat_uptr_t __user * envp) 1624 const compat_uptr_t __user * envp)
1622{ 1625{
1623 struct filename *path = getname(filename); 1626 return compat_do_execve(getname(filename), argv, envp);
1624 int error = PTR_ERR(path);
1625 if (!IS_ERR(path)) {
1626 error = compat_do_execve(path->name, argv, envp);
1627 putname(path);
1628 }
1629 return error;
1630} 1627}
1631#endif 1628#endif
diff --git a/fs/namei.c b/fs/namei.c
index d580df2e6804..385f7817bfcc 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -196,6 +196,7 @@ recopy:
196 goto error; 196 goto error;
197 197
198 result->uptr = filename; 198 result->uptr = filename;
199 result->aname = NULL;
199 audit_getname(result); 200 audit_getname(result);
200 return result; 201 return result;
201 202
@@ -210,6 +211,35 @@ getname(const char __user * filename)
210 return getname_flags(filename, 0, NULL); 211 return getname_flags(filename, 0, NULL);
211} 212}
212 213
214/*
215 * The "getname_kernel()" interface doesn't do pathnames longer
216 * than EMBEDDED_NAME_MAX. Deal with it - you're a kernel user.
217 */
218struct filename *
219getname_kernel(const char * filename)
220{
221 struct filename *result;
222 char *kname;
223 int len;
224
225 len = strlen(filename);
226 if (len >= EMBEDDED_NAME_MAX)
227 return ERR_PTR(-ENAMETOOLONG);
228
229 result = __getname();
230 if (unlikely(!result))
231 return ERR_PTR(-ENOMEM);
232
233 kname = (char *)result + sizeof(*result);
234 result->name = kname;
235 result->uptr = NULL;
236 result->aname = NULL;
237 result->separate = false;
238
239 strlcpy(kname, filename, EMBEDDED_NAME_MAX);
240 return result;
241}
242
213#ifdef CONFIG_AUDITSYSCALL 243#ifdef CONFIG_AUDITSYSCALL
214void putname(struct filename *name) 244void putname(struct filename *name)
215{ 245{
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index fd8bf3219ef7..b4a745d7d9a9 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -115,7 +115,6 @@ extern int copy_strings_kernel(int argc, const char *const *argv,
115extern int prepare_bprm_creds(struct linux_binprm *bprm); 115extern int prepare_bprm_creds(struct linux_binprm *bprm);
116extern void install_exec_creds(struct linux_binprm *bprm); 116extern void install_exec_creds(struct linux_binprm *bprm);
117extern void set_binfmt(struct linux_binfmt *new); 117extern void set_binfmt(struct linux_binfmt *new);
118extern void free_bprm(struct linux_binprm *);
119extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t); 118extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);
120 119
121#endif /* _LINUX_BINFMTS_H */ 120#endif /* _LINUX_BINFMTS_H */
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 09f553c59813..d79678c188ad 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2079,6 +2079,7 @@ extern struct file * dentry_open(const struct path *, int, const struct cred *);
2079extern int filp_close(struct file *, fl_owner_t id); 2079extern int filp_close(struct file *, fl_owner_t id);
2080 2080
2081extern struct filename *getname(const char __user *); 2081extern struct filename *getname(const char __user *);
2082extern struct filename *getname_kernel(const char *);
2082 2083
2083enum { 2084enum {
2084 FILE_CREATED = 1, 2085 FILE_CREATED = 1,
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 68a0e84463a0..a781dec1cd0b 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -128,6 +128,7 @@ struct bio_list;
128struct fs_struct; 128struct fs_struct;
129struct perf_event_context; 129struct perf_event_context;
130struct blk_plug; 130struct blk_plug;
131struct filename;
131 132
132/* 133/*
133 * List of flags we want to share for kernel threads, 134 * List of flags we want to share for kernel threads,
@@ -2311,7 +2312,7 @@ extern void do_group_exit(int);
2311extern int allow_signal(int); 2312extern int allow_signal(int);
2312extern int disallow_signal(int); 2313extern int disallow_signal(int);
2313 2314
2314extern int do_execve(const char *, 2315extern int do_execve(struct filename *,
2315 const char __user * const __user *, 2316 const char __user * const __user *,
2316 const char __user * const __user *); 2317 const char __user * const __user *);
2317extern long do_fork(unsigned long, unsigned long, unsigned long, int __user *, int __user *); 2318extern long do_fork(unsigned long, unsigned long, unsigned long, int __user *, int __user *);
diff --git a/init/main.c b/init/main.c
index 2fd9cef70ee8..eb03090cdced 100644
--- a/init/main.c
+++ b/init/main.c
@@ -812,7 +812,7 @@ void __init load_default_modules(void)
812static int run_init_process(const char *init_filename) 812static int run_init_process(const char *init_filename)
813{ 813{
814 argv_init[0] = init_filename; 814 argv_init[0] = init_filename;
815 return do_execve(init_filename, 815 return do_execve(getname_kernel(init_filename),
816 (const char __user *const __user *)argv_init, 816 (const char __user *const __user *)argv_init,
817 (const char __user *const __user *)envp_init); 817 (const char __user *const __user *)envp_init);
818} 818}
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 10176cd5956a..7aef2f4b6c64 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -1719,7 +1719,7 @@ void audit_putname(struct filename *name)
1719 struct audit_context *context = current->audit_context; 1719 struct audit_context *context = current->audit_context;
1720 1720
1721 BUG_ON(!context); 1721 BUG_ON(!context);
1722 if (!context->in_syscall) { 1722 if (!name->aname || !context->in_syscall) {
1723#if AUDIT_DEBUG == 2 1723#if AUDIT_DEBUG == 2
1724 printk(KERN_ERR "%s:%d(:%d): final_putname(%p)\n", 1724 printk(KERN_ERR "%s:%d(:%d): final_putname(%p)\n",
1725 __FILE__, __LINE__, context->serial, name); 1725 __FILE__, __LINE__, context->serial, name);
diff --git a/kernel/kmod.c b/kernel/kmod.c
index b086006c59e7..6b375af4958d 100644
--- a/kernel/kmod.c
+++ b/kernel/kmod.c
@@ -239,7 +239,7 @@ static int ____call_usermodehelper(void *data)
239 239
240 commit_creds(new); 240 commit_creds(new);
241 241
242 retval = do_execve(sub_info->path, 242 retval = do_execve(getname_kernel(sub_info->path),
243 (const char __user *const __user *)sub_info->argv, 243 (const char __user *const __user *)sub_info->argv,
244 (const char __user *const __user *)sub_info->envp); 244 (const char __user *const __user *)sub_info->envp);
245 if (!retval) 245 if (!retval)