aboutsummaryrefslogtreecommitdiffstats
path: root/fs/exec.c
diff options
context:
space:
mode:
authorHeiko Carstens <heiko.carstens@de.ibm.com>2012-02-04 04:47:10 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2012-02-06 18:15:20 -0500
commit96e02d1586782eadf051fa3d6bc4132d2447ac2c (patch)
treedd40e6302d2770d3c51cced64cfd3b8cf1355da4 /fs/exec.c
parent23783f817bceedd6d4e549385e3f400ea64059e5 (diff)
exec: fix use-after-free bug in setup_new_exec()
Setting the task name is done within setup_new_exec() by accessing bprm->filename. However this happens after flush_old_exec(). This may result in a use after free bug, flush_old_exec() may "complete" vfork_done, which will wake up the parent which in turn may free the passed in filename. To fix this add a new tcomm field in struct linux_binprm which contains the now early generated task name until it is used. Fixes this bug on s390: Unable to handle kernel pointer dereference at virtual kernel address 0000000039768000 Process kworker/u:3 (pid: 245, task: 000000003a3dc840, ksp: 0000000039453818) Krnl PSW : 0704000180000000 0000000000282e94 (setup_new_exec+0xa0/0x374) Call Trace: ([<0000000000282e2c>] setup_new_exec+0x38/0x374) [<00000000002dd12e>] load_elf_binary+0x402/0x1bf4 [<0000000000280a42>] search_binary_handler+0x38e/0x5bc [<0000000000282b6c>] do_execve_common+0x410/0x514 [<0000000000282cb6>] do_execve+0x46/0x58 [<00000000005bce58>] kernel_execve+0x28/0x70 [<000000000014ba2e>] ____call_usermodehelper+0x102/0x140 [<00000000005bc8da>] kernel_thread_starter+0x6/0xc [<00000000005bc8d4>] kernel_thread_starter+0x0/0xc Last Breaking-Event-Address: [<00000000002830f0>] setup_new_exec+0x2fc/0x374 Kernel panic - not syncing: Fatal exception: panic_on_oops Reported-by: Sebastian Ott <sebott@linux.vnet.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/exec.c')
-rw-r--r--fs/exec.c33
1 files changed, 17 insertions, 16 deletions
diff --git a/fs/exec.c b/fs/exec.c
index aeb135c7ff5c..92ce83a11e90 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1071,6 +1071,21 @@ void set_task_comm(struct task_struct *tsk, char *buf)
1071 perf_event_comm(tsk); 1071 perf_event_comm(tsk);
1072} 1072}
1073 1073
1074static void filename_to_taskname(char *tcomm, const char *fn, unsigned int len)
1075{
1076 int i, ch;
1077
1078 /* Copies the binary name from after last slash */
1079 for (i = 0; (ch = *(fn++)) != '\0';) {
1080 if (ch == '/')
1081 i = 0; /* overwrite what we wrote */
1082 else
1083 if (i < len - 1)
1084 tcomm[i++] = ch;
1085 }
1086 tcomm[i] = '\0';
1087}
1088
1074int flush_old_exec(struct linux_binprm * bprm) 1089int flush_old_exec(struct linux_binprm * bprm)
1075{ 1090{
1076 int retval; 1091 int retval;
@@ -1085,6 +1100,7 @@ int flush_old_exec(struct linux_binprm * bprm)
1085 1100
1086 set_mm_exe_file(bprm->mm, bprm->file); 1101 set_mm_exe_file(bprm->mm, bprm->file);
1087 1102
1103 filename_to_taskname(bprm->tcomm, bprm->filename, sizeof(bprm->tcomm));
1088 /* 1104 /*
1089 * Release all of the old mmap stuff 1105 * Release all of the old mmap stuff
1090 */ 1106 */
@@ -1116,10 +1132,6 @@ EXPORT_SYMBOL(would_dump);
1116 1132
1117void setup_new_exec(struct linux_binprm * bprm) 1133void setup_new_exec(struct linux_binprm * bprm)
1118{ 1134{
1119 int i, ch;
1120 const char *name;
1121 char tcomm[sizeof(current->comm)];
1122
1123 arch_pick_mmap_layout(current->mm); 1135 arch_pick_mmap_layout(current->mm);
1124 1136
1125 /* This is the point of no return */ 1137 /* This is the point of no return */
@@ -1130,18 +1142,7 @@ void setup_new_exec(struct linux_binprm * bprm)
1130 else 1142 else
1131 set_dumpable(current->mm, suid_dumpable); 1143 set_dumpable(current->mm, suid_dumpable);
1132 1144
1133 name = bprm->filename; 1145 set_task_comm(current, bprm->tcomm);
1134
1135 /* Copies the binary name from after last slash */
1136 for (i=0; (ch = *(name++)) != '\0';) {
1137 if (ch == '/')
1138 i = 0; /* overwrite what we wrote */
1139 else
1140 if (i < (sizeof(tcomm) - 1))
1141 tcomm[i++] = ch;
1142 }
1143 tcomm[i] = '\0';
1144 set_task_comm(current, tcomm);
1145 1146
1146 /* Set the new mm task size. We have to do that late because it may 1147 /* Set the new mm task size. We have to do that late because it may
1147 * depend on TIF_32BIT which is only updated in flush_thread() on 1148 * depend on TIF_32BIT which is only updated in flush_thread() on