diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-12 04:56:37 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-12 04:56:37 +0300 |
commit | c0222ac086669a631814bbf857f8c8023452a4d7 (patch) | |
tree | bb1d9908031fcf69016eeefa7b35a4f68f414333 /fs/binfmt_elf.c | |
parent | 140cd7fb04a4a2bc09a30980bc8104cc89e09330 (diff) | |
parent | e2965cd0003f222bd49f67907c2bc6ed691c6d20 (diff) | |
download | linux-c0222ac086669a631814bbf857f8c8023452a4d7.tar.xz |
Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus
Pull MIPS updates from Ralf Baechle:
"This is an unusually large pull request for MIPS - in parts because
lots of patches missed the 3.18 deadline but primarily because some
folks opened the flood gates.
- Retire the MIPS-specific phys_t with the generic phys_addr_t.
- Improvments for the backtrace code used by oprofile.
- Better backtraces on SMP systems.
- Cleanups for the Octeon platform code.
- Cleanups and fixes for the Loongson platform code.
- Cleanups and fixes to the firmware library.
- Switch ATH79 platform to use the firmware library.
- Grand overhault to the SEAD3 and Malta interrupt code.
- Move the GIC interrupt code to drivers/irqchip
- Lots of GIC cleanups and updates to the GIC code to use modern IRQ
infrastructures and features of the kernel.
- OF documentation updates for the GIC bindings
- Move GIC clocksource driver to drivers/clocksource
- Merge GIC clocksource driver with clockevent driver.
- Further updates to bring the GIC clocksource driver up to date.
- R3000 TLB code cleanups
- Improvments to the Loongson 3 platform code.
- Convert pr_warning to pr_warn.
- Merge a bunch of small lantiq and ralink fixes that have been
staged/lingering inside the openwrt tree for a while.
- Update archhelp for IP22/IP32
- Fix a number of issues for Loongson 1B.
- New clocksource and clockevent driver for Loongson 1B.
- Further work on clk handling for Loongson 1B.
- Platform work for Broadcom BMIPS.
- Error handling cleanups for TurboChannel.
- Fixes and optimization to the microMIPS support.
- Option to disable the FTLB.
- Dump more relevant information on machine check exception
- Change binfmt to allow arch to examine PT_*PROC headers
- Support for new style FPU register model in O32
- VDSO randomization.
- BCM47xx cleanups
- BCM47xx reimplement the way the kernel accesses NVRAM information.
- Random cleanups
- Add support for ATH25 platforms
- Remove pointless locking code in some PCI platforms.
- Some improvments to EVA support
- Minor Alchemy cleanup"
* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (185 commits)
MIPS: Add MFHC0 and MTHC0 instructions to uasm.
MIPS: Cosmetic cleanups of page table headers.
MIPS: Add CP0 macros for extended EntryLo registers
MIPS: Remove now unused definition of phys_t.
MIPS: Replace use of phys_t with phys_addr_t.
MIPS: Replace MIPS-specific 64BIT_PHYS_ADDR with generic PHYS_ADDR_T_64BIT
PCMCIA: Alchemy Don't select 64BIT_PHYS_ADDR in Kconfig.
MIPS: lib: memset: Clean up some MIPS{EL,EB} ifdefery
MIPS: iomap: Use __mem_{read,write}{b,w,l} for MMIO
MIPS: <asm/types.h> fix indentation.
MAINTAINERS: Add entry for BMIPS multiplatform kernel
MIPS: Enable VDSO randomization
MIPS: Remove a temporary hack for debugging cache flushes in SMTC configuration
MIPS: Remove declaration of obsolete arch_init_clk_ops()
MIPS: atomic.h: Reformat to fit in 79 columns
MIPS: Apply `.insn' to fixup labels throughout
MIPS: Fix microMIPS LL/SC immediate offsets
MIPS: Kconfig: Only allow 32-bit microMIPS builds
MIPS: signal.c: Fix an invalid cast in ISA mode bit handling
MIPS: mm: Only build one microassembler that is suitable
...
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r-- | fs/binfmt_elf.c | 238 |
1 files changed, 176 insertions, 62 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 3a6175fe10c0..02b16910f4c9 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -386,6 +386,127 @@ static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr) ELF_PAGESTART(cmds[first_idx].p_vaddr); } +/** + * load_elf_phdrs() - load ELF program headers + * @elf_ex: ELF header of the binary whose program headers should be loaded + * @elf_file: the opened ELF binary file + * + * Loads ELF program headers from the binary file elf_file, which has the ELF + * header pointed to by elf_ex, into a newly allocated array. The caller is + * responsible for freeing the allocated data. Returns an ERR_PTR upon failure. + */ +static struct elf_phdr *load_elf_phdrs(struct elfhdr *elf_ex, + struct file *elf_file) +{ + struct elf_phdr *elf_phdata = NULL; + int retval, size, err = -1; + + /* + * If the size of this structure has changed, then punt, since + * we will be doing the wrong thing. + */ + if (elf_ex->e_phentsize != sizeof(struct elf_phdr)) + goto out; + + /* Sanity check the number of program headers... */ + if (elf_ex->e_phnum < 1 || + elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr)) + goto out; + + /* ...and their total size. */ + size = sizeof(struct elf_phdr) * elf_ex->e_phnum; + if (size > ELF_MIN_ALIGN) + goto out; + + elf_phdata = kmalloc(size, GFP_KERNEL); + if (!elf_phdata) + goto out; + + /* Read in the program headers */ + retval = kernel_read(elf_file, elf_ex->e_phoff, + (char *)elf_phdata, size); + if (retval != size) { + err = (retval < 0) ? retval : -EIO; + goto out; + } + + /* Success! */ + err = 0; +out: + if (err) { + kfree(elf_phdata); + elf_phdata = NULL; + } + return elf_phdata; +} + +#ifndef CONFIG_ARCH_BINFMT_ELF_STATE + +/** + * struct arch_elf_state - arch-specific ELF loading state + * + * This structure is used to preserve architecture specific data during + * the loading of an ELF file, throughout the checking of architecture + * specific ELF headers & through to the point where the ELF load is + * known to be proceeding (ie. SET_PERSONALITY). + * + * This implementation is a dummy for architectures which require no + * specific state. + */ +struct arch_elf_state { +}; + +#define INIT_ARCH_ELF_STATE {} + +/** + * arch_elf_pt_proc() - check a PT_LOPROC..PT_HIPROC ELF program header + * @ehdr: The main ELF header + * @phdr: The program header to check + * @elf: The open ELF file + * @is_interp: True if the phdr is from the interpreter of the ELF being + * loaded, else false. + * @state: Architecture-specific state preserved throughout the process + * of loading the ELF. + * + * Inspects the program header phdr to validate its correctness and/or + * suitability for the system. Called once per ELF program header in the + * range PT_LOPROC to PT_HIPROC, for both the ELF being loaded and its + * interpreter. + * + * Return: Zero to proceed with the ELF load, non-zero to fail the ELF load + * with that return code. + */ +static inline int arch_elf_pt_proc(struct elfhdr *ehdr, + struct elf_phdr *phdr, + struct file *elf, bool is_interp, + struct arch_elf_state *state) +{ + /* Dummy implementation, always proceed */ + return 0; +} + +/** + * arch_check_elf() - check a PT_LOPROC..PT_HIPROC ELF program header + * @ehdr: The main ELF header + * @has_interp: True if the ELF has an interpreter, else false. + * @state: Architecture-specific state preserved throughout the process + * of loading the ELF. + * + * Provides a final opportunity for architecture code to reject the loading + * of the ELF & cause an exec syscall to return an error. This is called after + * all program headers to be checked by arch_elf_pt_proc have been. + * + * Return: Zero to proceed with the ELF load, non-zero to fail the ELF load + * with that return code. + */ +static inline int arch_check_elf(struct elfhdr *ehdr, bool has_interp, + struct arch_elf_state *state) +{ + /* Dummy implementation, always proceed */ + return 0; +} + +#endif /* !CONFIG_ARCH_BINFMT_ELF_STATE */ /* This is much more generalized than the library routine read function, so we keep this separate. Technically the library read function @@ -394,16 +515,15 @@ static unsigned long total_mapping_size(struct elf_phdr *cmds, int nr) static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, struct file *interpreter, unsigned long *interp_map_addr, - unsigned long no_base) + unsigned long no_base, struct elf_phdr *interp_elf_phdata) { - struct elf_phdr *elf_phdata; struct elf_phdr *eppnt; unsigned long load_addr = 0; int load_addr_set = 0; unsigned long last_bss = 0, elf_bss = 0; unsigned long error = ~0UL; unsigned long total_size; - int retval, i, size; + int i; /* First of all, some simple consistency checks */ if (interp_elf_ex->e_type != ET_EXEC && @@ -414,40 +534,14 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, if (!interpreter->f_op->mmap) goto out; - /* - * If the size of this structure has changed, then punt, since - * we will be doing the wrong thing. - */ - if (interp_elf_ex->e_phentsize != sizeof(struct elf_phdr)) - goto out; - if (interp_elf_ex->e_phnum < 1 || - interp_elf_ex->e_phnum > 65536U / sizeof(struct elf_phdr)) - goto out; - - /* Now read in all of the header information */ - size = sizeof(struct elf_phdr) * interp_elf_ex->e_phnum; - if (size > ELF_MIN_ALIGN) - goto out; - elf_phdata = kmalloc(size, GFP_KERNEL); - if (!elf_phdata) - goto out; - - retval = kernel_read(interpreter, interp_elf_ex->e_phoff, - (char *)elf_phdata, size); - error = -EIO; - if (retval != size) { - if (retval < 0) - error = retval; - goto out_close; - } - - total_size = total_mapping_size(elf_phdata, interp_elf_ex->e_phnum); + total_size = total_mapping_size(interp_elf_phdata, + interp_elf_ex->e_phnum); if (!total_size) { error = -EINVAL; - goto out_close; + goto out; } - eppnt = elf_phdata; + eppnt = interp_elf_phdata; for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) { if (eppnt->p_type == PT_LOAD) { int elf_type = MAP_PRIVATE | MAP_DENYWRITE; @@ -474,7 +568,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, *interp_map_addr = map_addr; error = map_addr; if (BAD_ADDR(map_addr)) - goto out_close; + goto out; if (!load_addr_set && interp_elf_ex->e_type == ET_DYN) { @@ -493,7 +587,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, eppnt->p_memsz > TASK_SIZE || TASK_SIZE - eppnt->p_memsz < k) { error = -ENOMEM; - goto out_close; + goto out; } /* @@ -523,7 +617,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, */ if (padzero(elf_bss)) { error = -EFAULT; - goto out_close; + goto out; } /* What we have mapped so far */ @@ -532,13 +626,10 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, /* Map the last of the bss segment */ error = vm_brk(elf_bss, last_bss - elf_bss); if (BAD_ADDR(error)) - goto out_close; + goto out; } error = load_addr; - -out_close: - kfree(elf_phdata); out: return error; } @@ -575,10 +666,9 @@ static int load_elf_binary(struct linux_binprm *bprm) int load_addr_set = 0; char * elf_interpreter = NULL; unsigned long error; - struct elf_phdr *elf_ppnt, *elf_phdata; + struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL; unsigned long elf_bss, elf_brk; int retval, i; - unsigned int size; unsigned long elf_entry; unsigned long interp_load_addr = 0; unsigned long start_code, end_code, start_data, end_data; @@ -589,6 +679,7 @@ static int load_elf_binary(struct linux_binprm *bprm) struct elfhdr elf_ex; struct elfhdr interp_elf_ex; } *loc; + struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE; loc = kmalloc(sizeof(*loc), GFP_KERNEL); if (!loc) { @@ -611,26 +702,10 @@ static int load_elf_binary(struct linux_binprm *bprm) if (!bprm->file->f_op->mmap) goto out; - /* Now read in all of the header information */ - if (loc->elf_ex.e_phentsize != sizeof(struct elf_phdr)) - goto out; - if (loc->elf_ex.e_phnum < 1 || - loc->elf_ex.e_phnum > 65536U / sizeof(struct elf_phdr)) - goto out; - size = loc->elf_ex.e_phnum * sizeof(struct elf_phdr); - retval = -ENOMEM; - elf_phdata = kmalloc(size, GFP_KERNEL); + elf_phdata = load_elf_phdrs(&loc->elf_ex, bprm->file); if (!elf_phdata) goto out; - retval = kernel_read(bprm->file, loc->elf_ex.e_phoff, - (char *)elf_phdata, size); - if (retval != size) { - if (retval >= 0) - retval = -EIO; - goto out_free_ph; - } - elf_ppnt = elf_phdata; elf_bss = 0; elf_brk = 0; @@ -699,12 +774,21 @@ static int load_elf_binary(struct linux_binprm *bprm) elf_ppnt = elf_phdata; for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) - if (elf_ppnt->p_type == PT_GNU_STACK) { + switch (elf_ppnt->p_type) { + case PT_GNU_STACK: if (elf_ppnt->p_flags & PF_X) executable_stack = EXSTACK_ENABLE_X; else executable_stack = EXSTACK_DISABLE_X; break; + + case PT_LOPROC ... PT_HIPROC: + retval = arch_elf_pt_proc(&loc->elf_ex, elf_ppnt, + bprm->file, false, + &arch_state); + if (retval) + goto out_free_dentry; + break; } /* Some simple consistency checks for the interpreter */ @@ -716,8 +800,36 @@ static int load_elf_binary(struct linux_binprm *bprm) /* Verify the interpreter has a valid arch */ if (!elf_check_arch(&loc->interp_elf_ex)) goto out_free_dentry; + + /* Load the interpreter program headers */ + interp_elf_phdata = load_elf_phdrs(&loc->interp_elf_ex, + interpreter); + if (!interp_elf_phdata) + goto out_free_dentry; + + /* Pass PT_LOPROC..PT_HIPROC headers to arch code */ + elf_ppnt = interp_elf_phdata; + for (i = 0; i < loc->interp_elf_ex.e_phnum; i++, elf_ppnt++) + switch (elf_ppnt->p_type) { + case PT_LOPROC ... PT_HIPROC: + retval = arch_elf_pt_proc(&loc->interp_elf_ex, + elf_ppnt, interpreter, + true, &arch_state); + if (retval) + goto out_free_dentry; + break; + } } + /* + * Allow arch code to reject the ELF at this point, whilst it's + * still possible to return an error to the code that invoked + * the exec syscall. + */ + retval = arch_check_elf(&loc->elf_ex, !!interpreter, &arch_state); + if (retval) + goto out_free_dentry; + /* Flush all traces of the currently running executable */ retval = flush_old_exec(bprm); if (retval) @@ -725,7 +837,7 @@ static int load_elf_binary(struct linux_binprm *bprm) /* Do this immediately, since STACK_TOP as used in setup_arg_pages may depend on the personality. */ - SET_PERSONALITY(loc->elf_ex); + SET_PERSONALITY2(loc->elf_ex, &arch_state); if (elf_read_implies_exec(loc->elf_ex, executable_stack)) current->personality |= READ_IMPLIES_EXEC; @@ -890,7 +1002,7 @@ static int load_elf_binary(struct linux_binprm *bprm) elf_entry = load_elf_interp(&loc->interp_elf_ex, interpreter, &interp_map_addr, - load_bias); + load_bias, interp_elf_phdata); if (!IS_ERR((void *)elf_entry)) { /* * load_elf_interp() returns relocation @@ -917,6 +1029,7 @@ static int load_elf_binary(struct linux_binprm *bprm) } } + kfree(interp_elf_phdata); kfree(elf_phdata); set_binfmt(&elf_format); @@ -981,6 +1094,7 @@ out_ret: /* error cleanup */ out_free_dentry: + kfree(interp_elf_phdata); allow_write_access(interpreter); if (interpreter) fput(interpreter); |