cregit-Linux how code gets into the kernel

Release 4.7 fs/binfmt_elf_fdpic.c

Directory: fs
/* binfmt_elf_fdpic.c: FDPIC ELF binary format
 *
 * Copyright (C) 2003, 2004, 2006 Red Hat, Inc. All Rights Reserved.
 * Written by David Howells (dhowells@redhat.com)
 * Derived from binfmt_elf.c
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>

#include <linux/fs.h>
#include <linux/stat.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/mman.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/binfmts.h>
#include <linux/string.h>
#include <linux/file.h>
#include <linux/fcntl.h>
#include <linux/slab.h>
#include <linux/pagemap.h>
#include <linux/security.h>
#include <linux/highmem.h>
#include <linux/highuid.h>
#include <linux/personality.h>
#include <linux/ptrace.h>
#include <linux/init.h>
#include <linux/elf.h>
#include <linux/elf-fdpic.h>
#include <linux/elfcore.h>
#include <linux/coredump.h>
#include <linux/dax.h>

#include <asm/uaccess.h>
#include <asm/param.h>
#include <asm/pgalloc.h>


typedef char *elf_caddr_t;

#if 0
#define kdebug(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ )
#else

#define kdebug(fmt, ...) do {} while(0)
#endif

#if 0
#define kdcore(fmt, ...) printk("FDPIC "fmt"\n" ,##__VA_ARGS__ )
#else

#define kdcore(fmt, ...) do {} while(0)
#endif

MODULE_LICENSE("GPL");

static int load_elf_fdpic_binary(struct linux_binprm *);
static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *, struct file *);
static int elf_fdpic_map_file(struct elf_fdpic_params *, struct file *,
			      struct mm_struct *, const char *);

static int create_elf_fdpic_tables(struct linux_binprm *, struct mm_struct *,
				   struct elf_fdpic_params *,
				   struct elf_fdpic_params *);

#ifndef CONFIG_MMU
static int elf_fdpic_transfer_args_to_stack(struct linux_binprm *,
					    unsigned long *);
static int elf_fdpic_map_file_constdisp_on_uclinux(struct elf_fdpic_params *,
						   struct file *,
						   struct mm_struct *);
#endif

static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *,
					     struct file *, struct mm_struct *);

#ifdef CONFIG_ELF_CORE
static int elf_fdpic_core_dump(struct coredump_params *cprm);
#endif


static struct linux_binfmt elf_fdpic_format = {
	.module		= THIS_MODULE,
	.load_binary	= load_elf_fdpic_binary,
#ifdef CONFIG_ELF_CORE
	.core_dump	= elf_fdpic_core_dump,
#endif
	.min_coredump	= ELF_EXEC_PAGESIZE,
};


static int __init init_elf_fdpic_binfmt(void) { register_binfmt(&elf_fdpic_format); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells1583.33%150.00%
al viroal viro316.67%150.00%
Total18100.00%2100.00%


static void __exit exit_elf_fdpic_binfmt(void) { unregister_binfmt(&elf_fdpic_format); }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells15100.00%1100.00%
Total15100.00%1100.00%

core_initcall(init_elf_fdpic_binfmt); module_exit(exit_elf_fdpic_binfmt);
static int is_elf(struct elfhdr *hdr, struct file *file) { if (memcmp(hdr->e_ident, ELFMAG, SELFMAG) != 0) return 0; if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) return 0; if (!elf_check_arch(hdr)) return 0; if (!file->f_op->mmap) return 0; return 1; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells7698.70%150.00%
rich felkerrich felker11.30%150.00%
Total77100.00%2100.00%

#ifndef elf_check_fdpic #define elf_check_fdpic(x) 0 #endif #ifndef elf_check_const_displacement #define elf_check_const_displacement(x) 0 #endif
static int is_constdisp(struct elfhdr *hdr) { if (!elf_check_fdpic(hdr)) return 1; if (elf_check_const_displacement(hdr)) return 1; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
rich felkerrich felker35100.00%1100.00%
Total35100.00%1100.00%

/*****************************************************************************/ /* * read the program headers table into memory */
static int elf_fdpic_fetch_phdrs(struct elf_fdpic_params *params, struct file *file) { struct elf32_phdr *phdr; unsigned long size; int retval, loop; if (params->hdr.e_phentsize != sizeof(struct elf_phdr)) return -ENOMEM; if (params->hdr.e_phnum > 65536U / sizeof(struct elf_phdr)) return -ENOMEM; size = params->hdr.e_phnum * sizeof(struct elf_phdr); params->phdrs = kmalloc(size, GFP_KERNEL); if (!params->phdrs) return -ENOMEM; retval = kernel_read(file, params->hdr.e_phoff, (char *) params->phdrs, size); if (unlikely(retval != size)) return retval < 0 ? retval : -ENOEXEC; /* determine stack size for this binary */ phdr = params->phdrs; for (loop = 0; loop < params->hdr.e_phnum; loop++, phdr++) { if (phdr->p_type != PT_GNU_STACK) continue; if (phdr->p_flags & PF_X) params->flags |= ELF_FDPIC_FLAG_EXEC_STACK; else params->flags |= ELF_FDPIC_FLAG_NOEXEC_STACK; params->stack_size = phdr->p_memsz; break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells217100.00%2100.00%
Total217100.00%2100.00%

/*****************************************************************************/ /* * load an fdpic binary into various bits of memory */
static int load_elf_fdpic_binary(struct linux_binprm *bprm) { struct elf_fdpic_params exec_params, interp_params; struct pt_regs *regs = current_pt_regs(); struct elf_phdr *phdr; unsigned long stack_size, entryaddr; #ifdef ELF_FDPIC_PLAT_INIT unsigned long dynaddr; #endif #ifndef CONFIG_MMU unsigned long stack_prot; #endif struct file *interpreter = NULL; /* to shut gcc up */ char *interpreter_name = NULL; int executable_stack; int retval, i; kdebug("____ LOAD %d ____", current->pid); memset(&exec_params, 0, sizeof(exec_params)); memset(&interp_params, 0, sizeof(interp_params)); exec_params.hdr = *(struct elfhdr *) bprm->buf; exec_params.flags = ELF_FDPIC_FLAG_PRESENT | ELF_FDPIC_FLAG_EXECUTABLE; /* check that this is a binary we know how to deal with */ retval = -ENOEXEC; if (!is_elf(&exec_params.hdr, bprm->file)) goto error; if (!elf_check_fdpic(&exec_params.hdr)) { #ifdef CONFIG_MMU /* binfmt_elf handles non-fdpic elf except on nommu */ goto error; #else /* nommu can only load ET_DYN (PIE) ELF */ if (exec_params.hdr.e_type != ET_DYN) goto error; #endif } /* read the program header table */ retval = elf_fdpic_fetch_phdrs(&exec_params, bprm->file); if (retval < 0) goto error; /* scan for a program header that specifies an interpreter */ phdr = exec_params.phdrs; for (i = 0; i < exec_params.hdr.e_phnum; i++, phdr++) { switch (phdr->p_type) { case PT_INTERP: retval = -ENOMEM; if (phdr->p_filesz > PATH_MAX) goto error; retval = -ENOENT; if (phdr->p_filesz < 2) goto error; /* read the name of the interpreter into memory */ interpreter_name = kmalloc(phdr->p_filesz, GFP_KERNEL); if (!interpreter_name) goto error; retval = kernel_read(bprm->file, phdr->p_offset, interpreter_name, phdr->p_filesz); if (unlikely(retval != phdr->p_filesz)) { if (retval >= 0) retval = -ENOEXEC; goto error; } retval = -ENOENT; if (interpreter_name[phdr->p_filesz - 1] != '\0') goto error; kdebug("Using ELF interpreter %s", interpreter_name); /* replace the program with the interpreter */ interpreter = open_exec(interpreter_name); retval = PTR_ERR(interpreter); if (IS_ERR(interpreter)) { interpreter = NULL; goto error; } /* * If the binary is not readable then enforce * mm->dumpable = 0 regardless of the interpreter's * permissions. */ would_dump(bprm, interpreter); retval = kernel_read(interpreter, 0, bprm->buf, BINPRM_BUF_SIZE); if (unlikely(retval != BINPRM_BUF_SIZE)) { if (retval >= 0) retval = -ENOEXEC; goto error; } interp_params.hdr = *((struct elfhdr *) bprm->buf); break; case PT_LOAD: #ifdef CONFIG_MMU if (exec_params.load_addr == 0) exec_params.load_addr = phdr->p_vaddr; #endif break; } } if (is_constdisp(&exec_params.hdr)) exec_params.flags |= ELF_FDPIC_FLAG_CONSTDISP; /* perform insanity checks on the interpreter */ if (interpreter_name) { retval = -ELIBBAD; if (!is_elf(&interp_params.hdr, interpreter)) goto error; interp_params.flags = ELF_FDPIC_FLAG_PRESENT; /* read the interpreter's program header table */ retval = elf_fdpic_fetch_phdrs(&interp_params, interpreter); if (retval < 0) goto error; } stack_size = exec_params.stack_size; if (exec_params.flags & ELF_FDPIC_FLAG_EXEC_STACK) executable_stack = EXSTACK_ENABLE_X; else if (exec_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK) executable_stack = EXSTACK_DISABLE_X; else executable_stack = EXSTACK_DEFAULT; if (stack_size == 0) { stack_size = interp_params.stack_size; if (interp_params.flags & ELF_FDPIC_FLAG_EXEC_STACK) executable_stack = EXSTACK_ENABLE_X; else if (interp_params.flags & ELF_FDPIC_FLAG_NOEXEC_STACK) executable_stack = EXSTACK_DISABLE_X; else executable_stack = EXSTACK_DEFAULT; } retval = -ENOEXEC; if (stack_size == 0) stack_size = 131072UL; /* same as exec.c's default commit */ if (is_constdisp(&interp_params.hdr)) interp_params.flags |= ELF_FDPIC_FLAG_CONSTDISP; /* flush all traces of the currently running executable */ retval = flush_old_exec(bprm); if (retval) goto error; /* there's now no turning back... the old userspace image is dead, * defunct, deceased, etc. */ if (elf_check_fdpic(&exec_params.hdr)) set_personality(PER_LINUX_FDPIC); else set_personality(PER_LINUX); if (elf_read_implies_exec(&exec_params.hdr, executable_stack)) current->personality |= READ_IMPLIES_EXEC; setup_new_exec(bprm); set_binfmt(&elf_fdpic_format); current->mm->start_code = 0; current->mm->end_code = 0; current->mm->start_stack = 0; current->mm->start_data = 0; current->mm->end_data = 0; current->mm->context.exec_fdpic_loadmap = 0; current->mm->context.interp_fdpic_loadmap = 0; #ifdef CONFIG_MMU elf_fdpic_arch_lay_out_mm(&exec_params, &interp_params, &current->mm->start_stack, &current->mm->start_brk); retval = setup_arg_pages(bprm, current->mm->start_stack, executable_stack); if (retval < 0) goto error; #endif /* load the executable and interpreter into memory */ retval = elf_fdpic_map_file(&exec_params, bprm->file, current->mm, "executable"); if (retval < 0) goto error; if (interpreter_name) { retval = elf_fdpic_map_file(&interp_params, interpreter, current->mm, "interpreter"); if (retval < 0) { printk(KERN_ERR "Unable to load interpreter\n"); goto error; } allow_write_access(interpreter); fput(interpreter); interpreter = NULL; } #ifdef CONFIG_MMU if (!current->mm->start_brk) current->mm->start_brk = current->mm->end_data; current->mm->brk = current->mm->start_brk = PAGE_ALIGN(current->mm->start_brk); #else /* create a stack area and zero-size brk area */ stack_size = (stack_size + PAGE_SIZE - 1) & PAGE_MASK; if (stack_size < PAGE_SIZE * 2) stack_size = PAGE_SIZE * 2; stack_prot = PROT_READ | PROT_WRITE; if (executable_stack == EXSTACK_ENABLE_X || (executable_stack == EXSTACK_DEFAULT && VM_STACK_FLAGS & VM_EXEC)) stack_prot |= PROT_EXEC; current->mm->start_brk = vm_mmap(NULL, 0, stack_size, stack_prot, MAP_PRIVATE | MAP_ANONYMOUS | MAP_UNINITIALIZED | MAP_GROWSDOWN, 0); if (IS_ERR_VALUE(current->mm->start_brk)) { retval = current->mm->start_brk; current->mm->start_brk = 0; goto error; } current->mm->brk = current->mm->start_brk; current->mm->context.end_brk = current->mm->start_brk; current->mm->start_stack = current->mm->start_brk + stack_size; #endif install_exec_creds(bprm); if (create_elf_fdpic_tables(bprm, current->mm, &exec_params, &interp_params) < 0) goto error; kdebug("- start_code %lx", current->mm->start_code); kdebug("- end_code %lx", current->mm->end_code); kdebug("- start_data %lx", current->mm->start_data); kdebug("- end_data %lx", current->mm->end_data); kdebug("- start_brk %lx", current->mm->start_brk); kdebug("- brk %lx", current->mm->brk); kdebug("- start_stack %lx", current->mm->start_stack); #ifdef ELF_FDPIC_PLAT_INIT /* * The ABI may specify that certain registers be set up in special * ways (on i386 %edx is the address of a DT_FINI function, for * example. This macro performs whatever initialization to * the regs structure is required. */ dynaddr = interp_params.dynamic_addr ?: exec_params.dynamic_addr; ELF_FDPIC_PLAT_INIT(regs, exec_params.map_addr, interp_params.map_addr, dynaddr); #endif /* everything is now ready... get the userspace context ready to roll */ entryaddr = interp_params.entry_addr ?: exec_params.entry_addr; start_thread(regs, entryaddr, current->mm->start_stack); retval = 0; error: if (interpreter) { allow_write_access(interpreter); fput(interpreter); } kfree(interpreter_name); kfree(exec_params.phdrs); kfree(exec_params.loadmap); kfree(interp_params.phdrs); kfree(interp_params.loadmap); return retval; }

Contributors

PersonTokensPropCommitsCommitProp
david howellsdavid howells121689.08%738.89%
rich felkerrich felker644.69%211.11%
mike frysingermike frysinger543.96%15.56%
al viroal viro171.25%316.67%
linus torvaldslinus torvalds60.44%211.11%
alexey dobriyanalexey dobriyan50.37%15.56%
jie zhangjie zhang20.15%15.56%
robert p. j. dayrobert p. j. day10.07%15.56%
Total1365100.00%18100.00%

/*****************************************************************************/ #ifndef ELF_BASE_PLATFORM /* * AT_BASE_PLATFORM indicates the "real" hardware/microarchitecture. * If the arch defines ELF_BASE_PLATFORM (in asm/elf.h), the value * will be copied to the user stack in the same manner as AT_PLATFORM. */ #define ELF_BASE_PLATFORM NULL #endif /* * present useful information to the program by shovelling it onto the new * process's stack */
static int create_elf_fdpic_tables(struct linux_binprm *bprm, struct mm_struct *mm, struct elf_fdpic_params *exec_params, struct elf_fdpic_params *interp_params) { const struct cred *cred = current_cred(); unsigned long sp, csp, nitems; elf_caddr_t __user *argv, *envp; size_t platform_len = 0, len; char *k_platform, *k_base_platform; char __user *u_platform, *u_base_platform, *p; int loop; int nr; /* reset for each csp adjustment */ #ifdef CONFIG_MMU /* In some cases (e.g. Hyper-Threading), we want to avoid L1 evictions * by the processes running on the same package. One thing we can do is * to shuffle the initial stack for them, so we give the architecture * an opportunity to do so here. */ sp = arch_align_stack(bprm->p); #else sp = mm->start_stack; /* stack the program arguments and environment */ if (elf_fdpic_transfer_args_to_stack(bprm, &sp) < 0) return -EFAULT; #endif /* * If this architecture has a platform capability string, copy it * to userspace. In some cases (Sparc), this info is impossible * for userspace to get any other way, in others (i386) it is * merely difficult. */ k_platform = ELF_PLATFORM; u_platform = NULL; if (k_platform) { platform_len = strlen(k_platform) + 1; sp -= platform_len; u_platform = (char __user *) sp; if (__copy_to_user(u_platform, k_platform, platform_len) != 0) return -EFAULT; } /* * If this architecture has a "base" platform capability * string, copy it to userspace. */ k_base_platform = ELF_BASE_PLATFORM; u_base_platform = NULL; if (k_base_platform) { platform_len = strlen(k_base_platform) + 1; sp -= platform_len; u_base_platform = (char __user *) sp; if (__copy_to_user(u_base_platform, k_base_platform, platform_len) != 0) return -EFAULT; } sp &= ~7UL; /* stack the load map(s) */ len = sizeof(struct elf32_fdpic_loadmap); len += sizeof(struct elf32_fdpic_loadseg) * exec_params->loadmap->nsegs; sp = (sp - len) & ~7UL; exec_params->map_addr = sp; if (copy_to_user((void __user *) sp, exec_params->loadmap, len) != 0) return -EFAULT; current->mm->context.exec_fdpic_loadmap = (unsigned long) sp; if (interp_params->loadmap) { len = sizeof(struct elf32_fdpic_loadmap); len += sizeof(struct elf32_fdpic_loadseg) * interp_params->loadmap->nsegs; sp = (sp - len) & ~7UL; interp_params->map_addr = sp; if (copy_to_user((void __user *) sp, interp_params->loadmap, len) != 0) return -EFAULT; current->mm->context.interp_fdpic_loadmap = (unsigned long) sp; } /* force 16 byte _final_ alignment here for generality */ #define DLINFO_ITEMS 15 nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0) + (k_base_platform ? 1 : 0) + AT_VECTOR_SIZE_ARCH; if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) nitems++; csp = sp; sp -= nitems * 2 * sizeof(unsigned long); sp -= (bprm->envc + 1) * sizeof(char *); /* envv[] */ sp -= (bprm->argc + 1) * sizeof(char *); /* argv[] */ sp -= 1 * sizeof(unsigned long); /* argc */ csp -= sp & 15UL; sp -= sp & 15UL; /* put the ELF interpreter info on the stack */ #define NEW_AUX_ENT(id, val) \ do { \ struct { unsigned long _id, _val; } __user *ent; \ \ ent = (void __user *) csp; \ __put_user((id), &ent[nr]._id); \ __put_user((val), &ent[nr]._val); \ nr++; \ } while (0) nr = 0; csp -= 2 * sizeof(unsigned long); NEW_AUX_ENT(AT_NULL, 0); if (k_platform) { nr = 0; csp -= 2 * sizeof(unsigned long); NEW_AUX_ENT(AT_PLATFORM, (elf_addr_t) (unsigned long) u_platform); } if (k_base_platform) { nr = 0; csp -= 2 * sizeof(unsigned long); NEW_AUX_ENT(AT_BASE_PLATFORM, (elf_addr_t) (unsigned long) u_base_platform); } if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) { nr = 0; csp -= 2 * sizeof(unsigned long); NEW_AUX_ENT(AT_EXECFD, bprm->interp_data); } nr = 0; csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long); NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP); #ifdef ELF_HWCAP2 NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2); #endif NEW_AUX_ENT(AT_PAGESZ, PAGE_SIZE); NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC); NEW_AUX_ENT(AT_PHDR, exec_params->ph_addr); NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr)); NEW_AUX_ENT(AT_PHNUM, exec_params->hdr.e_phnum); NEW_AUX_ENT(AT_BASE, interp_params->elfhdr_addr); NEW_AUX_ENT(AT_FLAGS, 0); NEW_AUX_ENT(AT_ENTRY, exec_params->entry_addr); NEW_AUX_ENT(AT_UID, (elf_addr_t) from_kuid_munged(cred->user_ns, cred->uid)); NEW_AUX_ENT(AT_EUID, (elf_addr_t) from_kuid_munged(cred->user_ns, cred->euid)); NEW_AUX_ENT(AT_GID, (elf_addr_t) from_kgid_munged(cred->user_ns, cred->gid)); NEW_AUX_ENT(AT_EGID, (elf_addr_t) from_kgid_munged(cred->user_ns, cred->