binfmt_elf_fdpic: fix /proc/<pid>/auxv
[linux-2.6-block.git] / fs / binfmt_elf_fdpic.c
index 3314249e867474ff81075b4e007030caaf0fcd2e..b799701454a96bd224d274533f45c92ec2a6861d 100644 (file)
@@ -505,8 +505,9 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
        char *k_platform, *k_base_platform;
        char __user *u_platform, *u_base_platform, *p;
        int loop;
-       int nr; /* reset for each csp adjustment */
        unsigned long flags = 0;
+       int ei_index;
+       elf_addr_t *elf_info;
 
 #ifdef CONFIG_MMU
        /* In some cases (e.g. Hyper-Threading), we want to avoid L1 evictions
@@ -601,44 +602,24 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
        csp -= sp & 15UL;
        sp -= sp & 15UL;
 
-       /* put the ELF interpreter info on the stack */
-#define NEW_AUX_ENT(id, val)                                           \
-       do {                                                            \
-               struct { unsigned long _id, _val; } __user *ent, v;     \
-                                                                       \
-               ent = (void __user *) csp;                              \
-               v._id = (id);                                           \
-               v._val = (val);                                         \
-               if (copy_to_user(ent + nr, &v, sizeof(v)))              \
-                       return -EFAULT;                                 \
-               nr++;                                                   \
+       /* Create the ELF interpreter info */
+       elf_info = (elf_addr_t *)mm->saved_auxv;
+       /* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */
+#define NEW_AUX_ENT(id, val) \
+       do { \
+               *elf_info++ = id; \
+               *elf_info++ = val; \
        } while (0)
 
-       nr = 0;
-       csp -= 2 * sizeof(unsigned long);
-       NEW_AUX_ENT(AT_NULL, 0);
-       if (k_platform) {
-               nr = 0;
-               csp -= 2 * sizeof(unsigned long);
-               NEW_AUX_ENT(AT_PLATFORM,
-                           (elf_addr_t) (unsigned long) u_platform);
-       }
-
-       if (k_base_platform) {
-               nr = 0;
-               csp -= 2 * sizeof(unsigned long);
-               NEW_AUX_ENT(AT_BASE_PLATFORM,
-                           (elf_addr_t) (unsigned long) u_base_platform);
-       }
-
-       if (bprm->have_execfd) {
-               nr = 0;
-               csp -= 2 * sizeof(unsigned long);
-               NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
-       }
-
-       nr = 0;
-       csp -= DLINFO_ITEMS * 2 * sizeof(unsigned long);
+#ifdef ARCH_DLINFO
+       /*
+        * ARCH_DLINFO must come first so PPC can do its special alignment of
+        * AUXV.
+        * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in
+        * ARCH_DLINFO changes
+        */
+       ARCH_DLINFO;
+#endif
        NEW_AUX_ENT(AT_HWCAP,   ELF_HWCAP);
 #ifdef ELF_HWCAP2
        NEW_AUX_ENT(AT_HWCAP2,  ELF_HWCAP2);
@@ -659,17 +640,29 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
        NEW_AUX_ENT(AT_EGID,    (elf_addr_t) from_kgid_munged(cred->user_ns, cred->egid));
        NEW_AUX_ENT(AT_SECURE,  bprm->secureexec);
        NEW_AUX_ENT(AT_EXECFN,  bprm->exec);
+       if (k_platform)
+               NEW_AUX_ENT(AT_PLATFORM,
+                           (elf_addr_t)(unsigned long)u_platform);
+       if (k_base_platform)
+               NEW_AUX_ENT(AT_BASE_PLATFORM,
+                           (elf_addr_t)(unsigned long)u_base_platform);
+       if (bprm->have_execfd)
+               NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
+#undef NEW_AUX_ENT
+       /* AT_NULL is zero; clear the rest too */
+       memset(elf_info, 0, (char *)mm->saved_auxv +
+              sizeof(mm->saved_auxv) - (char *)elf_info);
 
-#ifdef ARCH_DLINFO
-       nr = 0;
-       csp -= AT_VECTOR_SIZE_ARCH * 2 * sizeof(unsigned long);
+       /* And advance past the AT_NULL entry.  */
+       elf_info += 2;
 
-       /* ARCH_DLINFO must come last so platform specific code can enforce
-        * special alignment requirements on the AUXV if necessary (eg. PPC).
-        */
-       ARCH_DLINFO;
-#endif
-#undef NEW_AUX_ENT
+       ei_index = elf_info - (elf_addr_t *)mm->saved_auxv;
+       csp -= ei_index * sizeof(elf_addr_t);
+
+       /* Put the elf_info on the stack in the right place.  */
+       if (copy_to_user((void __user *)csp, mm->saved_auxv,
+                        ei_index * sizeof(elf_addr_t)))
+               return -EFAULT;
 
        /* allocate room for argv[] and envv[] */
        csp -= (bprm->envc + 1) * sizeof(elf_caddr_t);