Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 96e02d15 authored by Heiko Carstens's avatar Heiko Carstens Committed by Linus Torvalds
Browse files

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: default avatarSebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 23783f81
Loading
Loading
Loading
Loading
+17 −16
Original line number Original line Diff line number Diff line
@@ -1071,6 +1071,21 @@ void set_task_comm(struct task_struct *tsk, char *buf)
	perf_event_comm(tsk);
	perf_event_comm(tsk);
}
}


static void filename_to_taskname(char *tcomm, const char *fn, unsigned int len)
{
	int i, ch;

	/* Copies the binary name from after last slash */
	for (i = 0; (ch = *(fn++)) != '\0';) {
		if (ch == '/')
			i = 0; /* overwrite what we wrote */
		else
			if (i < len - 1)
				tcomm[i++] = ch;
	}
	tcomm[i] = '\0';
}

int flush_old_exec(struct linux_binprm * bprm)
int flush_old_exec(struct linux_binprm * bprm)
{
{
	int retval;
	int retval;
@@ -1085,6 +1100,7 @@ int flush_old_exec(struct linux_binprm * bprm)


	set_mm_exe_file(bprm->mm, bprm->file);
	set_mm_exe_file(bprm->mm, bprm->file);


	filename_to_taskname(bprm->tcomm, bprm->filename, sizeof(bprm->tcomm));
	/*
	/*
	 * Release all of the old mmap stuff
	 * Release all of the old mmap stuff
	 */
	 */
@@ -1116,10 +1132,6 @@ EXPORT_SYMBOL(would_dump);


void setup_new_exec(struct linux_binprm * bprm)
void setup_new_exec(struct linux_binprm * bprm)
{
{
	int i, ch;
	const char *name;
	char tcomm[sizeof(current->comm)];

	arch_pick_mmap_layout(current->mm);
	arch_pick_mmap_layout(current->mm);


	/* This is the point of no return */
	/* This is the point of no return */
@@ -1130,18 +1142,7 @@ void setup_new_exec(struct linux_binprm * bprm)
	else
	else
		set_dumpable(current->mm, suid_dumpable);
		set_dumpable(current->mm, suid_dumpable);


	name = bprm->filename;
	set_task_comm(current, bprm->tcomm);

	/* Copies the binary name from after last slash */
	for (i=0; (ch = *(name++)) != '\0';) {
		if (ch == '/')
			i = 0; /* overwrite what we wrote */
		else
			if (i < (sizeof(tcomm) - 1))
				tcomm[i++] = ch;
	}
	tcomm[i] = '\0';
	set_task_comm(current, tcomm);


	/* Set the new mm task size. We have to do that late because it may
	/* Set the new mm task size. We have to do that late because it may
	 * depend on TIF_32BIT which is only updated in flush_thread() on
	 * depend on TIF_32BIT which is only updated in flush_thread() on
+2 −1
Original line number Original line Diff line number Diff line
@@ -18,7 +18,7 @@ struct pt_regs;
#define BINPRM_BUF_SIZE 128
#define BINPRM_BUF_SIZE 128


#ifdef __KERNEL__
#ifdef __KERNEL__
#include <linux/list.h>
#include <linux/sched.h>


#define CORENAME_MAX_SIZE 128
#define CORENAME_MAX_SIZE 128


@@ -58,6 +58,7 @@ struct linux_binprm {
	unsigned interp_flags;
	unsigned interp_flags;
	unsigned interp_data;
	unsigned interp_data;
	unsigned long loader, exec;
	unsigned long loader, exec;
	char tcomm[TASK_COMM_LEN];
};
};


#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0