mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-05 03:44:03 +08:00
execve updates for v5.18-rc1
- 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) -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmI4ji4WHGtlZXNjb29r QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJi7VD/9o+PndYkeGclL7sYfouhSzK21W go4SGCrTl0oK/mfz3qXVYeS4VFjNTCTEs8rSZdjHN8a9VAVSJ38z6FPwbSQobzEP zXPuvwxe4GM4jb8FsBTcTEl1Wfw6kUV9JHXqFje6MuiZMXa8YDD+UMl95CgmGi1L 5sOw4quHXkG8nlC0v1PI9XSpmzK2nHmXBWVddnPXTUmEfitvoIJdf0iTJ4/4mYM/ OwrCiufGHvGtQFUrYTxgiZ3nvFdAkZDt+P8GA8NJOBCMDTPvsk57uTok1sW6CRFT lSymgoc3SczBtHYO6nFl5U04XGsNY+iHYhjhNL10IoucdCvS2VS0vEb8ZXKg6wtQ /tbgf1Mcfu7eoClA0ZjQX/pQbkPYL/s++Lwkc7pzknbmdwq+1yZF1+4Y1XItR4jJ kUhVsewQuU0os7BnaREkFOcwqXfA4hixb9w79p+SjMX8/XrnSkLJ3cFswkGTUxdO DOwhVcmqsZdVXMMk0R3oOtm9ABSp/FqvT8At2kZI0W93jhZGHWzOrU+psnkTUcDt KpFEJzdoh4ImZvBK8F5f07dAlqeVEZvVDhBt+x1Wxcu90p7rmZJT8OV2mJCDVhZG E2PW7UuLOAbgRM+E+gxz7SkpIMtOSFlxT2xGuygcRbIxOOeVnj1x9NwGdI9xcgpF s021x7TcHbpvYakRsg== =SyEY -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
b7a801f395
@ -7234,6 +7234,9 @@ F: net/core/of_net.c
|
||||
EXEC & BINFMT API
|
||||
R: Eric Biederman <ebiederm@xmission.com>
|
||||
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/x86/ia32/ia32_aout.c
|
||||
F: fs/*binfmt_*.c
|
||||
@ -7241,6 +7244,7 @@ F: fs/exec.c
|
||||
F: include/linux/binfmts.h
|
||||
F: include/linux/elf.h
|
||||
F: include/uapi/linux/binfmts.h
|
||||
F: include/uapi/linux/elf.h
|
||||
F: tools/testing/selftests/exec/
|
||||
N: asm/elf.h
|
||||
N: binfmt
|
||||
|
@ -12,7 +12,6 @@ config ALPHA
|
||||
select FORCE_PCI if !ALPHA_JENSEN
|
||||
select PCI_DOMAINS if PCI
|
||||
select PCI_SYSCALL if PCI
|
||||
select HAVE_AOUT
|
||||
select HAVE_ASM_MODVERSIONS
|
||||
select HAVE_PCSPKR_PLATFORM
|
||||
select HAVE_PERF_EVENTS
|
||||
|
@ -17,7 +17,6 @@ config M68K
|
||||
select GENERIC_CPU_DEVICES
|
||||
select GENERIC_IOMAP
|
||||
select GENERIC_IRQ_SHOW
|
||||
select HAVE_AOUT if MMU
|
||||
select HAVE_ASM_MODVERSIONS
|
||||
select HAVE_DEBUG_BUGVERBOSE
|
||||
select HAVE_EFFICIENT_UNALIGNED_ACCESS if !CPU_HAS_NO_UNALIGNED
|
||||
|
@ -28,6 +28,16 @@ config BINFMT_ELF
|
||||
ld.so (check the file <file:Documentation/Changes> for location and
|
||||
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
|
||||
def_bool y
|
||||
depends on COMPAT && BINFMT_ELF
|
||||
|
153
fs/binfmt_elf.c
153
fs/binfmt_elf.c
@ -93,7 +93,7 @@ static int elf_core_dump(struct coredump_params *cprm);
|
||||
#define ELF_CORE_EFLAGS 0
|
||||
#endif
|
||||
|
||||
#define ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(ELF_MIN_ALIGN-1))
|
||||
#define ELF_PAGESTART(_v) ((_v) & ~(int)(ELF_MIN_ALIGN-1))
|
||||
#define ELF_PAGEOFFSET(_v) ((_v) & (ELF_MIN_ALIGN-1))
|
||||
#define ELF_PAGEALIGN(_v) (((_v) + ELF_MIN_ALIGN - 1) & ~(ELF_MIN_ALIGN - 1))
|
||||
|
||||
@ -101,8 +101,10 @@ static struct linux_binfmt elf_format = {
|
||||
.module = THIS_MODULE,
|
||||
.load_binary = load_elf_binary,
|
||||
.load_shlib = load_elf_library,
|
||||
#ifdef CONFIG_COREDUMP
|
||||
.core_dump = elf_core_dump,
|
||||
.min_coredump = ELF_EXEC_PAGESIZE,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define BAD_ADDR(x) (unlikely((unsigned long)(x) >= TASK_SIZE))
|
||||
@ -170,8 +172,8 @@ static int padzero(unsigned long elf_bss)
|
||||
|
||||
static int
|
||||
create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
|
||||
unsigned long load_addr, unsigned long interp_load_addr,
|
||||
unsigned long e_entry)
|
||||
unsigned long interp_load_addr,
|
||||
unsigned long e_entry, unsigned long phdr_addr)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long p = bprm->p;
|
||||
@ -257,7 +259,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
|
||||
NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
|
||||
NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
|
||||
NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
|
||||
NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
|
||||
NEW_AUX_ENT(AT_PHDR, phdr_addr);
|
||||
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
|
||||
NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
|
||||
NEW_AUX_ENT(AT_BASE, interp_load_addr);
|
||||
@ -399,22 +401,21 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
|
||||
return(map_addr);
|
||||
}
|
||||
|
||||
static unsigned long total_mapping_size(const struct elf_phdr *cmds, int nr)
|
||||
static unsigned long total_mapping_size(const struct elf_phdr *phdr, int nr)
|
||||
{
|
||||
int i, first_idx = -1, last_idx = -1;
|
||||
elf_addr_t min_addr = -1;
|
||||
elf_addr_t max_addr = 0;
|
||||
bool pt_load = false;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
if (cmds[i].p_type == PT_LOAD) {
|
||||
last_idx = i;
|
||||
if (first_idx == -1)
|
||||
first_idx = i;
|
||||
if (phdr[i].p_type == PT_LOAD) {
|
||||
min_addr = min(min_addr, ELF_PAGESTART(phdr[i].p_vaddr));
|
||||
max_addr = max(max_addr, phdr[i].p_vaddr + phdr[i].p_memsz);
|
||||
pt_load = true;
|
||||
}
|
||||
}
|
||||
if (first_idx == -1)
|
||||
return 0;
|
||||
|
||||
return cmds[last_idx].p_vaddr + cmds[last_idx].p_memsz -
|
||||
ELF_PAGESTART(cmds[first_idx].p_vaddr);
|
||||
return pt_load ? (max_addr - min_addr) : 0;
|
||||
}
|
||||
|
||||
static int elf_read(struct file *file, void *buf, size_t len, loff_t pos)
|
||||
@ -823,8 +824,8 @@ static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr,
|
||||
static int load_elf_binary(struct linux_binprm *bprm)
|
||||
{
|
||||
struct file *interpreter = NULL; /* to shut gcc up */
|
||||
unsigned long load_addr = 0, load_bias = 0;
|
||||
int load_addr_set = 0;
|
||||
unsigned long load_bias = 0, phdr_addr = 0;
|
||||
int first_pt_load = 1;
|
||||
unsigned long error;
|
||||
struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
|
||||
struct elf_phdr *elf_property_phdata = NULL;
|
||||
@ -1074,12 +1075,12 @@ out_free_interp:
|
||||
|
||||
vaddr = elf_ppnt->p_vaddr;
|
||||
/*
|
||||
* The first time through the loop, load_addr_set is false:
|
||||
* The first time through the loop, first_pt_load is true:
|
||||
* layout will be calculated. Once set, use MAP_FIXED since
|
||||
* we know we've already safely mapped the entire region with
|
||||
* MAP_FIXED_NOREPLACE in the once-per-binary logic following.
|
||||
*/
|
||||
if (load_addr_set) {
|
||||
if (!first_pt_load) {
|
||||
elf_flags |= MAP_FIXED;
|
||||
} else if (elf_ex->e_type == ET_EXEC) {
|
||||
/*
|
||||
@ -1170,16 +1171,25 @@ out_free_interp:
|
||||
goto out_free_dentry;
|
||||
}
|
||||
|
||||
if (!load_addr_set) {
|
||||
load_addr_set = 1;
|
||||
load_addr = (elf_ppnt->p_vaddr - elf_ppnt->p_offset);
|
||||
if (first_pt_load) {
|
||||
first_pt_load = 0;
|
||||
if (elf_ex->e_type == ET_DYN) {
|
||||
load_bias += error -
|
||||
ELF_PAGESTART(load_bias + vaddr);
|
||||
load_addr += load_bias;
|
||||
reloc_func_desc = load_bias;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out which segment in the file contains the Program
|
||||
* Header table, and map to the associated memory address.
|
||||
*/
|
||||
if (elf_ppnt->p_offset <= elf_ex->e_phoff &&
|
||||
elf_ex->e_phoff < elf_ppnt->p_offset + elf_ppnt->p_filesz) {
|
||||
phdr_addr = elf_ex->e_phoff - elf_ppnt->p_offset +
|
||||
elf_ppnt->p_vaddr;
|
||||
}
|
||||
|
||||
k = elf_ppnt->p_vaddr;
|
||||
if ((elf_ppnt->p_flags & PF_X) && k < start_code)
|
||||
start_code = k;
|
||||
@ -1215,6 +1225,7 @@ out_free_interp:
|
||||
}
|
||||
|
||||
e_entry = elf_ex->e_entry + load_bias;
|
||||
phdr_addr += load_bias;
|
||||
elf_bss += load_bias;
|
||||
elf_brk += load_bias;
|
||||
start_code += load_bias;
|
||||
@ -1278,8 +1289,8 @@ out_free_interp:
|
||||
goto out;
|
||||
#endif /* ARCH_HAS_SETUP_ADDITIONAL_PAGES */
|
||||
|
||||
retval = create_elf_tables(bprm, elf_ex,
|
||||
load_addr, interp_load_addr, e_entry);
|
||||
retval = create_elf_tables(bprm, elf_ex, interp_load_addr,
|
||||
e_entry, phdr_addr);
|
||||
if (retval < 0)
|
||||
goto out;
|
||||
|
||||
@ -1630,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;
|
||||
@ -1662,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);
|
||||
@ -1686,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++;
|
||||
}
|
||||
|
||||
@ -1699,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,
|
||||
@ -1755,9 +1766,9 @@ static void do_thread_regset_writeback(struct task_struct *task,
|
||||
|
||||
static int fill_thread_core_info(struct elf_thread_core_info *t,
|
||||
const struct user_regset_view *view,
|
||||
long signr, size_t *total)
|
||||
long signr, struct elf_note_info *info)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int note_iter, view_iter;
|
||||
|
||||
/*
|
||||
* NT_PRSTATUS is the one special case, because the regset data
|
||||
@ -1771,17 +1782,17 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
|
||||
|
||||
fill_note(&t->notes[0], "CORE", NT_PRSTATUS,
|
||||
PRSTATUS_SIZE, &t->prstatus);
|
||||
*total += notesize(&t->notes[0]);
|
||||
info->size += notesize(&t->notes[0]);
|
||||
|
||||
do_thread_regset_writeback(t->task, &view->regsets[0]);
|
||||
|
||||
/*
|
||||
* Each other regset might generate a note too. For each regset
|
||||
* that has no core_note_type or is inactive, we leave t->notes[i]
|
||||
* all zero and we'll know to skip writing it later.
|
||||
* that has no core_note_type or is inactive, skip it.
|
||||
*/
|
||||
for (i = 1; i < view->n; ++i) {
|
||||
const struct user_regset *regset = &view->regsets[i];
|
||||
note_iter = 1;
|
||||
for (view_iter = 1; view_iter < view->n; ++view_iter) {
|
||||
const struct user_regset *regset = &view->regsets[view_iter];
|
||||
int note_type = regset->core_note_type;
|
||||
bool is_fpreg = note_type == NT_PRFPREG;
|
||||
void *data;
|
||||
@ -1797,13 +1808,17 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
if (WARN_ON_ONCE(note_iter >= info->thread_notes))
|
||||
break;
|
||||
|
||||
if (is_fpreg)
|
||||
SET_PR_FPVALID(&t->prstatus);
|
||||
|
||||
fill_note(&t->notes[i], is_fpreg ? "CORE" : "LINUX",
|
||||
fill_note(&t->notes[note_iter], is_fpreg ? "CORE" : "LINUX",
|
||||
note_type, ret, data);
|
||||
|
||||
*total += notesize(&t->notes[i]);
|
||||
info->size += notesize(&t->notes[note_iter]);
|
||||
note_iter++;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@ -1811,7 +1826,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);
|
||||
@ -1883,7 +1898,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))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
@ -1892,13 +1907,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;
|
||||
@ -2040,7 +2055,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;
|
||||
@ -2061,13 +2076,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);
|
||||
@ -2083,18 +2098,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);
|
||||
@ -2180,8 +2195,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 = { };
|
||||
@ -2189,16 +2203,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++;
|
||||
@ -2212,7 +2222,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;
|
||||
@ -2237,7 +2247,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;
|
||||
|
||||
@ -2257,8 +2267,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;
|
||||
@ -2295,8 +2305,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;
|
||||
@ -2313,7 +2323,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;
|
||||
}
|
||||
@ -2335,3 +2344,7 @@ static void __exit exit_elf_binfmt(void)
|
||||
core_initcall(init_elf_binfmt);
|
||||
module_exit(exit_elf_binfmt);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#ifdef CONFIG_BINFMT_ELF_KUNIT_TEST
|
||||
#include "binfmt_elf_test.c"
|
||||
#endif
|
||||
|
@ -83,8 +83,8 @@ static struct linux_binfmt elf_fdpic_format = {
|
||||
.load_binary = load_elf_fdpic_binary,
|
||||
#ifdef CONFIG_ELF_CORE
|
||||
.core_dump = elf_fdpic_core_dump,
|
||||
#endif
|
||||
.min_coredump = ELF_EXEC_PAGESIZE,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init init_elf_fdpic_binfmt(void)
|
||||
@ -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 @@ end_coredump:
|
||||
thread_list = thread_list->next;
|
||||
kfree(tmp);
|
||||
}
|
||||
kvfree(vma_meta);
|
||||
kfree(phdr4note);
|
||||
kfree(elf);
|
||||
kfree(psinfo);
|
||||
|
64
fs/binfmt_elf_test.c
Normal file
64
fs/binfmt_elf_test.c
Normal file
@ -0,0 +1,64 @@
|
||||
// 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 @@
|
||||
#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,13 +98,17 @@ 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,
|
||||
.load_binary = load_flat_binary,
|
||||
#ifdef CONFIG_COREDUMP
|
||||
.core_dump = flat_core_dump,
|
||||
.min_coredump = PAGE_SIZE
|
||||
#endif
|
||||
};
|
||||
|
||||
/****************************************************************************/
|
||||
@ -112,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
|
||||
|
||||
/****************************************************************************/
|
||||
/*
|
||||
|
@ -135,6 +135,8 @@
|
||||
#define elf_format compat_elf_format
|
||||
#define init_elf_binfmt init_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.
|
||||
|
@ -42,6 +42,7 @@
|
||||
#include <linux/path.h>
|
||||
#include <linux/timekeeping.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/elf.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/mmu_context.h>
|
||||
@ -53,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";
|
||||
@ -531,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);
|
||||
@ -745,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);
|
||||
/*
|
||||
@ -758,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);
|
||||
@ -980,6 +989,8 @@ static bool always_dump_vma(struct vm_area_struct *vma)
|
||||
return false;
|
||||
}
|
||||
|
||||
#define DUMP_SIZE_MAYBE_ELFHDR_PLACEHOLDER 1
|
||||
|
||||
/*
|
||||
* 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,
|
||||
* dump the first page to aid in determining what was mapped here.
|
||||
*/
|
||||
if (FILTER(ELF_HEADERS) &&
|
||||
vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ) &&
|
||||
(READ_ONCE(file_inode(vma->vm_file)->i_mode) & 0111) != 0)
|
||||
return PAGE_SIZE;
|
||||
vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) {
|
||||
if ((READ_ONCE(file_inode(vma->vm_file)->i_mode) & 0111) != 0)
|
||||
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
|
||||
|
||||
@ -1078,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
|
||||
@ -1097,36 +1130,51 @@ 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;
|
||||
|
||||
vma_data_size += m->dump_size;
|
||||
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 < 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];
|
||||
|
||||
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 0;
|
||||
return true;
|
||||
}
|
||||
|
32
fs/exec.c
32
fs/exec.c
@ -118,7 +118,7 @@ bool path_noexec(const struct path *path)
|
||||
* Note that a shared library must be both readable and executable due to
|
||||
* 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)
|
||||
{
|
||||
@ -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
|
||||
* signal to the parent that the child has run out of stack space.
|
||||
* 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)
|
||||
return -E2BIG;
|
||||
limit -= ptr_size;
|
||||
@ -536,7 +542,7 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
|
||||
if (!valid_arg_len(bprm, len))
|
||||
goto out;
|
||||
|
||||
/* We're going to work our way backwords. */
|
||||
/* We're going to work our way backwards. */
|
||||
pos = bprm->p;
|
||||
str += len;
|
||||
bprm->p -= len;
|
||||
@ -1269,7 +1275,7 @@ int begin_new_exec(struct linux_binprm * bprm)
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
retval = set_mm_exe_file(bprm->mm, bprm->file);
|
||||
@ -1897,6 +1903,9 @@ static int do_execveat_common(int fd, struct filename *filename,
|
||||
}
|
||||
|
||||
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)
|
||||
goto out_free;
|
||||
bprm->argc = retval;
|
||||
@ -1923,6 +1932,19 @@ static int do_execveat_common(int fd, struct filename *filename,
|
||||
if (retval < 0)
|
||||
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);
|
||||
out_free:
|
||||
free_bprm(bprm);
|
||||
@ -1951,6 +1973,8 @@ int kernel_execve(const char *kernel_filename,
|
||||
}
|
||||
|
||||
retval = count_strings_kernel(argv);
|
||||
if (WARN_ON_ONCE(retval == 0))
|
||||
retval = -EINVAL;
|
||||
if (retval < 0)
|
||||
goto out_free;
|
||||
bprm->argc = retval;
|
||||
|
@ -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.
|
||||
@ -98,8 +87,10 @@ struct linux_binfmt {
|
||||
struct module *module;
|
||||
int (*load_binary)(struct linux_binprm *);
|
||||
int (*load_shlib)(struct file *);
|
||||
#ifdef CONFIG_COREDUMP
|
||||
int (*core_dump)(struct coredump_params *cprm);
|
||||
unsigned long min_coredump; /* minimal dump size */
|
||||
#endif
|
||||
} __randomize_layout;
|
||||
|
||||
extern void __register_binfmt(struct linux_binfmt *fmt, int insert);
|
||||
|
@ -12,22 +12,34 @@ struct core_vma_metadata {
|
||||
unsigned long start, end;
|
||||
unsigned long flags;
|
||||
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
|
||||
* 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(struct coredump_params *cprm, size_t nr);
|
||||
extern int dump_emit(struct coredump_params *cprm, const void *addr, int nr);
|
||||
extern int dump_align(struct coredump_params *cprm, int align);
|
||||
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);
|
||||
extern void do_coredump(const kernel_siginfo_t *siginfo);
|
||||
#else
|
||||
static inline void do_coredump(const kernel_siginfo_t *siginfo) {}
|
||||
|
@ -35,10 +35,11 @@ typedef __s64 Elf64_Sxword;
|
||||
#define PT_HIOS 0x6fffffff /* OS-specific */
|
||||
#define PT_LOPROC 0x70000000
|
||||
#define PT_HIPROC 0x7fffffff
|
||||
#define PT_GNU_EH_FRAME 0x6474e550
|
||||
#define PT_GNU_PROPERTY 0x6474e553
|
||||
|
||||
#define PT_GNU_EH_FRAME (PT_LOOS + 0x474e550)
|
||||
#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 */
|
||||
#define PT_ARM_MEMTAG_MTE (PT_LOPROC + 0x1)
|
||||
|
@ -10,6 +10,7 @@ TEST_GEN_FILES := execveat.symlink execveat.denatured script subdir
|
||||
TEST_FILES := Makefile
|
||||
|
||||
TEST_GEN_PROGS += recursion-depth
|
||||
TEST_GEN_PROGS += null-argv
|
||||
|
||||
EXTRA_CLEAN := $(OUTPUT)/subdir.moved $(OUTPUT)/execveat.moved $(OUTPUT)/xxxxx* \
|
||||
$(OUTPUT)/S_I*.test
|
||||
|
78
tools/testing/selftests/exec/null-argv.c
Normal file
78
tools/testing/selftests/exec/null-argv.c
Normal file
@ -0,0 +1,78 @@
|
||||
// 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user