Commit 2722ae9c authored by Kees Cook's avatar Kees Cook
Browse files

Merge branch 'coredump-vma-snapshot-fix-for-v5.18' of...

Merge branch 'coredump-vma-snapshot-fix-for-v5.18' of https://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace into for-next/execve

- Fix missing mmap_lock in file_files_note (Eric W. Biederman)
parents 9e1a3ce0 f833116a
Loading
Loading
Loading
Loading
+30 −36
Original line number Diff line number Diff line
@@ -1641,17 +1641,16 @@ static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata,
 *   long file_ofs
 * followed by COUNT filenames in ASCII: "FILE1" NUL "FILE2" NUL...
 */
static int fill_files_note(struct memelfnote *note)
static int fill_files_note(struct memelfnote *note, struct coredump_params *cprm)
{
	struct mm_struct *mm = current->mm;
	struct vm_area_struct *vma;
	unsigned count, size, names_ofs, remaining, n;
	user_long_t *data;
	user_long_t *start_end_ofs;
	char *name_base, *name_curpos;
	int i;

	/* *Estimated* file count and total data size needed */
	count = mm->map_count;
	count = cprm->vma_count;
	if (count > UINT_MAX / 64)
		return -EINVAL;
	size = count * 64;
@@ -1673,11 +1672,12 @@ static int fill_files_note(struct memelfnote *note)
	name_base = name_curpos = ((char *)data) + names_ofs;
	remaining = size - names_ofs;
	count = 0;
	for (vma = mm->mmap; vma != NULL; vma = vma->vm_next) {
	for (i = 0; i < cprm->vma_count; i++) {
		struct core_vma_metadata *m = &cprm->vma_meta[i];
		struct file *file;
		const char *filename;

		file = vma->vm_file;
		file = m->file;
		if (!file)
			continue;
		filename = file_path(file, name_curpos, remaining);
@@ -1697,9 +1697,9 @@ static int fill_files_note(struct memelfnote *note)
		memmove(name_curpos, filename, n);
		name_curpos += n;

		*start_end_ofs++ = vma->vm_start;
		*start_end_ofs++ = vma->vm_end;
		*start_end_ofs++ = vma->vm_pgoff;
		*start_end_ofs++ = m->start;
		*start_end_ofs++ = m->end;
		*start_end_ofs++ = m->pgoff;
		count++;
	}

@@ -1710,7 +1710,7 @@ static int fill_files_note(struct memelfnote *note)
	 * Count usually is less than mm->map_count,
	 * we need to move filenames down.
	 */
	n = mm->map_count - count;
	n = cprm->vma_count - count;
	if (n != 0) {
		unsigned shift_bytes = n * 3 * sizeof(data[0]);
		memmove(name_base - shift_bytes, name_base,
@@ -1822,7 +1822,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,

static int fill_note_info(struct elfhdr *elf, int phdrs,
			  struct elf_note_info *info,
			  const kernel_siginfo_t *siginfo, struct pt_regs *regs)
			  struct coredump_params *cprm)
{
	struct task_struct *dump_task = current;
	const struct user_regset_view *view = task_user_regset_view(dump_task);
@@ -1894,7 +1894,7 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
	 * Now fill in each thread's information.
	 */
	for (t = info->thread; t != NULL; t = t->next)
		if (!fill_thread_core_info(t, view, siginfo->si_signo, &info->size))
		if (!fill_thread_core_info(t, view, cprm->siginfo->si_signo, &info->size))
			return 0;

	/*
@@ -1903,13 +1903,13 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
	fill_psinfo(psinfo, dump_task->group_leader, dump_task->mm);
	info->size += notesize(&info->psinfo);

	fill_siginfo_note(&info->signote, &info->csigdata, siginfo);
	fill_siginfo_note(&info->signote, &info->csigdata, cprm->siginfo);
	info->size += notesize(&info->signote);

	fill_auxv_note(&info->auxv, current->mm);
	info->size += notesize(&info->auxv);

	if (fill_files_note(&info->files) == 0)
	if (fill_files_note(&info->files, cprm) == 0)
		info->size += notesize(&info->files);

	return 1;
@@ -2051,7 +2051,7 @@ static int elf_note_info_init(struct elf_note_info *info)

static int fill_note_info(struct elfhdr *elf, int phdrs,
			  struct elf_note_info *info,
			  const kernel_siginfo_t *siginfo, struct pt_regs *regs)
			  struct coredump_params *cprm)
{
	struct core_thread *ct;
	struct elf_thread_status *ets;
@@ -2072,13 +2072,13 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
	list_for_each_entry(ets, &info->thread_list, list) {
		int sz;

		sz = elf_dump_thread_status(siginfo->si_signo, ets);
		sz = elf_dump_thread_status(cprm->siginfo->si_signo, ets);
		info->thread_status_size += sz;
	}
	/* now collect the dump for the current */
	memset(info->prstatus, 0, sizeof(*info->prstatus));
	fill_prstatus(&info->prstatus->common, current, siginfo->si_signo);
	elf_core_copy_regs(&info->prstatus->pr_reg, regs);
	fill_prstatus(&info->prstatus->common, current, cprm->siginfo->si_signo);
	elf_core_copy_regs(&info->prstatus->pr_reg, cprm->regs);

	/* Set up header */
	fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS);
@@ -2094,18 +2094,18 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
	fill_note(info->notes + 1, "CORE", NT_PRPSINFO,
		  sizeof(*info->psinfo), info->psinfo);

	fill_siginfo_note(info->notes + 2, &info->csigdata, siginfo);
	fill_siginfo_note(info->notes + 2, &info->csigdata, cprm->siginfo);
	fill_auxv_note(info->notes + 3, current->mm);
	info->numnote = 4;

	if (fill_files_note(info->notes + info->numnote) == 0) {
	if (fill_files_note(info->notes + info->numnote, cprm) == 0) {
		info->notes_files = info->notes + info->numnote;
		info->numnote++;
	}

	/* Try to dump the FPU. */
	info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs,
							       info->fpu);
	info->prstatus->pr_fpvalid =
		elf_core_copy_task_fpregs(current, cprm->regs, info->fpu);
	if (info->prstatus->pr_fpvalid)
		fill_note(info->notes + info->numnote++,
			  "CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu);
@@ -2191,8 +2191,7 @@ static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
static int elf_core_dump(struct coredump_params *cprm)
{
	int has_dumped = 0;
	int vma_count, segs, i;
	size_t vma_data_size;
	int segs, i;
	struct elfhdr elf;
	loff_t offset = 0, dataoff;
	struct elf_note_info info = { };
@@ -2200,16 +2199,12 @@ static int elf_core_dump(struct coredump_params *cprm)
	struct elf_shdr *shdr4extnum = NULL;
	Elf_Half e_phnum;
	elf_addr_t e_shoff;
	struct core_vma_metadata *vma_meta;

	if (dump_vma_snapshot(cprm, &vma_count, &vma_meta, &vma_data_size))
		return 0;

	/*
	 * The number of segs are recored into ELF header as 16bit value.
	 * Please check DEFAULT_MAX_MAP_COUNT definition when you modify here.
	 */
	segs = vma_count + elf_core_extra_phdrs();
	segs = cprm->vma_count + elf_core_extra_phdrs();

	/* for notes section */
	segs++;
@@ -2223,7 +2218,7 @@ static int elf_core_dump(struct coredump_params *cprm)
	 * Collect all the non-memory information about the process for the
	 * notes.  This also sets up the file header.
	 */
	if (!fill_note_info(&elf, e_phnum, &info, cprm->siginfo, cprm->regs))
	if (!fill_note_info(&elf, e_phnum, &info, cprm))
		goto end_coredump;

	has_dumped = 1;
@@ -2248,7 +2243,7 @@ static int elf_core_dump(struct coredump_params *cprm)

	dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);

	offset += vma_data_size;
	offset += cprm->vma_data_size;
	offset += elf_core_extra_data_size();
	e_shoff = offset;

@@ -2268,8 +2263,8 @@ static int elf_core_dump(struct coredump_params *cprm)
		goto end_coredump;

	/* Write program headers for segments dump */
	for (i = 0; i < vma_count; i++) {
		struct core_vma_metadata *meta = vma_meta + i;
	for (i = 0; i < cprm->vma_count; i++) {
		struct core_vma_metadata *meta = cprm->vma_meta + i;
		struct elf_phdr phdr;

		phdr.p_type = PT_LOAD;
@@ -2306,8 +2301,8 @@ static int elf_core_dump(struct coredump_params *cprm)
	/* Align to page */
	dump_skip_to(cprm, dataoff);

	for (i = 0; i < vma_count; i++) {
		struct core_vma_metadata *meta = vma_meta + i;
	for (i = 0; i < cprm->vma_count; i++) {
		struct core_vma_metadata *meta = cprm->vma_meta + i;

		if (!dump_user_range(cprm, meta->start, meta->dump_size))
			goto end_coredump;
@@ -2324,7 +2319,6 @@ static int elf_core_dump(struct coredump_params *cprm)
end_coredump:
	free_note_info(&info);
	kfree(shdr4extnum);
	kvfree(vma_meta);
	kfree(phdr4note);
	return has_dumped;
}
+6 −12
Original line number Diff line number Diff line
@@ -1465,7 +1465,7 @@ static bool elf_fdpic_dump_segments(struct coredump_params *cprm,
static int elf_fdpic_core_dump(struct coredump_params *cprm)
{
	int has_dumped = 0;
	int vma_count, segs;
	int segs;
	int i;
	struct elfhdr *elf = NULL;
	loff_t offset = 0, dataoff;
@@ -1480,8 +1480,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
	elf_addr_t e_shoff;
	struct core_thread *ct;
	struct elf_thread_status *tmp;
	struct core_vma_metadata *vma_meta = NULL;
	size_t vma_data_size;

	/* alloc memory for large data structures: too large to be on stack */
	elf = kmalloc(sizeof(*elf), GFP_KERNEL);
@@ -1491,9 +1489,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
	if (!psinfo)
		goto end_coredump;

	if (dump_vma_snapshot(cprm, &vma_count, &vma_meta, &vma_data_size))
		goto end_coredump;

	for (ct = current->signal->core_state->dumper.next;
					ct; ct = ct->next) {
		tmp = elf_dump_thread_status(cprm->siginfo->si_signo,
@@ -1513,7 +1508,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
	tmp->next = thread_list;
	thread_list = tmp;

	segs = vma_count + elf_core_extra_phdrs();
	segs = cprm->vma_count + elf_core_extra_phdrs();

	/* for notes section */
	segs++;
@@ -1558,7 +1553,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
	/* Page-align dumped data */
	dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);

	offset += vma_data_size;
	offset += cprm->vma_data_size;
	offset += elf_core_extra_data_size();
	e_shoff = offset;

@@ -1578,8 +1573,8 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
		goto end_coredump;

	/* write program headers for segments dump */
	for (i = 0; i < vma_count; i++) {
		struct core_vma_metadata *meta = vma_meta + i;
	for (i = 0; i < cprm->vma_count; i++) {
		struct core_vma_metadata *meta = cprm->vma_meta + i;
		struct elf_phdr phdr;
		size_t sz;

@@ -1628,7 +1623,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)

	dump_skip_to(cprm, dataoff);

	if (!elf_fdpic_dump_segments(cprm, vma_meta, vma_count))
	if (!elf_fdpic_dump_segments(cprm, cprm->vma_meta, cprm->vma_count))
		goto end_coredump;

	if (!elf_core_write_extra_data(cprm))
@@ -1652,7 +1647,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
		thread_list = thread_list->next;
		kfree(tmp);
	}
	kvfree(vma_meta);
	kfree(phdr4note);
	kfree(elf);
	kfree(psinfo);
+5 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@
#include <linux/flat.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include <linux/coredump.h>

#include <asm/byteorder.h>
#include <asm/unaligned.h>
@@ -97,7 +98,9 @@ static int load_flat_shared_library(int id, struct lib_info *p);
#endif

static int load_flat_binary(struct linux_binprm *);
#ifdef CONFIG_COREDUMP
static int flat_core_dump(struct coredump_params *cprm);
#endif

static struct linux_binfmt flat_format = {
	.module		= THIS_MODULE,
@@ -114,12 +117,14 @@ static struct linux_binfmt flat_format = {
 * Currently only a stub-function.
 */

#ifdef CONFIG_COREDUMP
static int flat_core_dump(struct coredump_params *cprm)
{
	pr_warn("Process %s:%d received signr %d and should have core dumped\n",
		current->comm, current->pid, cprm->siginfo->si_signo);
	return 1;
}
#endif

/****************************************************************************/
/*
+39 −20
Original line number Diff line number Diff line
@@ -54,6 +54,9 @@

#include <trace/events/sched.h>

static bool dump_vma_snapshot(struct coredump_params *cprm);
static void free_vma_snapshot(struct coredump_params *cprm);

static int core_uses_pid;
static unsigned int core_pipe_limit;
static char core_pattern[CORENAME_MAX_SIZE] = "core";
@@ -532,6 +535,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
		 * by any locks.
		 */
		.mm_flags = mm->flags,
		.vma_meta = NULL,
	};

	audit_core_dumps(siginfo->si_signo);
@@ -746,6 +750,9 @@ void do_coredump(const kernel_siginfo_t *siginfo)
			pr_info("Core dump to |%s disabled\n", cn.corename);
			goto close_fail;
		}
		if (!dump_vma_snapshot(&cprm))
			goto close_fail;

		file_start_write(cprm.file);
		core_dumped = binfmt->core_dump(&cprm);
		/*
@@ -759,6 +766,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
			dump_emit(&cprm, "", 1);
		}
		file_end_write(cprm.file);
		free_vma_snapshot(&cprm);
	}
	if (ispipe && core_pipe_limit)
		wait_for_dump_helpers(cprm.file);
@@ -1092,18 +1100,29 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
	return gate_vma;
}

static void free_vma_snapshot(struct coredump_params *cprm)
{
	if (cprm->vma_meta) {
		int i;
		for (i = 0; i < cprm->vma_count; i++) {
			struct file *file = cprm->vma_meta[i].file;
			if (file)
				fput(file);
		}
		kvfree(cprm->vma_meta);
		cprm->vma_meta = NULL;
	}
}

/*
 * Under the mmap_lock, take a snapshot of relevant information about the task's
 * VMAs.
 */
int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count,
		      struct core_vma_metadata **vma_meta,
		      size_t *vma_data_size_ptr)
static bool dump_vma_snapshot(struct coredump_params *cprm)
{
	struct vm_area_struct *vma, *gate_vma;
	struct mm_struct *mm = current->mm;
	int i;
	size_t vma_data_size = 0;

	/*
	 * Once the stack expansion code is fixed to not change VMA bounds
@@ -1111,36 +1130,37 @@ int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count,
	 * mmap_lock in read mode.
	 */
	if (mmap_write_lock_killable(mm))
		return -EINTR;
		return false;

	cprm->vma_data_size = 0;
	gate_vma = get_gate_vma(mm);
	*vma_count = mm->map_count + (gate_vma ? 1 : 0);
	cprm->vma_count = mm->map_count + (gate_vma ? 1 : 0);

	*vma_meta = kvmalloc_array(*vma_count, sizeof(**vma_meta), GFP_KERNEL);
	if (!*vma_meta) {
	cprm->vma_meta = kvmalloc_array(cprm->vma_count, sizeof(*cprm->vma_meta), GFP_KERNEL);
	if (!cprm->vma_meta) {
		mmap_write_unlock(mm);
		return -ENOMEM;
		return false;
	}

	for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
			vma = next_vma(vma, gate_vma), i++) {
		struct core_vma_metadata *m = (*vma_meta) + i;
		struct core_vma_metadata *m = cprm->vma_meta + i;

		m->start = vma->vm_start;
		m->end = vma->vm_end;
		m->flags = vma->vm_flags;
		m->dump_size = vma_dump_size(vma, cprm->mm_flags);
		m->pgoff = vma->vm_pgoff;

		m->file = vma->vm_file;
		if (m->file)
			get_file(m->file);
	}

	mmap_write_unlock(mm);

	if (WARN_ON(i != *vma_count)) {
		kvfree(*vma_meta);
		return -EFAULT;
	}

	for (i = 0; i < *vma_count; i++) {
		struct core_vma_metadata *m = (*vma_meta) + i;
	for (i = 0; i < cprm->vma_count; i++) {
		struct core_vma_metadata *m = cprm->vma_meta + i;

		if (m->dump_size == DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER) {
			char elfmag[SELFMAG];
@@ -1153,9 +1173,8 @@ int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count,
			}
		}

		vma_data_size += m->dump_size;
		cprm->vma_data_size += m->dump_size;
	}

	*vma_data_size_ptr = vma_data_size;
	return 0;
	return true;
}
+1 −12
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#include <uapi/linux/binfmts.h>

struct filename;
struct coredump_params;

#define CORENAME_MAX_SIZE 128

@@ -77,18 +78,6 @@ struct linux_binprm {
#define BINPRM_FLAGS_PRESERVE_ARGV0_BIT 3
#define BINPRM_FLAGS_PRESERVE_ARGV0 (1 << BINPRM_FLAGS_PRESERVE_ARGV0_BIT)

/* Function parameter for binfmt->coredump */
struct coredump_params {
	const kernel_siginfo_t *siginfo;
	struct pt_regs *regs;
	struct file *file;
	unsigned long limit;
	unsigned long mm_flags;
	loff_t written;
	loff_t pos;
	loff_t to_skip;
};

/*
 * This structure defines the functions that are used to load the binary formats that
 * linux accepts.
Loading