Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Heiko Carstens and committed by
Linus Torvalds
96e02d15 23783f81

+19 -17
+17 -16
fs/exec.c
··· 1071 1071 perf_event_comm(tsk); 1072 1072 } 1073 1073 1074 + static 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 + 1074 1089 int flush_old_exec(struct linux_binprm * bprm) 1075 1090 { 1076 1091 int retval; ··· 1100 1085 1101 1086 set_mm_exe_file(bprm->mm, bprm->file); 1102 1087 1088 + filename_to_taskname(bprm->tcomm, bprm->filename, sizeof(bprm->tcomm)); 1103 1089 /* 1104 1090 * Release all of the old mmap stuff 1105 1091 */ ··· 1132 1116 1133 1117 void setup_new_exec(struct linux_binprm * bprm) 1134 1118 { 1135 - int i, ch; 1136 - const char *name; 1137 - char tcomm[sizeof(current->comm)]; 1138 - 1139 1119 arch_pick_mmap_layout(current->mm); 1140 1120 1141 1121 /* This is the point of no return */ ··· 1142 1130 else 1143 1131 set_dumpable(current->mm, suid_dumpable); 1144 1132 1145 - name = bprm->filename; 1146 - 1147 - /* Copies the binary name from after last slash */ 1148 - for (i=0; (ch = *(name++)) != '\0';) { 1149 - if (ch == '/') 1150 - i = 0; /* overwrite what we wrote */ 1151 - else 1152 - if (i < (sizeof(tcomm) - 1)) 1153 - tcomm[i++] = ch; 1154 - } 1155 - tcomm[i] = '\0'; 1156 - set_task_comm(current, tcomm); 1133 + set_task_comm(current, bprm->tcomm); 1157 1134 1158 1135 /* Set the new mm task size. We have to do that late because it may 1159 1136 * depend on TIF_32BIT which is only updated in flush_thread() on
+2 -1
include/linux/binfmts.h
··· 18 18 #define BINPRM_BUF_SIZE 128 19 19 20 20 #ifdef __KERNEL__ 21 - #include <linux/list.h> 21 + #include <linux/sched.h> 22 22 23 23 #define CORENAME_MAX_SIZE 128 24 24 ··· 58 58 unsigned interp_flags; 59 59 unsigned interp_data; 60 60 unsigned long loader, exec; 61 + char tcomm[TASK_COMM_LEN]; 61 62 }; 62 63 63 64 #define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0