bus: mhi: fix doubled words and struct image_info kernel-doc
[linux-2.6-microblaze.git] / fs / binfmt_elf.c
index 25d489b..13d0539 100644 (file)
 #include <linux/sched/coredump.h>
 #include <linux/sched/task_stack.h>
 #include <linux/sched/cputime.h>
+#include <linux/sizes.h>
+#include <linux/types.h>
 #include <linux/cred.h>
 #include <linux/dax.h>
 #include <linux/uaccess.h>
 #include <asm/param.h>
 #include <asm/page.h>
 
+#ifndef ELF_COMPAT
+#define ELF_COMPAT 0
+#endif
+
 #ifndef user_long_t
 #define user_long_t long
 #endif
@@ -202,7 +208,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
                size_t len = strlen(k_platform) + 1;
 
                u_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
-               if (__copy_to_user(u_platform, k_platform, len))
+               if (copy_to_user(u_platform, k_platform, len))
                        return -EFAULT;
        }
 
@@ -215,7 +221,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
                size_t len = strlen(k_base_platform) + 1;
 
                u_base_platform = (elf_addr_t __user *)STACK_ALLOC(p, len);
-               if (__copy_to_user(u_base_platform, k_base_platform, len))
+               if (copy_to_user(u_base_platform, k_base_platform, len))
                        return -EFAULT;
        }
 
@@ -225,7 +231,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
        get_random_bytes(k_rand_bytes, sizeof(k_rand_bytes));
        u_rand_bytes = (elf_addr_t __user *)
                       STACK_ALLOC(p, sizeof(k_rand_bytes));
-       if (__copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes)))
+       if (copy_to_user(u_rand_bytes, k_rand_bytes, sizeof(k_rand_bytes)))
                return -EFAULT;
 
        /* Create the ELF interpreter info */
@@ -273,8 +279,8 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
                NEW_AUX_ENT(AT_BASE_PLATFORM,
                            (elf_addr_t)(unsigned long)u_base_platform);
        }
-       if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
-               NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
+       if (bprm->have_execfd) {
+               NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
        }
 #undef NEW_AUX_ENT
        /* AT_NULL is zero; clear the rest too */
@@ -308,21 +314,21 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
                return -EFAULT;
 
        /* Now, let's put argc (and argv, envp if appropriate) on the stack */
-       if (__put_user(argc, sp++))
+       if (put_user(argc, sp++))
                return -EFAULT;
 
        /* Populate list of argv pointers back to argv strings. */
        p = mm->arg_end = mm->arg_start;
        while (argc-- > 0) {
                size_t len;
-               if (__put_user((elf_addr_t)p, sp++))
+               if (put_user((elf_addr_t)p, sp++))
                        return -EFAULT;
                len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
                if (!len || len > MAX_ARG_STRLEN)
                        return -EINVAL;
                p += len;
        }
-       if (__put_user(0, sp++))
+       if (put_user(0, sp++))
                return -EFAULT;
        mm->arg_end = p;
 
@@ -330,14 +336,14 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
        mm->env_end = mm->env_start = p;
        while (envc-- > 0) {
                size_t len;
-               if (__put_user((elf_addr_t)p, sp++))
+               if (put_user((elf_addr_t)p, sp++))
                        return -EFAULT;
                len = strnlen_user((void __user *)p, MAX_ARG_STRLEN);
                if (!len || len > MAX_ARG_STRLEN)
                        return -EINVAL;
                p += len;
        }
-       if (__put_user(0, sp++))
+       if (put_user(0, sp++))
                return -EFAULT;
        mm->env_end = p;
 
@@ -347,8 +353,6 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
        return 0;
 }
 
-#ifndef elf_map
-
 static unsigned long elf_map(struct file *filep, unsigned long addr,
                const struct elf_phdr *eppnt, int prot, int type,
                unsigned long total_size)
@@ -388,8 +392,6 @@ static unsigned long elf_map(struct file *filep, unsigned long addr,
        return(map_addr);
 }
 
-#endif /* !elf_map */
-
 static unsigned long total_mapping_size(const struct elf_phdr *cmds, int nr)
 {
        int i, first_idx = -1, last_idx = -1;
@@ -539,7 +541,8 @@ static inline int arch_check_elf(struct elfhdr *ehdr, bool has_interp,
 
 #endif /* !CONFIG_ARCH_BINFMT_ELF_STATE */
 
-static inline int make_prot(u32 p_flags)
+static inline int make_prot(u32 p_flags, struct arch_elf_state *arch_state,
+                           bool has_interp, bool is_interp)
 {
        int prot = 0;
 
@@ -549,7 +552,8 @@ static inline int make_prot(u32 p_flags)
                prot |= PROT_WRITE;
        if (p_flags & PF_X)
                prot |= PROT_EXEC;
-       return prot;
+
+       return arch_elf_adjust_prot(prot, arch_state, has_interp, is_interp);
 }
 
 /* This is much more generalized than the library routine read function,
@@ -559,7 +563,8 @@ static inline int make_prot(u32 p_flags)
 
 static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
                struct file *interpreter,
-               unsigned long no_base, struct elf_phdr *interp_elf_phdata)
+               unsigned long no_base, struct elf_phdr *interp_elf_phdata,
+               struct arch_elf_state *arch_state)
 {
        struct elf_phdr *eppnt;
        unsigned long load_addr = 0;
@@ -591,7 +596,8 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
        for (i = 0; i < interp_elf_ex->e_phnum; i++, eppnt++) {
                if (eppnt->p_type == PT_LOAD) {
                        int elf_type = MAP_PRIVATE | MAP_DENYWRITE;
-                       int elf_prot = make_prot(eppnt->p_flags);
+                       int elf_prot = make_prot(eppnt->p_flags, arch_state,
+                                                true, true);
                        unsigned long vaddr = 0;
                        unsigned long k, map_addr;
 
@@ -682,6 +688,111 @@ out:
  * libraries.  There is no binary dependent code anywhere else.
  */
 
+static int parse_elf_property(const char *data, size_t *off, size_t datasz,
+                             struct arch_elf_state *arch,
+                             bool have_prev_type, u32 *prev_type)
+{
+       size_t o, step;
+       const struct gnu_property *pr;
+       int ret;
+
+       if (*off == datasz)
+               return -ENOENT;
+
+       if (WARN_ON_ONCE(*off > datasz || *off % ELF_GNU_PROPERTY_ALIGN))
+               return -EIO;
+       o = *off;
+       datasz -= *off;
+
+       if (datasz < sizeof(*pr))
+               return -ENOEXEC;
+       pr = (const struct gnu_property *)(data + o);
+       o += sizeof(*pr);
+       datasz -= sizeof(*pr);
+
+       if (pr->pr_datasz > datasz)
+               return -ENOEXEC;
+
+       WARN_ON_ONCE(o % ELF_GNU_PROPERTY_ALIGN);
+       step = round_up(pr->pr_datasz, ELF_GNU_PROPERTY_ALIGN);
+       if (step > datasz)
+               return -ENOEXEC;
+
+       /* Properties are supposed to be unique and sorted on pr_type: */
+       if (have_prev_type && pr->pr_type <= *prev_type)
+               return -ENOEXEC;
+       *prev_type = pr->pr_type;
+
+       ret = arch_parse_elf_property(pr->pr_type, data + o,
+                                     pr->pr_datasz, ELF_COMPAT, arch);
+       if (ret)
+               return ret;
+
+       *off = o + step;
+       return 0;
+}
+
+#define NOTE_DATA_SZ SZ_1K
+#define GNU_PROPERTY_TYPE_0_NAME "GNU"
+#define NOTE_NAME_SZ (sizeof(GNU_PROPERTY_TYPE_0_NAME))
+
+static int parse_elf_properties(struct file *f, const struct elf_phdr *phdr,
+                               struct arch_elf_state *arch)
+{
+       union {
+               struct elf_note nhdr;
+               char data[NOTE_DATA_SZ];
+       } note;
+       loff_t pos;
+       ssize_t n;
+       size_t off, datasz;
+       int ret;
+       bool have_prev_type;
+       u32 prev_type;
+
+       if (!IS_ENABLED(CONFIG_ARCH_USE_GNU_PROPERTY) || !phdr)
+               return 0;
+
+       /* load_elf_binary() shouldn't call us unless this is true... */
+       if (WARN_ON_ONCE(phdr->p_type != PT_GNU_PROPERTY))
+               return -ENOEXEC;
+
+       /* If the properties are crazy large, that's too bad (for now): */
+       if (phdr->p_filesz > sizeof(note))
+               return -ENOEXEC;
+
+       pos = phdr->p_offset;
+       n = kernel_read(f, &note, phdr->p_filesz, &pos);
+
+       BUILD_BUG_ON(sizeof(note) < sizeof(note.nhdr) + NOTE_NAME_SZ);
+       if (n < 0 || n < sizeof(note.nhdr) + NOTE_NAME_SZ)
+               return -EIO;
+
+       if (note.nhdr.n_type != NT_GNU_PROPERTY_TYPE_0 ||
+           note.nhdr.n_namesz != NOTE_NAME_SZ ||
+           strncmp(note.data + sizeof(note.nhdr),
+                   GNU_PROPERTY_TYPE_0_NAME, n - sizeof(note.nhdr)))
+               return -ENOEXEC;
+
+       off = round_up(sizeof(note.nhdr) + NOTE_NAME_SZ,
+                      ELF_GNU_PROPERTY_ALIGN);
+       if (off > n)
+               return -ENOEXEC;
+
+       if (note.nhdr.n_descsz > n - off)
+               return -ENOEXEC;
+       datasz = off + note.nhdr.n_descsz;
+
+       have_prev_type = false;
+       do {
+               ret = parse_elf_property(note.data, &off, datasz, arch,
+                                        have_prev_type, &prev_type);
+               have_prev_type = true;
+       } while (!ret);
+
+       return ret == -ENOENT ? 0 : ret;
+}
+
 static int load_elf_binary(struct linux_binprm *bprm)
 {
        struct file *interpreter = NULL; /* to shut gcc up */
@@ -689,6 +800,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
        int load_addr_set = 0;
        unsigned long error;
        struct elf_phdr *elf_ppnt, *elf_phdata, *interp_elf_phdata = NULL;
+       struct elf_phdr *elf_property_phdata = NULL;
        unsigned long elf_bss, elf_brk;
        int bss_prot = 0;
        int retval, i;
@@ -726,6 +838,11 @@ static int load_elf_binary(struct linux_binprm *bprm)
        for (i = 0; i < elf_ex->e_phnum; i++, elf_ppnt++) {
                char *elf_interpreter;
 
+               if (elf_ppnt->p_type == PT_GNU_PROPERTY) {
+                       elf_property_phdata = elf_ppnt;
+                       continue;
+               }
+
                if (elf_ppnt->p_type != PT_INTERP)
                        continue;
 
@@ -819,9 +936,14 @@ out_free_interp:
                        goto out_free_dentry;
 
                /* Pass PT_LOPROC..PT_HIPROC headers to arch code */
+               elf_property_phdata = NULL;
                elf_ppnt = interp_elf_phdata;
                for (i = 0; i < interp_elf_ex->e_phnum; i++, elf_ppnt++)
                        switch (elf_ppnt->p_type) {
+                       case PT_GNU_PROPERTY:
+                               elf_property_phdata = elf_ppnt;
+                               break;
+
                        case PT_LOPROC ... PT_HIPROC:
                                retval = arch_elf_pt_proc(interp_elf_ex,
                                                          elf_ppnt, interpreter,
@@ -832,6 +954,11 @@ out_free_interp:
                        }
        }
 
+       retval = parse_elf_properties(interpreter ?: bprm->file,
+                                     elf_property_phdata, &arch_state);
+       if (retval)
+               goto out_free_dentry;
+
        /*
         * Allow arch code to reject the ELF at this point, whilst it's
         * still possible to return an error to the code that invoked
@@ -844,7 +971,7 @@ out_free_interp:
                goto out_free_dentry;
 
        /* Flush all traces of the currently running executable */
-       retval = flush_old_exec(bprm);
+       retval = begin_new_exec(bprm);
        if (retval)
                goto out_free_dentry;
 
@@ -858,7 +985,6 @@ out_free_interp:
                current->flags |= PF_RANDOMIZE;
 
        setup_new_exec(bprm);
-       install_exec_creds(bprm);
 
        /* Do this so that we can load the interpreter, if need be.  We will
           change some of these later */
@@ -913,7 +1039,8 @@ out_free_interp:
                        }
                }
 
-               elf_prot = make_prot(elf_ppnt->p_flags);
+               elf_prot = make_prot(elf_ppnt->p_flags, &arch_state,
+                                    !!interpreter, false);
 
                elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
 
@@ -1056,7 +1183,8 @@ out_free_interp:
        if (interpreter) {
                elf_entry = load_elf_interp(interp_elf_ex,
                                            interpreter,
-                                           load_bias, interp_elf_phdata);
+                                           load_bias, interp_elf_phdata,
+                                           &arch_state);
                if (!IS_ERR((void *)elf_entry)) {
                        /*
                         * load_elf_interp() returns relocation
@@ -1355,7 +1483,6 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
            vma->vm_pgoff == 0 && (vma->vm_flags & VM_READ)) {
                u32 __user *header = (u32 __user *) vma->vm_start;
                u32 word;
-               mm_segment_t fs = get_fs();
                /*
                 * Doing it this way gets the constant folded by GCC.
                 */
@@ -1368,14 +1495,8 @@ static unsigned long vma_dump_size(struct vm_area_struct *vma,
                magic.elfmag[EI_MAG1] = ELFMAG1;
                magic.elfmag[EI_MAG2] = ELFMAG2;
                magic.elfmag[EI_MAG3] = ELFMAG3;
-               /*
-                * Switch to the user "segment" for get_user(),
-                * then put back what elf_core_dump() had in place.
-                */
-               set_fs(USER_DS);
                if (unlikely(get_user(word, header)))
                        word = 0;
-               set_fs(fs);
                if (word == magic.cmp)
                        return PAGE_SIZE;
        }
@@ -1556,10 +1677,7 @@ static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm)
 static void fill_siginfo_note(struct memelfnote *note, user_siginfo_t *csigdata,
                const kernel_siginfo_t *siginfo)
 {
-       mm_segment_t old_fs = get_fs();
-       set_fs(KERNEL_DS);
-       copy_siginfo_to_user((user_siginfo_t __user *) csigdata, siginfo);
-       set_fs(old_fs);
+       copy_siginfo_to_external(csigdata, siginfo);
        fill_note(note, "CORE", NT_SIGINFO, sizeof(*csigdata), csigdata);
 }
 
@@ -1703,7 +1821,7 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
                                 long signr, size_t *total)
 {
        unsigned int i;
-       unsigned int regset0_size = regset_size(t->task, &view->regsets[0]);
+       int regset0_size;
 
        /*
         * NT_PRSTATUS is the one special case, because the regset data
@@ -1712,8 +1830,10 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
         * We assume that regset 0 is NT_PRSTATUS.
         */
        fill_prstatus(&t->prstatus, t->task, signr);
-       (void) view->regsets[0].get(t->task, &view->regsets[0], 0, regset0_size,
-                                   &t->prstatus.pr_reg, NULL);
+       regset0_size = regset_get(t->task, &view->regsets[0],
+                  sizeof(t->prstatus.pr_reg), &t->prstatus.pr_reg);
+       if (regset0_size < 0)
+               return 0;
 
        fill_note(&t->notes[0], "CORE", NT_PRSTATUS,
                  PRSTATUS_SIZE(t->prstatus, regset0_size), &t->prstatus);
@@ -1728,32 +1848,28 @@ static int fill_thread_core_info(struct elf_thread_core_info *t,
         */
        for (i = 1; i < view->n; ++i) {
                const struct user_regset *regset = &view->regsets[i];
+               int note_type = regset->core_note_type;
+               bool is_fpreg = note_type == NT_PRFPREG;
+               void *data;
+               int ret;
+
                do_thread_regset_writeback(t->task, regset);
-               if (regset->core_note_type && regset->get &&
-                   (!regset->active || regset->active(t->task, regset) > 0)) {
-                       int ret;
-                       size_t size = regset_size(t->task, regset);
-                       void *data = kzalloc(size, GFP_KERNEL);
-                       if (unlikely(!data))
-                               return 0;
-                       ret = regset->get(t->task, regset,
-                                         0, size, data, NULL);
-                       if (unlikely(ret))
-                               kfree(data);
-                       else {
-                               if (regset->core_note_type != NT_PRFPREG)
-                                       fill_note(&t->notes[i], "LINUX",
-                                                 regset->core_note_type,
-                                                 size, data);
-                               else {
-                                       SET_PR_FPVALID(&t->prstatus,
-                                                       1, regset0_size);
-                                       fill_note(&t->notes[i], "CORE",
-                                                 NT_PRFPREG, size, data);
-                               }
-                               *total += notesize(&t->notes[i]);
-                       }
-               }
+               if (!note_type) // not for coredumps
+                       continue;
+               if (regset->active && regset->active(t->task, regset) <= 0)
+                       continue;
+
+               ret = regset_get_alloc(t->task, regset, ~0U, &data);
+               if (ret < 0)
+                       continue;
+
+               if (is_fpreg)
+                       SET_PR_FPVALID(&t->prstatus, 1, regset0_size);
+
+               fill_note(&t->notes[i], is_fpreg ? "CORE" : "LINUX",
+                         note_type, ret, data);
+
+               *total += notesize(&t->notes[i]);
        }
 
        return 1;
@@ -1922,9 +2038,6 @@ struct elf_thread_status
        struct elf_prstatus prstatus;   /* NT_PRSTATUS */
        elf_fpregset_t fpu;             /* NT_PRFPREG */
        struct task_struct *thread;
-#ifdef ELF_CORE_COPY_XFPREGS
-       elf_fpxregset_t xfpu;           /* ELF_CORE_XFPREG_TYPE */
-#endif
        struct memelfnote notes[3];
        int num_notes;
 };
@@ -1955,15 +2068,6 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
                t->num_notes++;
                sz += notesize(&t->notes[1]);
        }
-
-#ifdef ELF_CORE_COPY_XFPREGS
-       if (elf_core_copy_task_xfpregs(p, &t->xfpu)) {
-               fill_note(&t->notes[2], "LINUX", ELF_CORE_XFPREG_TYPE,
-                         sizeof(t->xfpu), &t->xfpu);
-               t->num_notes++;
-               sz += notesize(&t->notes[2]);
-       }
-#endif 
        return sz;
 }
 
@@ -1974,9 +2078,6 @@ struct elf_note_info {
        struct elf_prpsinfo *psinfo;    /* NT_PRPSINFO */
        struct list_head thread_list;
        elf_fpregset_t *fpu;
-#ifdef ELF_CORE_COPY_XFPREGS
-       elf_fpxregset_t *xfpu;
-#endif
        user_siginfo_t csigdata;
        int thread_status_size;
        int numnote;
@@ -2000,11 +2101,6 @@ static int elf_note_info_init(struct elf_note_info *info)
        info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL);
        if (!info->fpu)
                return 0;
-#ifdef ELF_CORE_COPY_XFPREGS
-       info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL);
-       if (!info->xfpu)
-               return 0;
-#endif
        return 1;
 }
 
@@ -2068,13 +2164,6 @@ static int fill_note_info(struct elfhdr *elf, int phdrs,
        if (info->prstatus->pr_fpvalid)
                fill_note(info->notes + info->numnote++,
                          "CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu);
-#ifdef ELF_CORE_COPY_XFPREGS
-       if (elf_core_copy_task_xfpregs(current, info->xfpu))
-               fill_note(info->notes + info->numnote++,
-                         "LINUX", ELF_CORE_XFPREG_TYPE,
-                         sizeof(*info->xfpu), info->xfpu);
-#endif
-
        return 1;
 }
 
@@ -2127,9 +2216,6 @@ static void free_note_info(struct elf_note_info *info)
        kfree(info->psinfo);
        kfree(info->notes);
        kfree(info->fpu);
-#ifdef ELF_CORE_COPY_XFPREGS
-       kfree(info->xfpu);
-#endif
 }
 
 #endif
@@ -2186,7 +2272,6 @@ 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;
-       mm_segment_t fs;
        int segs, i;
        size_t vma_data_size = 0;
        struct vm_area_struct *vma, *gate_vma;
@@ -2235,13 +2320,10 @@ static int elf_core_dump(struct coredump_params *cprm)
         * notes.  This also sets up the file header.
         */
        if (!fill_note_info(&elf, e_phnum, &info, cprm->siginfo, cprm->regs))
-               goto cleanup;
+               goto end_coredump;
 
        has_dumped = 1;
 
-       fs = get_fs();
-       set_fs(KERNEL_DS);
-
        offset += sizeof(elf);                          /* Elf header */
        offset += segs * sizeof(struct elf_phdr);       /* Program headers */
 
@@ -2369,9 +2451,6 @@ static int elf_core_dump(struct coredump_params *cprm)
        }
 
 end_coredump:
-       set_fs(fs);
-
-cleanup:
        free_note_info(&info);
        kfree(shdr4extnum);
        kvfree(vma_filesz);