diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ab41b6b147ae..7d6f2f791fe9 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -2166,7 +2166,8 @@ 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 segs, i; + int vma_count, segs, i; + size_t vma_data_size; struct elfhdr elf; loff_t offset = 0, dataoff; struct elf_note_info info = { }; @@ -2174,12 +2175,16 @@ 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 = cprm->vma_count + elf_core_extra_phdrs(); + segs = vma_count + elf_core_extra_phdrs(); /* for notes section */ segs++; @@ -2217,7 +2222,7 @@ static int elf_core_dump(struct coredump_params *cprm) dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); - offset += cprm->vma_data_size; + offset += vma_data_size; offset += elf_core_extra_data_size(); e_shoff = offset; @@ -2237,8 +2242,8 @@ static int elf_core_dump(struct coredump_params *cprm) goto end_coredump; /* Write program headers for segments dump */ - for (i = 0; i < cprm->vma_count; i++) { - struct core_vma_metadata *meta = cprm->vma_meta + i; + for (i = 0; i < vma_count; i++) { + struct core_vma_metadata *meta = vma_meta + i; struct elf_phdr phdr; phdr.p_type = PT_LOAD; @@ -2275,8 +2280,8 @@ static int elf_core_dump(struct coredump_params *cprm) if (!dump_skip(cprm, dataoff - cprm->pos)) goto end_coredump; - for (i = 0; i < cprm->vma_count; i++) { - struct core_vma_metadata *meta = cprm->vma_meta + i; + for (i = 0; i < vma_count; i++) { + struct core_vma_metadata *meta = vma_meta + i; if (!dump_user_range(cprm, meta->start, meta->dump_size)) goto end_coredump; @@ -2294,6 +2299,7 @@ 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; } diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c index 5764295a3f0f..be4062b8ba75 100644 --- a/fs/binfmt_elf_fdpic.c +++ b/fs/binfmt_elf_fdpic.c @@ -1479,7 +1479,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 segs; + int vma_count, segs; int i; struct elfhdr *elf = NULL; loff_t offset = 0, dataoff; @@ -1494,6 +1494,8 @@ 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); @@ -1503,6 +1505,9 @@ 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->mm->core_state->dumper.next; ct; ct = ct->next) { tmp = elf_dump_thread_status(cprm->siginfo->si_signo, @@ -1522,7 +1527,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) tmp->next = thread_list; thread_list = tmp; - segs = cprm->vma_count + elf_core_extra_phdrs(); + segs = vma_count + elf_core_extra_phdrs(); /* for notes section */ segs++; @@ -1567,7 +1572,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) /* Page-align dumped data */ dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); - offset += cprm->vma_data_size; + offset += vma_data_size; offset += elf_core_extra_data_size(); e_shoff = offset; @@ -1587,8 +1592,8 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) goto end_coredump; /* write program headers for segments dump */ - for (i = 0; i < cprm->vma_count; i++) { - struct core_vma_metadata *meta = cprm->vma_meta + i; + for (i = 0; i < vma_count; i++) { + struct core_vma_metadata *meta = vma_meta + i; struct elf_phdr phdr; size_t sz; @@ -1638,7 +1643,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) if (!dump_skip(cprm, dataoff - cprm->pos)) goto end_coredump; - if (!elf_fdpic_dump_segments(cprm, cprm->vma_meta, cprm->vma_count)) + if (!elf_fdpic_dump_segments(cprm, vma_meta, vma_count)) goto end_coredump; if (!elf_core_write_extra_data(cprm)) @@ -1662,6 +1667,7 @@ end_coredump: thread_list = thread_list->next; kfree(tmp); } + kvfree(vma_meta); kfree(phdr4note); kfree(elf); kfree(psinfo); diff --git a/fs/coredump.c b/fs/coredump.c index a605bd610867..b1b74dbfa22a 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -53,8 +53,6 @@ #include -static bool dump_vma_snapshot(struct coredump_params *cprm); - int core_uses_pid; unsigned int core_pipe_limit; char core_pattern[CORENAME_MAX_SIZE] = "core"; @@ -604,7 +602,6 @@ void do_coredump(const kernel_siginfo_t *siginfo) * by any locks. */ .mm_flags = mm->flags, - .vma_meta = NULL, }; audit_core_dumps(siginfo->si_signo); @@ -810,13 +807,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); file_end_write(cprm.file); - kvfree(cprm.vma_meta); } if (ispipe && core_pipe_limit) wait_for_dump_helpers(cprm.file); @@ -1092,11 +1085,14 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma, * Under the mmap_lock, take a snapshot of relevant information about the task's * VMAs. */ -static bool dump_vma_snapshot(struct coredump_params *cprm) +int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count, + struct core_vma_metadata **vma_meta, + size_t *vma_data_size_ptr) { 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 @@ -1104,21 +1100,20 @@ static bool dump_vma_snapshot(struct coredump_params *cprm) * mmap_lock in read mode. */ if (mmap_write_lock_killable(mm)) - return false; + return -EINTR; - cprm->vma_data_size = 0; gate_vma = get_gate_vma(mm); - cprm->vma_count = mm->map_count + (gate_vma ? 1 : 0); + *vma_count = mm->map_count + (gate_vma ? 1 : 0); - cprm->vma_meta = kvmalloc_array(cprm->vma_count, sizeof(*cprm->vma_meta), GFP_KERNEL); - if (!cprm->vma_meta) { + *vma_meta = kvmalloc_array(*vma_count, sizeof(**vma_meta), GFP_KERNEL); + if (!*vma_meta) { mmap_write_unlock(mm); - return false; + return -ENOMEM; } for (i = 0, vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma), i++) { - struct core_vma_metadata *m = cprm->vma_meta + i; + struct core_vma_metadata *m = (*vma_meta) + i; m->start = vma->vm_start; m->end = vma->vm_end; @@ -1128,14 +1123,13 @@ static bool dump_vma_snapshot(struct coredump_params *cprm) mmap_write_unlock(mm); - if (WARN_ON(i != cprm->vma_count)) { - kvfree(cprm->vma_meta); - return false; + if (WARN_ON(i != *vma_count)) { + kvfree(*vma_meta); + return -EFAULT; } - - for (i = 0; i < cprm->vma_count; i++) { - struct core_vma_metadata *m = cprm->vma_meta + i; + for (i = 0; i < *vma_count; i++) { + struct core_vma_metadata *m = (*vma_meta) + i; if (m->dump_size == DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER) { char elfmag[SELFMAG]; @@ -1148,8 +1142,9 @@ static bool dump_vma_snapshot(struct coredump_params *cprm) } } - cprm->vma_data_size += m->dump_size; + vma_data_size += m->dump_size; } - return true; + *vma_data_size_ptr = vma_data_size; + return 0; } diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h index 5a9786e6b554..0571701ab1c5 100644 --- a/include/linux/binfmts.h +++ b/include/linux/binfmts.h @@ -82,9 +82,6 @@ struct coredump_params { unsigned long mm_flags; loff_t written; loff_t pos; - int vma_count; - size_t vma_data_size; - struct core_vma_metadata *vma_meta; }; /* diff --git a/include/linux/coredump.h b/include/linux/coredump.h index 9c1fd6241307..e58e8c207782 100644 --- a/include/linux/coredump.h +++ b/include/linux/coredump.h @@ -24,6 +24,9 @@ extern int dump_align(struct coredump_params *cprm, int align); extern void dump_truncate(struct coredump_params *cprm); int dump_user_range(struct coredump_params *cprm, unsigned long start, unsigned long len); +int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count, + struct core_vma_metadata **vma_meta, + size_t *vma_data_size_ptr); #ifdef CONFIG_COREDUMP extern void do_coredump(const kernel_siginfo_t *siginfo); #else