]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/3.4.25/exec-do-not-leave-bprm-interp-on-stack.patch
4.14-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 3.4.25 / exec-do-not-leave-bprm-interp-on-stack.patch
CommitLineData
a0f1e827
GKH
1From b66c5984017533316fd1951770302649baf1aa33 Mon Sep 17 00:00:00 2001
2From: Kees Cook <keescook@chromium.org>
3Date: Thu, 20 Dec 2012 15:05:16 -0800
4Subject: exec: do not leave bprm->interp on stack
5
6From: Kees Cook <keescook@chromium.org>
7
8commit b66c5984017533316fd1951770302649baf1aa33 upstream.
9
10If a series of scripts are executed, each triggering module loading via
11unprintable bytes in the script header, kernel stack contents can leak
12into the command line.
13
14Normally execution of binfmt_script and binfmt_misc happens recursively.
15However, when modules are enabled, and unprintable bytes exist in the
16bprm->buf, execution will restart after attempting to load matching
17binfmt modules. Unfortunately, the logic in binfmt_script and
18binfmt_misc does not expect to get restarted. They leave bprm->interp
19pointing to their local stack. This means on restart bprm->interp is
20left pointing into unused stack memory which can then be copied into the
21userspace argv areas.
22
23After additional study, it seems that both recursion and restart remains
24the desirable way to handle exec with scripts, misc, and modules. As
25such, we need to protect the changes to interp.
26
27This changes the logic to require allocation for any changes to the
28bprm->interp. To avoid adding a new kmalloc to every exec, the default
29value is left as-is. Only when passing through binfmt_script or
30binfmt_misc does an allocation take place.
31
32For a proof of concept, see DoTest.sh from:
33
34 http://www.halfdog.net/Security/2012/LinuxKernelBinfmtScriptStackDataDisclosure/
35
36Signed-off-by: Kees Cook <keescook@chromium.org>
37Cc: halfdog <me@halfdog.net>
38Cc: P J P <ppandit@redhat.com>
39Cc: Alexander Viro <viro@zeniv.linux.org.uk>
40Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
41Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
42Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
43
44---
45 fs/binfmt_misc.c | 5 ++++-
46 fs/binfmt_script.c | 4 +++-
47 fs/exec.c | 15 +++++++++++++++
48 include/linux/binfmts.h | 1 +
49 4 files changed, 23 insertions(+), 2 deletions(-)
50
51--- a/fs/binfmt_misc.c
52+++ b/fs/binfmt_misc.c
53@@ -176,7 +176,10 @@ static int load_misc_binary(struct linux
54 goto _error;
55 bprm->argc ++;
56
57- bprm->interp = iname; /* for binfmt_script */
58+ /* Update interp in case binfmt_script needs it. */
59+ retval = bprm_change_interp(iname, bprm);
60+ if (retval < 0)
61+ goto _error;
62
63 interp_file = open_exec (iname);
64 retval = PTR_ERR (interp_file);
65--- a/fs/binfmt_script.c
66+++ b/fs/binfmt_script.c
67@@ -82,7 +82,9 @@ static int load_script(struct linux_binp
68 retval = copy_strings_kernel(1, &i_name, bprm);
69 if (retval) return retval;
70 bprm->argc++;
71- bprm->interp = interp;
72+ retval = bprm_change_interp(interp, bprm);
73+ if (retval < 0)
74+ return retval;
75
76 /*
77 * OK, now restart the process with the interpreter's dentry.
78--- a/fs/exec.c
79+++ b/fs/exec.c
80@@ -1206,9 +1206,24 @@ void free_bprm(struct linux_binprm *bprm
81 mutex_unlock(&current->signal->cred_guard_mutex);
82 abort_creds(bprm->cred);
83 }
84+ /* If a binfmt changed the interp, free it. */
85+ if (bprm->interp != bprm->filename)
86+ kfree(bprm->interp);
87 kfree(bprm);
88 }
89
90+int bprm_change_interp(char *interp, struct linux_binprm *bprm)
91+{
92+ /* If a binfmt changed the interp, free it first. */
93+ if (bprm->interp != bprm->filename)
94+ kfree(bprm->interp);
95+ bprm->interp = kstrdup(interp, GFP_KERNEL);
96+ if (!bprm->interp)
97+ return -ENOMEM;
98+ return 0;
99+}
100+EXPORT_SYMBOL(bprm_change_interp);
101+
102 /*
103 * install the new credentials for this executable
104 */
105--- a/include/linux/binfmts.h
106+++ b/include/linux/binfmts.h
107@@ -128,6 +128,7 @@ extern int setup_arg_pages(struct linux_
108 unsigned long stack_top,
109 int executable_stack);
110 extern int bprm_mm_init(struct linux_binprm *bprm);
111+extern int bprm_change_interp(char *interp, struct linux_binprm *bprm);
112 extern int copy_strings_kernel(int argc, const char *const *argv,
113 struct linux_binprm *bprm);
114 extern int prepare_bprm_creds(struct linux_binprm *bprm);