]> git.ipfire.org Git - thirdparty/linux.git/blobdiff - fs/binfmt_elf.c
Merge tag 'pm-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
[thirdparty/linux.git] / fs / binfmt_elf.c
index 25d489bc9453330697b70af11bfa7c077883321d..8945671fe0e53141d98dada9354eb01a07df1baf 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
@@ -539,7 +545,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 +556,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 +567,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 +600,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 +692,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 +804,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 +842,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 +940,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 +958,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
@@ -913,7 +1044,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 +1188,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 +1488,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 +1500,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 +1682,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);
 }
 
@@ -2186,7 +2309,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 +2357,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 +2488,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);