Commit b7a801f3 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'execve-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull execve updates from Kees Cook:
 "Execve and binfmt updates.

  Eric and I have stepped up to be the active maintainers of this area,
  so here's our first collection. The bulk of the work was in coredump
  handling fixes; additional details are noted below:

   - Handle unusual AT_PHDR offsets (Akira Kawata)

   - Fix initial mapping size when PT_LOADs are not ordered (Alexey
     Dobriyan)

   - Move more code under CONFIG_COREDUMP (Alexey Dobriyan)

   - Fix missing mmap_lock in file_files_note (Eric W. Biederman)

   - Remove a.out support for alpha and m68k (Eric W. Biederman)

   - Include first pages of non-exec ELF libraries in coredump (Jann
     Horn)

   - Don't write past end of notes for regset gap in coredump (Rick
     Edgecombe)

   - Comment clean-ups (Tom Rix)

   - Force single empty string when argv is empty (Kees Cook)

   - Add NULL argv selftest (Kees Cook)

   - Properly redefine PT_GNU_* in terms of PT_LOOS (Kees Cook)

   - MAINTAINERS: Update execve entry with tree (Kees Cook)

   - Introduce initial KUnit testing for binfmt_elf (Kees Cook)"

* tag 'execve-v5.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  binfmt_elf: Don't write past end of notes for regset gap
  a.out: Stop building a.out/osf1 support on alpha and m68k
  coredump: Don't compile flat_core_dump when coredumps are disabled
  coredump: Use the vma snapshot in fill_files_note
  coredump/elf: Pass coredump_params into fill_note_info
  coredump: Remove the WARN_ON in dump_vma_snapshot
  coredump: Snapshot the vmas in do_coredump
  coredump: Move definition of struct coredump_params into coredump.h
  binfmt_elf: Introduce KUnit test
  ELF: Properly redefine PT_GNU_* in terms of PT_LOOS
  MAINTAINERS: Update execve entry with more details
  exec: cleanup comments
  fs/binfmt_elf: Refactor load_elf_binary function
  fs/binfmt_elf: Fix AT_PHDR for unusual ELF files
  binfmt: move more stuff undef CONFIG_COREDUMP
  selftests/exec: Test for empty string on NULL argv
  exec: Force single empty string when argv is empty
  coredump: Also dump first pages of non-executable ELF libraries
  ELF: fix overflow in total mapping size calculation
parents ad9c6ee6 dd664099
...@@ -7234,6 +7234,9 @@ F: net/core/of_net.c ...@@ -7234,6 +7234,9 @@ F: net/core/of_net.c
EXEC & BINFMT API EXEC & BINFMT API
R: Eric Biederman <ebiederm@xmission.com> R: Eric Biederman <ebiederm@xmission.com>
R: Kees Cook <keescook@chromium.org> R: Kees Cook <keescook@chromium.org>
L: linux-mm@kvack.org
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/execve
F: arch/alpha/kernel/binfmt_loader.c F: arch/alpha/kernel/binfmt_loader.c
F: arch/x86/ia32/ia32_aout.c F: arch/x86/ia32/ia32_aout.c
F: fs/*binfmt_*.c F: fs/*binfmt_*.c
...@@ -7241,6 +7244,7 @@ F: fs/exec.c ...@@ -7241,6 +7244,7 @@ F: fs/exec.c
F: include/linux/binfmts.h F: include/linux/binfmts.h
F: include/linux/elf.h F: include/linux/elf.h
F: include/uapi/linux/binfmts.h F: include/uapi/linux/binfmts.h
F: include/uapi/linux/elf.h
F: tools/testing/selftests/exec/ F: tools/testing/selftests/exec/
N: asm/elf.h N: asm/elf.h
N: binfmt N: binfmt
......
...@@ -12,7 +12,6 @@ config ALPHA ...@@ -12,7 +12,6 @@ config ALPHA
select FORCE_PCI if !ALPHA_JENSEN select FORCE_PCI if !ALPHA_JENSEN
select PCI_DOMAINS if PCI select PCI_DOMAINS if PCI
select PCI_SYSCALL if PCI select PCI_SYSCALL if PCI
select HAVE_AOUT
select HAVE_ASM_MODVERSIONS select HAVE_ASM_MODVERSIONS
select HAVE_PCSPKR_PLATFORM select HAVE_PCSPKR_PLATFORM
select HAVE_PERF_EVENTS select HAVE_PERF_EVENTS
......
...@@ -17,7 +17,6 @@ config M68K ...@@ -17,7 +17,6 @@ config M68K
select GENERIC_CPU_DEVICES select GENERIC_CPU_DEVICES
select GENERIC_IOMAP select GENERIC_IOMAP
select GENERIC_IRQ_SHOW select GENERIC_IRQ_SHOW
select HAVE_AOUT if MMU
select HAVE_ASM_MODVERSIONS select HAVE_ASM_MODVERSIONS
select HAVE_DEBUG_BUGVERBOSE select HAVE_DEBUG_BUGVERBOSE
select HAVE_EFFICIENT_UNALIGNED_ACCESS if !CPU_HAS_NO_UNALIGNED select HAVE_EFFICIENT_UNALIGNED_ACCESS if !CPU_HAS_NO_UNALIGNED
......
...@@ -28,6 +28,16 @@ config BINFMT_ELF ...@@ -28,6 +28,16 @@ config BINFMT_ELF
ld.so (check the file <file:Documentation/Changes> for location and ld.so (check the file <file:Documentation/Changes> for location and
latest version). latest version).
config BINFMT_ELF_KUNIT_TEST
bool "Build KUnit tests for ELF binary support" if !KUNIT_ALL_TESTS
depends on KUNIT=y && BINFMT_ELF=y
default KUNIT_ALL_TESTS
help
This builds the ELF loader KUnit tests, which try to gather
prior bug fixes into a regression test collection. This is really
only needed for debugging. Note that with CONFIG_COMPAT=y, the
compat_binfmt_elf KUnit test is also created.
config COMPAT_BINFMT_ELF config COMPAT_BINFMT_ELF
def_bool y def_bool y
depends on COMPAT && BINFMT_ELF depends on COMPAT && BINFMT_ELF
......
This diff is collapsed.
...@@ -83,8 +83,8 @@ static struct linux_binfmt elf_fdpic_format = { ...@@ -83,8 +83,8 @@ static struct linux_binfmt elf_fdpic_format = {
.load_binary = load_elf_fdpic_binary, .load_binary = load_elf_fdpic_binary,
#ifdef CONFIG_ELF_CORE #ifdef CONFIG_ELF_CORE
.core_dump = elf_fdpic_core_dump, .core_dump = elf_fdpic_core_dump,
#endif
.min_coredump = ELF_EXEC_PAGESIZE, .min_coredump = ELF_EXEC_PAGESIZE,
#endif
}; };
static int __init init_elf_fdpic_binfmt(void) static int __init init_elf_fdpic_binfmt(void)
...@@ -1465,7 +1465,7 @@ static bool elf_fdpic_dump_segments(struct coredump_params *cprm, ...@@ -1465,7 +1465,7 @@ static bool elf_fdpic_dump_segments(struct coredump_params *cprm,
static int elf_fdpic_core_dump(struct coredump_params *cprm) static int elf_fdpic_core_dump(struct coredump_params *cprm)
{ {
int has_dumped = 0; int has_dumped = 0;
int vma_count, segs; int segs;
int i; int i;
struct elfhdr *elf = NULL; struct elfhdr *elf = NULL;
loff_t offset = 0, dataoff; loff_t offset = 0, dataoff;
...@@ -1480,8 +1480,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1480,8 +1480,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
elf_addr_t e_shoff; elf_addr_t e_shoff;
struct core_thread *ct; struct core_thread *ct;
struct elf_thread_status *tmp; 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 */ /* alloc memory for large data structures: too large to be on stack */
elf = kmalloc(sizeof(*elf), GFP_KERNEL); elf = kmalloc(sizeof(*elf), GFP_KERNEL);
...@@ -1491,9 +1489,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1491,9 +1489,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
if (!psinfo) if (!psinfo)
goto end_coredump; 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; for (ct = current->signal->core_state->dumper.next;
ct; ct = ct->next) { ct; ct = ct->next) {
tmp = elf_dump_thread_status(cprm->siginfo->si_signo, tmp = elf_dump_thread_status(cprm->siginfo->si_signo,
...@@ -1513,7 +1508,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1513,7 +1508,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
tmp->next = thread_list; tmp->next = thread_list;
thread_list = tmp; thread_list = tmp;
segs = vma_count + elf_core_extra_phdrs(); segs = cprm->vma_count + elf_core_extra_phdrs();
/* for notes section */ /* for notes section */
segs++; segs++;
...@@ -1558,7 +1553,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1558,7 +1553,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
/* Page-align dumped data */ /* Page-align dumped data */
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE); dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
offset += vma_data_size; offset += cprm->vma_data_size;
offset += elf_core_extra_data_size(); offset += elf_core_extra_data_size();
e_shoff = offset; e_shoff = offset;
...@@ -1578,8 +1573,8 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1578,8 +1573,8 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
goto end_coredump; goto end_coredump;
/* write program headers for segments dump */ /* write program headers for segments dump */
for (i = 0; i < vma_count; i++) { for (i = 0; i < cprm->vma_count; i++) {
struct core_vma_metadata *meta = vma_meta + i; struct core_vma_metadata *meta = cprm->vma_meta + i;
struct elf_phdr phdr; struct elf_phdr phdr;
size_t sz; size_t sz;
...@@ -1628,7 +1623,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1628,7 +1623,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
dump_skip_to(cprm, dataoff); 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; goto end_coredump;
if (!elf_core_write_extra_data(cprm)) if (!elf_core_write_extra_data(cprm))
...@@ -1652,7 +1647,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1652,7 +1647,6 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
thread_list = thread_list->next; thread_list = thread_list->next;
kfree(tmp); kfree(tmp);
} }
kvfree(vma_meta);
kfree(phdr4note); kfree(phdr4note);
kfree(elf); kfree(elf);
kfree(psinfo); kfree(psinfo);
......
// SPDX-License-Identifier: GPL-2.0-only
#include <kunit/test.h>
static void total_mapping_size_test(struct kunit *test)
{
struct elf_phdr empty[] = {
{ .p_type = PT_LOAD, .p_vaddr = 0, .p_memsz = 0, },
{ .p_type = PT_INTERP, .p_vaddr = 10, .p_memsz = 999999, },
};
/*
* readelf -lW /bin/mount | grep '^ .*0x0' | awk '{print "\t\t{ .p_type = PT_" \
* $1 ", .p_vaddr = " $3 ", .p_memsz = " $6 ", },"}'
*/
struct elf_phdr mount[] = {
{ .p_type = PT_PHDR, .p_vaddr = 0x00000040, .p_memsz = 0x0002d8, },
{ .p_type = PT_INTERP, .p_vaddr = 0x00000318, .p_memsz = 0x00001c, },
{ .p_type = PT_LOAD, .p_vaddr = 0x00000000, .p_memsz = 0x0033a8, },
{ .p_type = PT_LOAD, .p_vaddr = 0x00004000, .p_memsz = 0x005c91, },
{ .p_type = PT_LOAD, .p_vaddr = 0x0000a000, .p_memsz = 0x0022f8, },
{ .p_type = PT_LOAD, .p_vaddr = 0x0000d330, .p_memsz = 0x000d40, },
{ .p_type = PT_DYNAMIC, .p_vaddr = 0x0000d928, .p_memsz = 0x000200, },
{ .p_type = PT_NOTE, .p_vaddr = 0x00000338, .p_memsz = 0x000030, },
{ .p_type = PT_NOTE, .p_vaddr = 0x00000368, .p_memsz = 0x000044, },
{ .p_type = PT_GNU_PROPERTY, .p_vaddr = 0x00000338, .p_memsz = 0x000030, },
{ .p_type = PT_GNU_EH_FRAME, .p_vaddr = 0x0000b490, .p_memsz = 0x0001ec, },
{ .p_type = PT_GNU_STACK, .p_vaddr = 0x00000000, .p_memsz = 0x000000, },
{ .p_type = PT_GNU_RELRO, .p_vaddr = 0x0000d330, .p_memsz = 0x000cd0, },
};
size_t mount_size = 0xE070;
/* https://lore.kernel.org/linux-fsdevel/YfF18Dy85mCntXrx@fractal.localdomain */
struct elf_phdr unordered[] = {
{ .p_type = PT_LOAD, .p_vaddr = 0x00000000, .p_memsz = 0x0033a8, },
{ .p_type = PT_LOAD, .p_vaddr = 0x0000d330, .p_memsz = 0x000d40, },
{ .p_type = PT_LOAD, .p_vaddr = 0x00004000, .p_memsz = 0x005c91, },
{ .p_type = PT_LOAD, .p_vaddr = 0x0000a000, .p_memsz = 0x0022f8, },
};
/* No headers, no size. */
KUNIT_EXPECT_EQ(test, total_mapping_size(NULL, 0), 0);
KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 0), 0);
/* Empty headers, no size. */
KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 1), 0);
/* No PT_LOAD headers, no size. */
KUNIT_EXPECT_EQ(test, total_mapping_size(&empty[1], 1), 0);
/* Empty PT_LOAD and non-PT_LOAD headers, no size. */
KUNIT_EXPECT_EQ(test, total_mapping_size(empty, 2), 0);
/* Normal set of PT_LOADS, and expected size. */
KUNIT_EXPECT_EQ(test, total_mapping_size(mount, ARRAY_SIZE(mount)), mount_size);
/* Unordered PT_LOADs result in same size. */
KUNIT_EXPECT_EQ(test, total_mapping_size(unordered, ARRAY_SIZE(unordered)), mount_size);
}
static struct kunit_case binfmt_elf_test_cases[] = {
KUNIT_CASE(total_mapping_size_test),
{},
};
static struct kunit_suite binfmt_elf_test_suite = {
.name = KBUILD_MODNAME,
.test_cases = binfmt_elf_test_cases,
};
kunit_test_suite(binfmt_elf_test_suite);
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <linux/flat.h> #include <linux/flat.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/vmalloc.h> #include <linux/vmalloc.h>
#include <linux/coredump.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include <asm/unaligned.h> #include <asm/unaligned.h>
...@@ -97,13 +98,17 @@ static int load_flat_shared_library(int id, struct lib_info *p); ...@@ -97,13 +98,17 @@ static int load_flat_shared_library(int id, struct lib_info *p);
#endif #endif
static int load_flat_binary(struct linux_binprm *); static int load_flat_binary(struct linux_binprm *);
#ifdef CONFIG_COREDUMP
static int flat_core_dump(struct coredump_params *cprm); static int flat_core_dump(struct coredump_params *cprm);
#endif
static struct linux_binfmt flat_format = { static struct linux_binfmt flat_format = {
.module = THIS_MODULE, .module = THIS_MODULE,
.load_binary = load_flat_binary, .load_binary = load_flat_binary,
#ifdef CONFIG_COREDUMP
.core_dump = flat_core_dump, .core_dump = flat_core_dump,
.min_coredump = PAGE_SIZE .min_coredump = PAGE_SIZE
#endif
}; };
/****************************************************************************/ /****************************************************************************/
...@@ -112,12 +117,14 @@ static struct linux_binfmt flat_format = { ...@@ -112,12 +117,14 @@ static struct linux_binfmt flat_format = {
* Currently only a stub-function. * Currently only a stub-function.
*/ */
#ifdef CONFIG_COREDUMP
static int flat_core_dump(struct coredump_params *cprm) static int flat_core_dump(struct coredump_params *cprm)
{ {
pr_warn("Process %s:%d received signr %d and should have core dumped\n", pr_warn("Process %s:%d received signr %d and should have core dumped\n",
current->comm, current->pid, cprm->siginfo->si_signo); current->comm, current->pid, cprm->siginfo->si_signo);
return 1; return 1;
} }
#endif
/****************************************************************************/ /****************************************************************************/
/* /*
......
...@@ -135,6 +135,8 @@ ...@@ -135,6 +135,8 @@
#define elf_format compat_elf_format #define elf_format compat_elf_format
#define init_elf_binfmt init_compat_elf_binfmt #define init_elf_binfmt init_compat_elf_binfmt
#define exit_elf_binfmt exit_compat_elf_binfmt #define exit_elf_binfmt exit_compat_elf_binfmt
#define binfmt_elf_test_cases compat_binfmt_elf_test_cases
#define binfmt_elf_test_suite compat_binfmt_elf_test_suite
/* /*
* We share all the actual code with the native (64-bit) version. * We share all the actual code with the native (64-bit) version.
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include <linux/path.h> #include <linux/path.h>
#include <linux/timekeeping.h> #include <linux/timekeeping.h>
#include <linux/sysctl.h> #include <linux/sysctl.h>
#include <linux/elf.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
...@@ -53,6 +54,9 @@ ...@@ -53,6 +54,9 @@
#include <trace/events/sched.h> #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 int core_uses_pid;
static unsigned int core_pipe_limit; static unsigned int core_pipe_limit;
static char core_pattern[CORENAME_MAX_SIZE] = "core"; static char core_pattern[CORENAME_MAX_SIZE] = "core";
...@@ -531,6 +535,7 @@ void do_coredump(const kernel_siginfo_t *siginfo) ...@@ -531,6 +535,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
* by any locks. * by any locks.
*/ */
.mm_flags = mm->flags, .mm_flags = mm->flags,
.vma_meta = NULL,
}; };
audit_core_dumps(siginfo->si_signo); audit_core_dumps(siginfo->si_signo);
...@@ -745,6 +750,9 @@ void do_coredump(const kernel_siginfo_t *siginfo) ...@@ -745,6 +750,9 @@ void do_coredump(const kernel_siginfo_t *siginfo)
pr_info("Core dump to |%s disabled\n", cn.corename); pr_info("Core dump to |%s disabled\n", cn.corename);
goto close_fail; goto close_fail;
} }
if (!dump_vma_snapshot(&cprm))
goto close_fail;
file_start_write(cprm.file); file_start_write(cprm.file);
core_dumped = binfmt->core_dump(&cprm); core_dumped = binfmt->core_dump(&cprm);
/* /*
...@@ -758,6 +766,7 @@ void do_coredump(const kernel_siginfo_t *siginfo) ...@@ -758,6 +766,7 @@ void do_coredump(const kernel_siginfo_t *siginfo)
dump_emit(&cprm, "", 1); dump_emit(&cprm, "", 1);
} }
file_end_write(cprm.file); file_end_write(cprm.file);
free_vma_snapshot(&cprm);
} }
if (ispipe && core_pipe_limit) if (ispipe && core_pipe_limit)
wait_for_dump_helpers(cprm.file); wait_for_dump_helpers(cprm.file);
...@@ -980,6 +989,8 @@ static bool always_dump_vma(struct vm_area_struct *vma) ...@@ -980,6 +989,8 @@ static bool always_dump_vma(struct vm_area_struct *vma)
return false; return false;
} }
#define DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER 1
/* /*
* Decide how much of @vma's contents should be included in a core dump. * Decide how much of @vma's contents should be included in a core dump.
*/ */
...@@ -1039,9 +1050,20 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma, ...@@ -1039,9 +1050,20 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
* dump the first page to aid in determining what was mapped here. * dump the first page to aid in determining what was mapped here.
*/ */
if (FILTER(ELF_HEADERS) && if (FILTER(ELF_HEADERS) &&
vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ) && vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) {
(READ_ONCE(file_inode(vma->vm_file)->i_mode) & 0111) != 0) if ((READ_ONCE(file_inode(vma->vm_file)->i_mode) & 0111) != 0)
return PAGE_SIZE; return PAGE_SIZE;
/*
* ELF libraries aren't always executable.
* We'll want to check whether the mapping starts with the ELF
* magic, but not now - we're holding the mmap lock,
* so copy_from_user() doesn't work here.
* Use a placeholder instead, and fix it up later in
* dump_vma_snapshot().
*/
return DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER;
}
#undef FILTER #undef FILTER
...@@ -1078,18 +1100,29 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma, ...@@ -1078,18 +1100,29 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
return gate_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 * Under the mmap_lock, take a snapshot of relevant information about the task's
* VMAs. * VMAs.
*/ */
int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count, static bool dump_vma_snapshot(struct coredump_params *cprm)
struct core_vma_metadata **vma_meta,
size_t *vma_data_size_ptr)
{ {
struct vm_area_struct *vma, *gate_vma; struct vm_area_struct *vma, *gate_vma;
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
int i; int i;
size_t vma_data_size = 0;
/* /*
* Once the stack expansion code is fixed to not change VMA bounds * Once the stack expansion code is fixed to not change VMA bounds
...@@ -1097,36 +1130,51 @@ int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count, ...@@ -1097,36 +1130,51 @@ int dump_vma_snapshot(struct coredump_params *cprm, int *vma_count,
* mmap_lock in read mode. * mmap_lock in read mode.
*/ */
if (mmap_write_lock_killable(mm)) if (mmap_write_lock_killable(mm))
return -EINTR; return false;
cprm->vma_data_size = 0;
gate_vma = get_gate_vma(mm); 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); cprm->vma_meta = kvmalloc_array(cprm->vma_count, sizeof(*cprm->vma_meta), GFP_KERNEL);
if (!*vma_meta) { if (!cprm->vma_meta) {
mmap_write_unlock(mm); mmap_write_unlock(mm);
return -ENOMEM; return false;
} }
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL; for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma), i++) { 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->start = vma->vm_start;
m->end = vma->vm_end; m->end = vma->vm_end;
m->flags = vma->vm_flags; m->flags = vma->vm_flags;
m->dump_size = vma_dump_size(vma, cprm->mm_flags); m->dump_size = vma_dump_size(vma, cprm->mm_flags);
m->pgoff = vma->vm_pgoff;
vma_data_size += m->dump_size; m->file = vma->vm_file;
if (m->file)
get_file(m->file);
} }
mmap_write_unlock(mm); mmap_write_unlock(mm);
if (WARN_ON(i != *vma_count)) { for (i = 0; i < cprm->vma_count; i++) {
kvfree(*vma_meta); struct core_vma_metadata *m = cprm->vma_meta + i;
return -EFAULT;
if (m->dump_size == DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER) {
char elfmag[SELFMAG];
if (copy_from_user(elfmag, (void __user *)m->start, SELFMAG) ||
memcmp(elfmag, ELFMAG, SELFMAG) != 0) {
m->dump_size = 0;
} else {
m->dump_size = PAGE_SIZE;
}
}
cprm->vma_data_size += m->dump_size;
} }
*vma_data_size_ptr = vma_data_size; return true;
return 0;
} }
...@@ -118,7 +118,7 @@ bool path_noexec(const struct path *path) ...@@ -118,7 +118,7 @@ bool path_noexec(const struct path *path)
* Note that a shared library must be both readable and executable due to * Note that a shared library must be both readable and executable due to
* security reasons. * security reasons.
* *
* Also note that we take the address to load from from the file itself. * Also note that we take the address to load from the file itself.
*/ */
SYSCALL_DEFINE1(uselib, const char __user *, library) SYSCALL_DEFINE1(uselib, const char __user *, library)
{ {
...@@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm) ...@@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
* the stack. They aren't stored until much later when we can't * the stack. They aren't stored until much later when we can't
* signal to the parent that the child has run out of stack space. * signal to the parent that the child has run out of stack space.
* Instead, calculate it here so it's possible to fail gracefully. * Instead, calculate it here so it's possible to fail gracefully.
*
* In the case of argc = 0, make sure there is space for adding a
* empty string (which will bump argc to 1), to ensure confused
* userspace programs don't start processing from argv[1], thinking
* argc can never be 0, to keep them from walking envp by accident.
* See do_execveat_common().
*/ */
ptr_size = (bprm->argc + bprm->envc) * sizeof(void *); ptr_size = (max(bprm->argc, 1) + bprm->envc) * sizeof(void *);
if (limit <= ptr_size) if (limit <= ptr_size)
return -E2BIG; return -E2BIG;
limit -= ptr_size; limit -= ptr_size;
...@@ -536,7 +542,7 @@ static int copy_strings(int argc, struct user_arg_ptr argv, ...@@ -536,7 +542,7 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
if (!valid_arg_len(bprm, len)) if (!valid_arg_len(bprm, len))
goto out; goto out;
/* We're going to work our way backwords. */ /* We're going to work our way backwards. */
pos = bprm->p; pos = bprm->p;
str += len; str += len;
bprm->p -= len; bprm->p -= len;
...@@ -1269,7 +1275,7 @@ int begin_new_exec(struct linux_binprm * bprm) ...@@ -1269,7 +1275,7 @@ int begin_new_exec(struct linux_binprm * bprm)
/* /*
* Must be called _before_ exec_mmap() as bprm->mm is * Must be called _before_ exec_mmap() as bprm->mm is
* not visibile until then. This also enables the update * not visible until then. This also enables the update
* to be lockless. * to be lockless.
*/ */
retval = set_mm_exe_file(bprm->mm, bprm->file); retval = set_mm_exe_file(bprm->mm, bprm->file);
...@@ -1897,6 +1903,9 @@ static int do_execveat_common(int fd, struct filename *filename, ...@@ -1897,6 +1903,9 @@ static int do_execveat_common(int fd, struct filename *filename,
} }
retval = count(argv, MAX_ARG_STRINGS); retval = count(argv, MAX_ARG_STRINGS);
if (retval == 0)
pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
current->comm, bprm->filename);
if (retval < 0) if (retval < 0)
goto out_free; goto out_free;
bprm->argc = retval; bprm->argc = retval;
...@@ -1923,6 +1932,19 @@ static int do_execveat_common(int fd, struct filename *filename, ...@@ -1923,6 +1932,19 @@ static int do_execveat_common(int fd, struct filename *filename,
if (retval < 0) if (retval < 0)
goto out_free; goto out_free;
/*
* When argv is empty, add an empty string ("") as argv[0] to
* ensure confused userspace programs that start processing
* from argv[1] won't end up walking envp. See also
* bprm_stack_limits().
*/
if (bprm->argc == 0) {
retval = copy_string_kernel("", bprm);
if (retval < 0)
goto out_free;
bprm->argc = 1;
}
retval = bprm_execve(bprm, fd, filename, flags); retval = bprm_execve(bprm, fd, filename, flags);
out_free: out_free:
free_bprm(bprm); free_bprm(bprm);
...@@ -1951,6 +1973,8 @@ int kernel_execve(const char *kernel_filename, ...@@ -1951,6 +1973,8 @@ int kernel_execve(const char *kernel_filename,
} }
retval = count_strings_kernel(argv); retval = count_strings_kernel(argv);
if (WARN_ON_ONCE(retval == 0))
retval = -EINVAL;
if (retval < 0) if (retval < 0)
goto out_free; goto out_free;
bprm->argc = retval; bprm->argc = retval;
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <uapi/linux/binfmts.h> #include <uapi/linux/binfmts.h>
struct filename; struct filename;
struct coredump_params;
#define CORENAME_MAX_SIZE 128 #define CORENAME_MAX_SIZE 128
...@@ -77,18 +78,6 @@ struct linux_binprm { ...@@ -77,18 +78,6 @@ struct linux_binprm {
#define BINPRM_FLAGS_PRESERVE_ARGV0_BIT 3 #define BINPRM_FLAGS_PRESERVE_ARGV0_BIT 3
#define BINPRM_FLAGS_PRESERVE_ARGV0 (1 << BINPRM_FLAGS_PRESERVE_ARGV0_BIT) #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 * This structure defines the functions that are used to load the binary formats that
* linux accepts. * linux accepts.
...@@ -98,8 +87,10 @@ struct linux_binfmt { ...@@ -98,8 +87,10 @@ struct linux_binfmt {
struct module *module; struct module *module;
int (*load_binary)(struct linux_binprm *); int (*load_binary)(struct linux_binprm *);
int (*load_shlib)(struct file *); int (*load_shlib)(struct file *);
#ifdef CONFIG_COREDUMP
int (*core_dump)(struct coredump_params *cprm); int (*core_dump)(struct coredump_params *cprm);
unsigned long min_coredump; /* minimal dump size */ unsigned long min_coredump; /* minimal dump size */
#endif
} __randomize_layout; } __randomize_layout;
extern void __register_binfmt(struct linux_binfmt *fmt, int insert); extern void __register_binfmt(struct linux_binfmt *fmt, int insert);
......
...@@ -12,22 +12,34 @@ struct core_vma_metadata { ...@@ -12,22 +12,34 @@ struct core_vma_metadata {
unsigned long start, end; unsigned long start, end;
unsigned long flags; unsigned long flags;
unsigned long dump_size; unsigned long dump_size;
unsigned long pgoff;
struct file *file;
};
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;
int vma_count;
size_t vma_data_size;
struct core_vma_metadata *vma_meta;
}; };
/* /*
* These are the only things you should do on a core-file: use only these * These are the only things you should do on a core-file: use only these
* functions to write out all the necessary info. * functions to write out all the necessary info.
*/ */
struct coredump_params;
extern void dump_skip_to(struct coredump_params *cprm, unsigned long to); extern void dump_skip_to(struct coredump_params *cprm, unsigned long to);
extern void dump_skip(struct coredump_params *cprm, size_t nr); extern void dump_skip(struct coredump_params *cprm, size_t nr);
extern int dump_emit(struct coredump_params *cprm, const void *addr, int nr); extern int dump_emit(struct coredump_params *cprm, const void *addr, int nr);
extern int dump_align(struct coredump_params *cprm, int align); extern int dump_align(struct coredump_params *cprm, int align);
int dump_user_range(struct coredump_params *cprm, unsigned long start, int dump_user_range(struct coredump_params *cprm, unsigned long start,
unsigned long len); 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);
extern void do_coredump(const kernel_siginfo_t *siginfo); extern void do_coredump(const kernel_siginfo_t *siginfo);
#else #else
static inline void do_coredump(const kernel_siginfo_t *siginfo) {} static inline void do_coredump(const kernel_siginfo_t *siginfo) {}
......
...@@ -35,10 +35,11 @@ typedef __s64 Elf64_Sxword; ...@@ -35,10 +35,11 @@ typedef __s64 Elf64_Sxword;
#define PT_HIOS 0x6fffffff /* OS-specific */ #define PT_HIOS 0x6fffffff /* OS-specific */
#define PT_LOPROC 0x70000000 #define PT_LOPROC 0x70000000
#define PT_HIPROC 0x7fffffff #define PT_HIPROC 0x7fffffff
#define PT_GNU_EH_FRAME 0x6474e550 #define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
#define PT_GNU_PROPERTY 0x6474e553
#define PT_GNU_STACK (PT_LOOS + 0x474e551) #define PT_GNU_STACK (PT_LOOS + 0x474e551)
#define PT_GNU_RELRO (PT_LOOS + 0x474e552)
#define PT_GNU_PROPERTY (PT_LOOS + 0x474e553)
/* ARM MTE memory tag segment type */ /* ARM MTE memory tag segment type */
#define PT_ARM_MEMTAG_MTE (PT_LOPROC + 0x1) #define PT_ARM_MEMTAG_MTE (PT_LOPROC + 0x1)
......
...@@ -10,6 +10,7 @@ TEST_GEN_FILES := execveat.symlink execveat.denatured script subdir ...@@ -10,6 +10,7 @@ TEST_GEN_FILES := execveat.symlink execveat.denatured script subdir
TEST_FILES := Makefile TEST_FILES := Makefile
TEST_GEN_PROGS += recursion-depth TEST_GEN_PROGS += recursion-depth
TEST_GEN_PROGS += null-argv
EXTRA_CLEAN := $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)/xxxxx* \ EXTRA_CLEAN := $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)/xxxxx* \
$(OUTPUT)/S_I*.test $(OUTPUT)/S_I*.test
......
// SPDX-License-Identifier: GPL-2.0-only
/* Test that empty argvs are swapped out for a single empty string. */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "../kselftest.h"
#define FORK(exec) \
do { \
pid = fork(); \
if (pid == 0) { \
/* Child */ \
exec; /* Some kind of exec */ \
perror("# " #exec); \
return 1; \
} \
check_result(pid, #exec); \
} while (0)
void check_result(pid_t pid, const char *msg)
{
int wstatus;
if (pid == (pid_t)-1) {
perror("# fork");
ksft_test_result_fail("fork failed: %s\n", msg);
return;
}
if (waitpid(pid, &wstatus, 0) < 0) {
perror("# waitpid");
ksft_test_result_fail("waitpid failed: %s\n", msg);
return;
}
if (!WIFEXITED(wstatus)) {
ksft_test_result_fail("child did not exit: %s\n", msg);
return;
}
if (WEXITSTATUS(wstatus) != 0) {
ksft_test_result_fail("non-zero exit: %s\n", msg);
return;
}
ksft_test_result_pass("%s\n", msg);
}
int main(int argc, char *argv[], char *envp[])
{
pid_t pid;
static char * const args[] = { NULL };
static char * const str[] = { "", NULL };
/* argc counting checks */
if (argc < 1) {
fprintf(stderr, "# FAIL: saw argc == 0 (old kernel?)\n");
return 1;
}
if (argc != 1) {
fprintf(stderr, "# FAIL: unknown argc (%d)\n", argc);
return 1;
}
if (argv[0][0] == '\0') {
/* Good, we found a NULL terminated string at argv[0]! */
return 0;
}
/* Test runner. */
ksft_print_header();
ksft_set_plan(5);
FORK(execve(argv[0], str, NULL));
FORK(execve(argv[0], NULL, NULL));
FORK(execve(argv[0], NULL, envp));
FORK(execve(argv[0], args, NULL));
FORK(execve(argv[0], args, envp));
ksft_exit(ksft_cnt.ksft_pass == ksft_plan);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment