net: cpts: convert to timecounter adjtime.
[cascardo/linux.git] / fs / binfmt_elf.c
index 3a6175f..02b1691 100644 (file)
@@ -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);