Re: /proc/${pid}/auxv

From: David S. Miller
Date: Mon Apr 10 2006 - 01:52:31 EST


From: "David S. Miller" <davem@xxxxxxxxxxxxx>
Date: Sun, 02 Apr 2006 01:14:30 -0800 (PST)

> I think this needs to be handled by the binfmt of the current thread
> because the size of the auxv words themselves is binfmt dependent.
>
> For core file output, the binfmt handler does the auxv writing and
> it interpretes the type of the entry words correctly.
>
> So what happens now is that, for a 32-bit ELF binary executing on
> 64-bit kernel, /proc/${pid}/auxv will report an extra AT_NULL entry or
> garbage at the end (because it's interpreting 32-bit words as 64-bit
> words when trying to find the AT_NULL that ends the auxv vector).
> Whereas core file generation will find the end accurately and place
> only the exact number of AUXV entries into the core file.

Here is a patch that implements the fix since nobody has gotten
to it yet:

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index 537893a..16aa604 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -63,6 +63,7 @@ static int elf_core_dump(long signr, str
#else
#define elf_core_dump NULL
#endif
+static int elf_auxv_size(struct mm_struct *mm);

#if ELF_EXEC_PAGESIZE > PAGE_SIZE
# define ELF_MIN_ALIGN ELF_EXEC_PAGESIZE
@@ -83,6 +84,7 @@ static struct linux_binfmt elf_format =
.load_binary = load_elf_binary,
.load_shlib = load_elf_library,
.core_dump = elf_core_dump,
+ .auxv_size = elf_auxv_size,
.min_coredump = ELF_EXEC_PAGESIZE
};

@@ -1672,6 +1674,18 @@ #undef NUM_NOTES
}

#endif /* USE_ELF_CORE_DUMP */
+
+static int elf_auxv_size(struct mm_struct *mm)
+{
+ elf_addr_t *auxv = (elf_addr_t *) mm->saved_auxv;
+ int sz = 0;
+
+ do
+ sz += 2;
+ while (auxv[sz - 2] != AT_NULL);
+
+ return sz * sizeof (elf_addr_t);
+}

static int __init init_elf_binfmt(void)
{
diff --git a/fs/proc/base.c b/fs/proc/base.c
index a3a3eec..4810e89 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -466,11 +466,8 @@ static int proc_pid_auxv(struct task_str
int res = 0;
struct mm_struct *mm = get_task_mm(task);
if (mm) {
- unsigned int nwords = 0;
- do
- nwords += 2;
- while (mm->saved_auxv[nwords - 2] != 0); /* AT_NULL */
- res = nwords * sizeof(mm->saved_auxv[0]);
+ if (task->binfmt && task->binfmt->auxv_size)
+ res = task->binfmt->auxv_size(mm);
if (res > PAGE_SIZE)
res = PAGE_SIZE;
memcpy(buffer, mm->saved_auxv, res);
diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
index c1e82c5..390a879 100644
--- a/include/linux/binfmts.h
+++ b/include/linux/binfmts.h
@@ -52,12 +52,14 @@ #define BINPRM_FLAGS_EXECFD (1 << BINPRM
* This structure defines the functions that are used to load the binary formats that
* linux accepts.
*/
+struct task_struct;
struct linux_binfmt {
struct linux_binfmt * next;
struct module *module;
int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
int (*load_shlib)(struct file *);
int (*core_dump)(long signr, struct pt_regs * regs, struct file * file);
+ int (*auxv_size)(struct mm_struct *mm);
unsigned long min_coredump; /* minimal dump size */
};

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/