]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - fs/binfmt_elf.c
Merge tag 'random_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso...
[karo-tx-linux.git] / fs / binfmt_elf.c
index 5075fd5c62c86d93b30cb413f791d1810157b57c..879ff9c7ffd01a0f1f2d6a7e92c32926af5ef4c2 100644 (file)
@@ -163,8 +163,6 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        unsigned long p = bprm->p;
        int argc = bprm->argc;
        int envc = bprm->envc;
-       elf_addr_t __user *argv;
-       elf_addr_t __user *envp;
        elf_addr_t __user *sp;
        elf_addr_t __user *u_platform;
        elf_addr_t __user *u_base_platform;
@@ -304,38 +302,38 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
        /* Now, let's put argc (and argv, envp if appropriate) on the stack */
        if (__put_user(argc, sp++))
                return -EFAULT;
-       argv = sp;
-       envp = argv + argc + 1;
 
-       /* Populate argv and envp */
+       /* Populate list of argv pointers back to argv strings. */
        p = current->mm->arg_end = current->mm->arg_start;
        while (argc-- > 0) {
                size_t len;
-               if (__put_user((elf_addr_t)p, argv++))
+               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, argv))
+       if (__put_user(0, sp++))
                return -EFAULT;
-       current->mm->arg_end = current->mm->env_start = p;
+       current->mm->arg_end = p;
+
+       /* Populate list of envp pointers back to envp strings. */
+       current->mm->env_end = current->mm->env_start = p;
        while (envc-- > 0) {
                size_t len;
-               if (__put_user((elf_addr_t)p, envp++))
+               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, envp))
+       if (__put_user(0, sp++))
                return -EFAULT;
        current->mm->env_end = p;
 
        /* Put the elf_info on the stack in the right place.  */
-       sp = (elf_addr_t __user *)envp + 1;
        if (copy_to_user(sp, elf_info, ei_index * sizeof(elf_addr_t)))
                return -EFAULT;
        return 0;
@@ -927,17 +925,60 @@ static int load_elf_binary(struct linux_binprm *bprm)
                elf_flags = MAP_PRIVATE | MAP_DENYWRITE | MAP_EXECUTABLE;
 
                vaddr = elf_ppnt->p_vaddr;
+               /*
+                * If we are loading ET_EXEC or we have already performed
+                * the ET_DYN load_addr calculations, proceed normally.
+                */
                if (loc->elf_ex.e_type == ET_EXEC || load_addr_set) {
                        elf_flags |= MAP_FIXED;
                } else if (loc->elf_ex.e_type == ET_DYN) {
-                       /* Try and get dynamic programs out of the way of the
-                        * default mmap base, as well as whatever program they
-                        * might try to exec.  This is because the brk will
-                        * follow the loader, and is not movable.  */
-                       load_bias = ELF_ET_DYN_BASE - vaddr;
-                       if (current->flags & PF_RANDOMIZE)
-                               load_bias += arch_mmap_rnd();
-                       load_bias = ELF_PAGESTART(load_bias);
+                       /*
+                        * This logic is run once for the first LOAD Program
+                        * Header for ET_DYN binaries to calculate the
+                        * randomization (load_bias) for all the LOAD
+                        * Program Headers, and to calculate the entire
+                        * size of the ELF mapping (total_size). (Note that
+                        * load_addr_set is set to true later once the
+                        * initial mapping is performed.)
+                        *
+                        * There are effectively two types of ET_DYN
+                        * binaries: programs (i.e. PIE: ET_DYN with INTERP)
+                        * and loaders (ET_DYN without INTERP, since they
+                        * _are_ the ELF interpreter). The loaders must
+                        * be loaded away from programs since the program
+                        * may otherwise collide with the loader (especially
+                        * for ET_EXEC which does not have a randomized
+                        * position). For example to handle invocations of
+                        * "./ld.so someprog" to test out a new version of
+                        * the loader, the subsequent program that the
+                        * loader loads must avoid the loader itself, so
+                        * they cannot share the same load range. Sufficient
+                        * room for the brk must be allocated with the
+                        * loader as well, since brk must be available with
+                        * the loader.
+                        *
+                        * Therefore, programs are loaded offset from
+                        * ELF_ET_DYN_BASE and loaders are loaded into the
+                        * independently randomized mmap region (0 load_bias
+                        * without MAP_FIXED).
+                        */
+                       if (elf_interpreter) {
+                               load_bias = ELF_ET_DYN_BASE;
+                               if (current->flags & PF_RANDOMIZE)
+                                       load_bias += arch_mmap_rnd();
+                               elf_flags |= MAP_FIXED;
+                       } else
+                               load_bias = 0;
+
+                       /*
+                        * Since load_bias is used for all subsequent loading
+                        * calculations, we must lower it by the first vaddr
+                        * so that the remaining calculations based on the
+                        * ELF vaddrs will be correctly offset. The result
+                        * is then page aligned.
+                        */
+                       load_bias = ELF_PAGESTART(load_bias - vaddr);
+
                        total_size = total_mapping_size(elf_phdata,
                                                        loc->elf_ex.e_phnum);
                        if (!total_size) {