Merge branch 'fdpic' of http://git.linaro.org/people/nicolas.pitre/linux into devel...
authorRussell King <rmk+kernel@armlinux.org.uk>
Thu, 28 Sep 2017 10:16:33 +0000 (11:16 +0100)
committerRussell King <rmk+kernel@armlinux.org.uk>
Mon, 2 Oct 2017 22:16:29 +0000 (23:16 +0100)
This series provides the needed changes to suport the ELF_FDPIC binary
format on ARM. Both MMU and non-MMU systems are supported. This format
has many advantages over the BFLT format used on MMU-less systems, such
as being real ELF that can be parsed by standard tools, can support
shared dynamic libs, etc.

1  2 
arch/arm/kernel/signal.c
fs/binfmt_elf.c
fs/binfmt_elf_fdpic.c

diff --combined arch/arm/kernel/signal.c
index e2de50bf87425e4a55baa66ef68d1fe99e53e1f8,1f3574ec4fad823d560a644b53a1f9c871240d89..237973492479a3c0e15fd35a486b2255ef3aa913
  #include <linux/uaccess.h>
  #include <linux/tracehook.h>
  #include <linux/uprobes.h>
 +#include <linux/syscalls.h>
  
  #include <asm/elf.h>
  #include <asm/cacheflush.h>
  #include <asm/traps.h>
- #include <asm/ucontext.h>
  #include <asm/unistd.h>
  #include <asm/vfp.h>
  
- extern const unsigned long sigreturn_codes[7];
+ #include "signal.h"
+ extern const unsigned long sigreturn_codes[17];
  
  static unsigned long signal_return_offset;
  
@@@ -172,15 -172,6 +173,6 @@@ static int restore_vfp_context(char __u
  /*
   * Do a signal return; undo the signal stack.  These are aligned to 64-bit.
   */
- struct sigframe {
-       struct ucontext uc;
-       unsigned long retcode[2];
- };
- struct rt_sigframe {
-       struct siginfo info;
-       struct sigframe sig;
- };
  
  static int restore_sigframe(struct pt_regs *regs, struct sigframe __user *sf)
  {
@@@ -366,9 -357,20 +358,20 @@@ setup_return(struct pt_regs *regs, stru
             unsigned long __user *rc, void __user *frame)
  {
        unsigned long handler = (unsigned long)ksig->ka.sa.sa_handler;
+       unsigned long handler_fdpic_GOT = 0;
        unsigned long retcode;
-       int thumb = 0;
+       unsigned int idx, thumb = 0;
        unsigned long cpsr = regs->ARM_cpsr & ~(PSR_f | PSR_E_BIT);
+       bool fdpic = IS_ENABLED(CONFIG_BINFMT_ELF_FDPIC) &&
+                    (current->personality & FDPIC_FUNCPTRS);
+       if (fdpic) {
+               unsigned long __user *fdpic_func_desc =
+                                       (unsigned long __user *)handler;
+               if (__get_user(handler, &fdpic_func_desc[0]) ||
+                   __get_user(handler_fdpic_GOT, &fdpic_func_desc[1]))
+                       return 1;
+       }
  
        cpsr |= PSR_ENDSTATE;
  
  
        if (ksig->ka.sa.sa_flags & SA_RESTORER) {
                retcode = (unsigned long)ksig->ka.sa.sa_restorer;
+               if (fdpic) {
+                       /*
+                        * We need code to load the function descriptor.
+                        * That code follows the standard sigreturn code
+                        * (6 words), and is made of 3 + 2 words for each
+                        * variant. The 4th copied word is the actual FD
+                        * address that the assembly code expects.
+                        */
+                       idx = 6 + thumb * 3;
+                       if (ksig->ka.sa.sa_flags & SA_SIGINFO)
+                               idx += 5;
+                       if (__put_user(sigreturn_codes[idx],   rc  ) ||
+                           __put_user(sigreturn_codes[idx+1], rc+1) ||
+                           __put_user(sigreturn_codes[idx+2], rc+2) ||
+                           __put_user(retcode,                rc+3))
+                               return 1;
+                       goto rc_finish;
+               }
        } else {
-               unsigned int idx = thumb << 1;
+               idx = thumb << 1;
                if (ksig->ka.sa.sa_flags & SA_SIGINFO)
                        idx += 3;
  
                    __put_user(sigreturn_codes[idx+1], rc+1))
                        return 1;
  
+ rc_finish:
  #ifdef CONFIG_MMU
                if (cpsr & MODE32_BIT) {
                        struct mm_struct *mm = current->mm;
                         * the return code written onto the stack.
                         */
                        flush_icache_range((unsigned long)rc,
-                                          (unsigned long)(rc + 2));
+                                          (unsigned long)(rc + 3));
  
                        retcode = ((unsigned long)rc) + thumb;
                }
        regs->ARM_sp = (unsigned long)frame;
        regs->ARM_lr = retcode;
        regs->ARM_pc = handler;
+       if (fdpic)
+               regs->ARM_r9 = handler_fdpic_GOT;
        regs->ARM_cpsr = cpsr;
  
        return 0;
@@@ -614,10 -636,6 +637,10 @@@ do_work_pending(struct pt_regs *regs, u
         * Update the trace code with the current status.
         */
        trace_hardirqs_off();
 +
 +      /* Check valid user FS if needed */
 +      addr_limit_user_check();
 +
        do {
                if (likely(thread_flags & _TIF_NEED_RESCHED)) {
                        schedule();
diff --combined fs/binfmt_elf.c
index 73b01e474fdc630bcc71e6c883d59b5009b64502,89536811d8f8a606331990495e2f6206404040c0..e4f7ef8294ac459365e75e73c59891417be15e23
  #define user_siginfo_t siginfo_t
  #endif
  
+ /* That's for binfmt_elf_fdpic to deal with */
+ #ifndef elf_check_fdpic
+ #define elf_check_fdpic(ex) false
+ #endif
  static int load_elf_binary(struct linux_binprm *bprm);
  static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
                                int, int, unsigned long);
@@@ -252,7 -257,7 +257,7 @@@ create_elf_tables(struct linux_binprm *
        NEW_AUX_ENT(AT_EUID, from_kuid_munged(cred->user_ns, cred->euid));
        NEW_AUX_ENT(AT_GID, from_kgid_munged(cred->user_ns, cred->gid));
        NEW_AUX_ENT(AT_EGID, from_kgid_munged(cred->user_ns, cred->egid));
 -      NEW_AUX_ENT(AT_SECURE, security_bprm_secureexec(bprm));
 +      NEW_AUX_ENT(AT_SECURE, bprm->secureexec);
        NEW_AUX_ENT(AT_RANDOM, (elf_addr_t)(unsigned long)u_rand_bytes);
  #ifdef ELF_HWCAP2
        NEW_AUX_ENT(AT_HWCAP2, ELF_HWCAP2);
@@@ -409,7 -414,6 +414,7 @@@ static struct elf_phdr *load_elf_phdrs(
  {
        struct elf_phdr *elf_phdata = NULL;
        int retval, size, err = -1;
 +      loff_t pos = elf_ex->e_phoff;
  
        /*
         * If the size of this structure has changed, then punt, since
                goto out;
  
        /* Read in the program headers */
 -      retval = kernel_read(elf_file, elf_ex->e_phoff,
 -                           (char *)elf_phdata, size);
 +      retval = kernel_read(elf_file, elf_phdata, size, &pos);
        if (retval != size) {
                err = (retval < 0) ? retval : -EIO;
                goto out;
@@@ -541,7 -546,8 +546,8 @@@ static unsigned long load_elf_interp(st
        if (interp_elf_ex->e_type != ET_EXEC &&
            interp_elf_ex->e_type != ET_DYN)
                goto out;
-       if (!elf_check_arch(interp_elf_ex))
+       if (!elf_check_arch(interp_elf_ex) ||
+           elf_check_fdpic(interp_elf_ex))
                goto out;
        if (!interpreter->f_op->mmap)
                goto out;
@@@ -698,7 -704,6 +704,7 @@@ static int load_elf_binary(struct linux
                struct elfhdr interp_elf_ex;
        } *loc;
        struct arch_elf_state arch_state = INIT_ARCH_ELF_STATE;
 +      loff_t pos;
  
        loc = kmalloc(sizeof(*loc), GFP_KERNEL);
        if (!loc) {
                goto out;
        if (!elf_check_arch(&loc->elf_ex))
                goto out;
+       if (elf_check_fdpic(&loc->elf_ex))
+               goto out;
        if (!bprm->file->f_op->mmap)
                goto out;
  
                        if (!elf_interpreter)
                                goto out_free_ph;
  
 -                      retval = kernel_read(bprm->file, elf_ppnt->p_offset,
 -                                           elf_interpreter,
 -                                           elf_ppnt->p_filesz);
 +                      pos = elf_ppnt->p_offset;
 +                      retval = kernel_read(bprm->file, elf_interpreter,
 +                                           elf_ppnt->p_filesz, &pos);
                        if (retval != elf_ppnt->p_filesz) {
                                if (retval >= 0)
                                        retval = -EIO;
                        would_dump(bprm, interpreter);
  
                        /* Get the exec headers */
 -                      retval = kernel_read(interpreter, 0,
 -                                           (void *)&loc->interp_elf_ex,
 -                                           sizeof(loc->interp_elf_ex));
 +                      pos = 0;
 +                      retval = kernel_read(interpreter, &loc->interp_elf_ex,
 +                                           sizeof(loc->interp_elf_ex), &pos);
                        if (retval != sizeof(loc->interp_elf_ex)) {
                                if (retval >= 0)
                                        retval = -EIO;
                if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0)
                        goto out_free_dentry;
                /* Verify the interpreter has a valid arch */
-               if (!elf_check_arch(&loc->interp_elf_ex))
+               if (!elf_check_arch(&loc->interp_elf_ex) ||
+                   elf_check_fdpic(&loc->interp_elf_ex))
                        goto out_free_dentry;
  
                /* Load the interpreter program headers */
@@@ -1176,10 -1184,9 +1185,10 @@@ static int load_elf_library(struct fil
        unsigned long elf_bss, bss, len;
        int retval, error, i, j;
        struct elfhdr elf_ex;
 +      loff_t pos = 0;
  
        error = -ENOEXEC;
 -      retval = kernel_read(file, 0, (char *)&elf_ex, sizeof(elf_ex));
 +      retval = kernel_read(file, &elf_ex, sizeof(elf_ex), &pos);
        if (retval != sizeof(elf_ex))
                goto out;
  
        if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 ||
            !elf_check_arch(&elf_ex) || !file->f_op->mmap)
                goto out;
+       if (elf_check_fdpic(&elf_ex))
+               goto out;
  
        /* Now read in all of the header information */
  
  
        eppnt = elf_phdata;
        error = -ENOEXEC;
 -      retval = kernel_read(file, elf_ex.e_phoff, (char *)eppnt, j);
 +      pos =  elf_ex.e_phoff;
 +      retval = kernel_read(file, eppnt, j, &pos);
        if (retval != j)
                goto out_free_ph;
  
diff --combined fs/binfmt_elf_fdpic.c
index e70c039ac19067f1a0b490bf2ffe8d5c0ba52498,6a56dea1387661d420903591b4be2c508c72c76d..5429b035e249bfb6eb0d7887ccca0353535af952
@@@ -145,7 -145,6 +145,7 @@@ static int elf_fdpic_fetch_phdrs(struc
        struct elf32_phdr *phdr;
        unsigned long size;
        int retval, loop;
 +      loff_t pos = params->hdr.e_phoff;
  
        if (params->hdr.e_phentsize != sizeof(struct elf_phdr))
                return -ENOMEM;
        if (!params->phdrs)
                return -ENOMEM;
  
 -      retval = kernel_read(file, params->hdr.e_phoff,
 -                           (char *) params->phdrs, size);
 +      retval = kernel_read(file, params->phdrs, size, &pos);
        if (unlikely(retval != size))
                return retval < 0 ? retval : -ENOEXEC;
  
@@@ -199,7 -199,6 +199,7 @@@ static int load_elf_fdpic_binary(struc
        char *interpreter_name = NULL;
        int executable_stack;
        int retval, i;
 +      loff_t pos;
  
        kdebug("____ LOAD %d ____", current->pid);
  
                        if (!interpreter_name)
                                goto error;
  
 -                      retval = kernel_read(bprm->file,
 -                                           phdr->p_offset,
 -                                           interpreter_name,
 -                                           phdr->p_filesz);
 +                      pos = phdr->p_offset;
 +                      retval = kernel_read(bprm->file, interpreter_name,
 +                                           phdr->p_filesz, &pos);
                        if (unlikely(retval != phdr->p_filesz)) {
                                if (retval >= 0)
                                        retval = -ENOEXEC;
                         */
                        would_dump(bprm, interpreter);
  
 -                      retval = kernel_read(interpreter, 0, bprm->buf,
 -                                           BINPRM_BUF_SIZE);
 +                      pos = 0;
 +                      retval = kernel_read(interpreter, bprm->buf,
 +                                      BINPRM_BUF_SIZE, &pos);
                        if (unlikely(retval != BINPRM_BUF_SIZE)) {
                                if (retval >= 0)
                                        retval = -ENOEXEC;
                                 executable_stack);
        if (retval < 0)
                goto error;
+ #ifdef ARCH_HAS_SETUP_ADDITIONAL_PAGES
+       retval = arch_setup_additional_pages(bprm, !!interpreter_name);
+       if (retval < 0)
+               goto error;
+ #endif
  #endif
  
        /* load the executable and interpreter into memory */
@@@ -651,7 -655,7 +656,7 @@@ static int create_elf_fdpic_tables(stru
        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->egid));
 -      NEW_AUX_ENT(AT_SECURE,  security_bprm_secureexec(bprm));
 +      NEW_AUX_ENT(AT_SECURE,  bprm->secureexec);
        NEW_AUX_ENT(AT_EXECFN,  bprm->exec);
  
  #ifdef ARCH_DLINFO
@@@ -831,6 -835,9 +836,9 @@@ static int elf_fdpic_map_file(struct el
                        if (phdr->p_vaddr >= seg->p_vaddr &&
                            phdr->p_vaddr + phdr->p_memsz <=
                            seg->p_vaddr + seg->p_memsz) {
+                               Elf32_Dyn __user *dyn;
+                               Elf32_Sword d_tag;
                                params->dynamic_addr =
                                        (phdr->p_vaddr - seg->p_vaddr) +
                                        seg->addr;
                                        goto dynamic_error;
  
                                tmp = phdr->p_memsz / sizeof(Elf32_Dyn);
-                               if (((Elf32_Dyn *)
-                                    params->dynamic_addr)[tmp - 1].d_tag != 0)
+                               dyn = (Elf32_Dyn __user *)params->dynamic_addr;
+                               __get_user(d_tag, &dyn[tmp - 1].d_tag);
+                               if (d_tag != 0)
                                        goto dynamic_error;
                                break;
                        }