2007-05-04 14:50:36

by Quentin Godfroy

[permalink] [raw]
Subject: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On a dynamic ELF executable, the current kernel loader gives to the
interpreter (in the AUXV vector) the AT_PHDR argument as :
offset_of_phdr_in_file + first address.

It can be wrong for an executable where the program headers are not located
in the first loaded segment.

This patch corrects the behaviour.

Signed-off-by: Quentin Godfroy <[email protected]>
---
Here is an example of such an ELF executable which the current code
fails on :
ftp://quatramaran.ens.fr/pub/godfroy/addrpath/broken-sample

--- linux-2.6.21.1/fs/binfmt_elf.c 2007-05-04 03:20:00.000000000 -0400
+++ linux-2.6.21.1-patch/fs/binfmt_elf.c 2007-05-04 08:02:18.000000000 -0400
@@ -134,6 +134,7 @@ static int padzero(unsigned long elf_bss
static int
create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
int interp_aout, unsigned long load_addr,
+ unsigned long phdr_addr,
unsigned long interp_load_addr)
{
unsigned long p = bprm->p;
@@ -190,7 +191,7 @@ create_elf_tables(struct linux_binprm *b
NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
- NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
+ NEW_AUX_ENT(AT_PHDR, phdr_addr);
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
NEW_AUX_ENT(AT_BASE, interp_load_addr);
@@ -529,7 +530,7 @@ static unsigned long randomize_stack_top
static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
{
struct file *interpreter = NULL; /* to shut gcc up */
- unsigned long load_addr = 0, load_bias = 0;
+ unsigned long load_addr = 0, load_bias = 0, phdr_addr = 0;
int load_addr_set = 0;
char * elf_interpreter = NULL;
unsigned int interpreter_type = INTERPRETER_NONE;
@@ -718,6 +719,16 @@ static int load_elf_binary(struct linux_
break;
}

+ elf_ppnt = elf_phdata;
+ for (i = 0; i< loc->elf_ex.e_phnum; i++, elf_ppnt++)
+ if (elf_ppnt->p_type == PT_PHDR) {
+ phdr_addr = elf_ppnt->p_vaddr;
+ break;
+ }
+ retval = -ENOEXEC;
+ if (!phdr_addr)
+ goto out_free_dentry;
+
/* Some simple consistency checks for the interpreter */
if (elf_interpreter) {
interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
@@ -987,7 +998,7 @@ static int load_elf_binary(struct linux_
current->flags &= ~PF_FORKNOEXEC;
create_elf_tables(bprm, &loc->elf_ex,
(interpreter_type == INTERPRETER_AOUT),
- load_addr, interp_load_addr);
+ load_addr, phdr_addr, interp_load_addr);
/* N.B. passed_fileno might not be initialized? */
if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) + 1;


2007-05-04 23:23:04

by Andrew Morton

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On Fri, 4 May 2007 10:09:21 -0400
Quentin Godfroy <[email protected]> wrote:

> On a dynamic ELF executable, the current kernel loader gives to the
> interpreter (in the AUXV vector) the AT_PHDR argument as :
> offset_of_phdr_in_file + first address.
>
> It can be wrong for an executable where the program headers are not located
> in the first loaded segment.
>
> This patch corrects the behaviour.
>
> Signed-off-by: Quentin Godfroy <[email protected]>
> ---
> Here is an example of such an ELF executable which the current code
> fails on :
> ftp://quatramaran.ens.fr/pub/godfroy/addrpath/broken-sample
>
> --- linux-2.6.21.1/fs/binfmt_elf.c 2007-05-04 03:20:00.000000000 -0400
> +++ linux-2.6.21.1-patch/fs/binfmt_elf.c 2007-05-04 08:02:18.000000000 -0400
> @@ -134,6 +134,7 @@ static int padzero(unsigned long elf_bss
> static int
> create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
> int interp_aout, unsigned long load_addr,
> + unsigned long phdr_addr,
> unsigned long interp_load_addr)
> {
> unsigned long p = bprm->p;
> @@ -190,7 +191,7 @@ create_elf_tables(struct linux_binprm *b
> NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
> NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
> NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
> - NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
> + NEW_AUX_ENT(AT_PHDR, phdr_addr);
> NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
> NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
> NEW_AUX_ENT(AT_BASE, interp_load_addr);
> @@ -529,7 +530,7 @@ static unsigned long randomize_stack_top
> static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
> {
> struct file *interpreter = NULL; /* to shut gcc up */
> - unsigned long load_addr = 0, load_bias = 0;
> + unsigned long load_addr = 0, load_bias = 0, phdr_addr = 0;
> int load_addr_set = 0;
> char * elf_interpreter = NULL;
> unsigned int interpreter_type = INTERPRETER_NONE;
> @@ -718,6 +719,16 @@ static int load_elf_binary(struct linux_
> break;
> }
>
> + elf_ppnt = elf_phdata;
> + for (i = 0; i< loc->elf_ex.e_phnum; i++, elf_ppnt++)
> + if (elf_ppnt->p_type == PT_PHDR) {
> + phdr_addr = elf_ppnt->p_vaddr;
> + break;
> + }
> + retval = -ENOEXEC;
> + if (!phdr_addr)
> + goto out_free_dentry;
> +
> /* Some simple consistency checks for the interpreter */
> if (elf_interpreter) {
> interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
> @@ -987,7 +998,7 @@ static int load_elf_binary(struct linux_
> current->flags &= ~PF_FORKNOEXEC;
> create_elf_tables(bprm, &loc->elf_ex,
> (interpreter_type == INTERPRETER_AOUT),
> - load_addr, interp_load_addr);
> + load_addr, phdr_addr, interp_load_addr);
> /* N.B. passed_fileno might not be initialized? */
> if (interpreter_type == INTERPRETER_AOUT)
> current->mm->arg_start += strlen(passed_fileno) + 1;

This patch kills my FC6 machine (using a config which was derived from RH's
original):

Freeing unused kernel memory: 368k freed
Write protecting the kernel read-only data: 959k
request_module: runaway loop modprobe binfmt-464c
request_module: runaway loop modprobe binfmt-464c
request_module: runaway loop modprobe binfmt-464c
request_module: runaway loop modprobe binfmt-464c
request_module: runaway loop modprobe binfmt-464c


.config: http://userweb.kernel.org/~akpm/config-akpm2.txt

2007-05-04 23:31:48

by Jeremy Fitzhardinge

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

Quentin Godfroy wrote:
> + elf_ppnt = elf_phdata;
> + for (i = 0; i< loc->elf_ex.e_phnum; i++, elf_ppnt++)
> + if (elf_ppnt->p_type == PT_PHDR) {
> + phdr_addr = elf_ppnt->p_vaddr;
>

Won't this break with ET_DYN executables? And besides, isn't this the
same thing? Shouldn't PT_PHDR->p_vaddr point to the vaddr of the Phdr
table itself?

J

2007-05-05 03:24:56

by Quentin Godfroy

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On Fri, May 04, 2007 at 04:31:49PM -0700, Jeremy Fitzhardinge wrote:
> Quentin Godfroy wrote:
> > + elf_ppnt = elf_phdata;
> > + for (i = 0; i< loc->elf_ex.e_phnum; i++, elf_ppnt++)
> > + if (elf_ppnt->p_type == PT_PHDR) {
> > + phdr_addr = elf_ppnt->p_vaddr;
> >
>
> Won't this break with ET_DYN executables? And besides, isn't this the
> same thing?

Indeed, I haven't seen that. For ET_DYN executables, it could be done a
thing like load_addr+elf_ppnt->p_vaddr (in the function that creates the
auxv, as ity has access to the elf header), and for ET_EXEC do what I
propose. I think this is trivial to do. I'll do it as soon as I come back
in front of my machine.


> Shouldn't PT_PHDR->p_vaddr point to the vaddr of the Phdr
> table itself?

I don't understand. Yes it is what it is supposed to be, and the kernel
is supposed to give the vaddr of the phdr table to the interpreter and
not load addr + offset of phdr in file, which is sometimes wrong.

2007-05-05 03:34:44

by Quentin Godfroy

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On Fri, May 04, 2007 at 04:22:08PM -0700, Andrew Morton wrote:
> This patch kills my FC6 machine (using a config which was derived from RH's
> original):
>
> Freeing unused kernel memory: 368k freed
> Write protecting the kernel read-only data: 959k
> request_module: runaway loop modprobe binfmt-464c
> request_module: runaway loop modprobe binfmt-464c
> request_module: runaway loop modprobe binfmt-464c
> request_module: runaway loop modprobe binfmt-464c
> request_module: runaway loop modprobe binfmt-464c
>
>
> .config: http://userweb.kernel.org/~akpm/config-akpm2.txt

I didn't try it on a 64bit kernel. I'll do as soon as I can reach my
machine. Probably the loop does not find PT_PHDR and then returns noexec.

I had such a problem, but it was because I forgot elf_ppnt = elf_phdata
before the loop.

By the way, is init 32 bits or 64 bits? It could break the ia32
emulation thing, but not the 64bit native mode.

Anyway the problem could be addressed by returning back to the old
behaviour if the loop fails, but it's not clean at all.

2007-05-05 04:24:05

by Jeremy Fitzhardinge

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

Quentin Godfroy wrote:
>> Won't this break with ET_DYN executables? And besides, isn't this the
>> same thing?
>>
>
> Indeed, I haven't seen that. For ET_DYN executables, it could be done a
> thing like load_addr+elf_ppnt->p_vaddr (in the function that creates the
> auxv, as ity has access to the elf header), and for ET_EXEC do what I
> propose. I think this is trivial to do. I'll do it as soon as I come back
> in front of my machine.
>

I don't think you need to special-case it. You can compute the offset
between the linked address and the load address (first
PT_LOAD[0]->p_vaddr - load_addr) and use that to offset all the other
addresses.


> I don't understand. Yes it is what it is supposed to be, and the kernel
> is supposed to give the vaddr of the phdr table to the interpreter and
> not load addr + offset of phdr in file, which is sometimes wrong.
>

How can it be wrong? Does the PT_PHDR point to a different array of
Phdr entries?

J

2007-05-05 04:27:55

by Andrew Morton

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On Fri, 4 May 2007 23:34:08 -0400 Quentin Godfroy <[email protected]> wrote:

> By the way, is init 32 bits or 64 bits? It could break the ia32
> emulation thing, but not the 64bit native mode.

akpm2:/home/akpm> file /sbin/init
/sbin/init: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, stripped

akpm2:/home/akpm> cat /etc/issue
Fedora Core release 6 (Zod)
Kernel \r on an \m

2007-05-06 23:43:21

by Quentin Godfroy

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On Fri, May 04, 2007 at 09:24:05PM -0700, Jeremy Fitzhardinge wrote:
> > Indeed, I haven't seen that. For ET_DYN executables, it could be done a
> > thing like load_addr+elf_ppnt->p_vaddr (in the function that creates the
> > auxv, as ity has access to the elf header), and for ET_EXEC do what I
> > propose. I think this is trivial to do. I'll do it as soon as I come back
> > in front of my machine.
> >
>
> I don't think you need to special-case it. You can compute the offset
> between the linked address and the load address (first
> PT_LOAD[0]->p_vaddr - load_addr) and use that to offset all the other
> addresses.

Indeed. And it has the advantage to work for prelinked objects. (but I
have to understand anyway how does the kernel handles prelinked (or not) pie
executables)

>
>
> > I don't understand. Yes it is what it is supposed to be, and the kernel
> > is supposed to give the vaddr of the phdr table to the interpreter and
> > not load addr + offset of phdr in file, which is sometimes wrong.
> >
>
> How can it be wrong? Does the PT_PHDR point to a different array of
> Phdr entries?

No, of course, but in my case I wanted to build an executable with a
modified rpath. I had to add a new PT_LOAD segment. To do so, as the
program header is generally located at the very beginning of the
executable, I had to copy it to the end, and so the address where it was
loaded was completely different.

The load address was typically 0x08048000, and the phdr location was
0x0804a570. But the kernel gave to the ld.so in the auxv the addr
0x08048570 for the phdr. And it provoked a segfault because of the .bss
which was between the segments. (and even if there was no .bss, it would
have worked only by chance because the segments could all fit in a page
of 4kb)

Quentin

2007-05-07 17:47:58

by Quentin Godfroy

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On Fri, May 04, 2007 at 10:09:21AM -0400, Quentin Godfroy wrote:
> On a dynamic ELF executable, the current kernel loader gives to the
> interpreter (in the AUXV vector) the AT_PHDR argument as :
> offset_of_phdr_in_file + first address.
>
> It can be wrong for an executable where the program headers are not located
> in the first loaded segment.
>
> This patch corrects the behaviour.
>
> Signed-off-by: Quentin Godfroy <[email protected]>
> ---
> Here is an example of such an ELF executable which the current code
> fails on :
> ftp://quatramaran.ens.fr/pub/godfroy/addrpath/broken-sample
>
> --- linux-2.6.21.1/fs/binfmt_elf.c 2007-05-04 03:20:00.000000000 -0400
> +++ linux-2.6.21.1-patch/fs/binfmt_elf.c 2007-05-04 08:02:18.000000000 -0400
> @@ -134,6 +134,7 @@ static int padzero(unsigned long elf_bss
> static int
> create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
> int interp_aout, unsigned long load_addr,
> + unsigned long phdr_addr,
> unsigned long interp_load_addr)
> {
> unsigned long p = bprm->p;
> @@ -190,7 +191,7 @@ create_elf_tables(struct linux_binprm *b
> NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
> NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
> NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
> - NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
> + NEW_AUX_ENT(AT_PHDR, phdr_addr);
> NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
> NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
> NEW_AUX_ENT(AT_BASE, interp_load_addr);
> @@ -529,7 +530,7 @@ static unsigned long randomize_stack_top
> static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
> {
> struct file *interpreter = NULL; /* to shut gcc up */
> - unsigned long load_addr = 0, load_bias = 0;
> + unsigned long load_addr = 0, load_bias = 0, phdr_addr = 0;
> int load_addr_set = 0;
> char * elf_interpreter = NULL;
> unsigned int interpreter_type = INTERPRETER_NONE;
> @@ -718,6 +719,16 @@ static int load_elf_binary(struct linux_
> break;
> }
>
> + elf_ppnt = elf_phdata;
> + for (i = 0; i< loc->elf_ex.e_phnum; i++, elf_ppnt++)
> + if (elf_ppnt->p_type == PT_PHDR) {
> + phdr_addr = elf_ppnt->p_vaddr;
> + break;
> + }
> + retval = -ENOEXEC;
> + if (!phdr_addr)
> + goto out_free_dentry;
> +
> /* Some simple consistency checks for the interpreter */
> if (elf_interpreter) {
> interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;
> @@ -987,7 +998,7 @@ static int load_elf_binary(struct linux_
> current->flags &= ~PF_FORKNOEXEC;
> create_elf_tables(bprm, &loc->elf_ex,
> (interpreter_type == INTERPRETER_AOUT),
> - load_addr, interp_load_addr);
> + load_addr, phdr_addr, interp_load_addr);
> /* N.B. passed_fileno might not be initialized? */
> if (interpreter_type == INTERPRETER_AOUT)
> current->mm->arg_start += strlen(passed_fileno) + 1;

I have made another patch, which I hope should not break anything this
time. I tested it on an x86_64 kernel with 32 and 64 bits executables,
PIE and not PIE (well my only PIE are libc-2.5.so and ld-2.5.so). I
rejoined the loops as suggested, and the kernel falls back on load_addr +
exec->e_phoff if there was no PT_PHDR entry in the program headers.

Signed-off-by: Quentin Godfroy <[email protected]>
---
--- linux-2.6.21.1/fs/binfmt_elf.c 2007-05-07 10:58:38.000000000 -0400
+++ linux-2.6.21.1-patch/fs/binfmt_elf.c 2007-05-07 13:14:33.000000000 -0400
@@ -134,6 +134,7 @@ static int padzero(unsigned long elf_bss
static int
create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
int interp_aout, unsigned long load_addr,
+ unsigned long phdr_addr,
unsigned long interp_load_addr)
{
unsigned long p = bprm->p;
@@ -190,7 +191,10 @@ create_elf_tables(struct linux_binprm *b
NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
- NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
+ if(phdr_addr)
+ NEW_AUX_ENT(AT_PHDR, phdr_addr);
+ else
+ NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
NEW_AUX_ENT(AT_BASE, interp_load_addr);
@@ -529,7 +533,7 @@ static unsigned long randomize_stack_top
static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
{
struct file *interpreter = NULL; /* to shut gcc up */
- unsigned long load_addr = 0, load_bias = 0;
+ unsigned long load_addr = 0, load_bias = 0, phdr_addr = 0;
int load_addr_set = 0;
char * elf_interpreter = NULL;
unsigned int interpreter_type = INTERPRETER_NONE;
@@ -620,7 +624,12 @@ static int load_elf_binary(struct linux_
start_data = 0;
end_data = 0;

- for (i = 0; i < loc->elf_ex.e_phnum; i++) {
+ for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
+ if (elf_ppnt->p_type == PT_PHDR) {
+ phdr_addr = elf_ppnt->p_vaddr;
+ continue;
+ }
+
if (elf_ppnt->p_type == PT_INTERP) {
/* This is the program interpreter used for
* shared libraries - for now assume that this
@@ -703,20 +712,17 @@ static int load_elf_binary(struct linux_
/* Get the exec headers */
loc->interp_ex = *((struct exec *)bprm->buf);
loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
- break;
+ continue;
}
- elf_ppnt++;
- }

- elf_ppnt = elf_phdata;
- for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
if (elf_ppnt->p_type == PT_GNU_STACK) {
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
- break;
+ continue;
}
+ }

/* Some simple consistency checks for the interpreter */
if (elf_interpreter) {
@@ -985,9 +991,12 @@ static int load_elf_binary(struct linux_

compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
+ if (phdr_addr)
+ phdr_addr += load_bias;
+
create_elf_tables(bprm, &loc->elf_ex,
(interpreter_type == INTERPRETER_AOUT),
- load_addr, interp_load_addr);
+ load_addr, phdr_addr, interp_load_addr);
/* N.B. passed_fileno might not be initialized? */
if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) + 1;

2007-05-07 18:13:13

by Quentin Godfroy

[permalink] [raw]
Subject: Re: patch: VFS: fix passing of AT_PHDR value in auxv to ELF interpreter

On Mon, May 07, 2007 at 01:47:02PM -0400, Quentin Godfroy wrote:
> I have made another patch, which I hope should not break anything this
> time. I tested it on an x86_64 kernel with 32 and 64 bits executables,
> PIE and not PIE (well my only PIE are libc-2.5.so and ld-2.5.so). I
> rejoined the loops as suggested, and the kernel falls back on load_addr +
> exec->e_phoff if there was no PT_PHDR entry in the program headers.
>
> Signed-off-by: Quentin Godfroy <[email protected]>
> ---
> --- linux-2.6.21.1/fs/binfmt_elf.c 2007-05-07 10:58:38.000000000 -0400
> +++ linux-2.6.21.1-patch/fs/binfmt_elf.c 2007-05-07 13:14:33.000000000 -0400
> @@ -134,6 +134,7 @@ static int padzero(unsigned long elf_bss
> static int
> create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
> int interp_aout, unsigned long load_addr,
> + unsigned long phdr_addr,
> unsigned long interp_load_addr)
> {
> unsigned long p = bprm->p;
> @@ -190,7 +191,10 @@ create_elf_tables(struct linux_binprm *b
> NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
> NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
> NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
> - NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
> + if(phdr_addr)
> + NEW_AUX_ENT(AT_PHDR, phdr_addr);
> + else
> + NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
> NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
> NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
> NEW_AUX_ENT(AT_BASE, interp_load_addr);
> @@ -529,7 +533,7 @@ static unsigned long randomize_stack_top
> static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
> {
> struct file *interpreter = NULL; /* to shut gcc up */
> - unsigned long load_addr = 0, load_bias = 0;
> + unsigned long load_addr = 0, load_bias = 0, phdr_addr = 0;
> int load_addr_set = 0;
> char * elf_interpreter = NULL;
> unsigned int interpreter_type = INTERPRETER_NONE;
> @@ -620,7 +624,12 @@ static int load_elf_binary(struct linux_
> start_data = 0;
> end_data = 0;
>
> - for (i = 0; i < loc->elf_ex.e_phnum; i++) {
> + for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
> + if (elf_ppnt->p_type == PT_PHDR) {
> + phdr_addr = elf_ppnt->p_vaddr;
> + continue;
> + }
> +
> if (elf_ppnt->p_type == PT_INTERP) {
> /* This is the program interpreter used for
> * shared libraries - for now assume that this
> @@ -703,20 +712,17 @@ static int load_elf_binary(struct linux_
> /* Get the exec headers */
> loc->interp_ex = *((struct exec *)bprm->buf);
> loc->interp_elf_ex = *((struct elfhdr *)bprm->buf);
> - break;
> + continue;
> }
> - elf_ppnt++;
> - }
>
> - elf_ppnt = elf_phdata;
> - for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
> if (elf_ppnt->p_type == PT_GNU_STACK) {
> if (elf_ppnt->p_flags & PF_X)
> executable_stack = EXSTACK_ENABLE_X;
> else
> executable_stack = EXSTACK_DISABLE_X;
> - break;
> + continue;
> }
> + }
>
> /* Some simple consistency checks for the interpreter */
> if (elf_interpreter) {
> @@ -985,9 +991,12 @@ static int load_elf_binary(struct linux_
>
> compute_creds(bprm);
> current->flags &= ~PF_FORKNOEXEC;
> + if (phdr_addr)
> + phdr_addr += load_bias;
> +
> create_elf_tables(bprm, &loc->elf_ex,
> (interpreter_type == INTERPRETER_AOUT),
> - load_addr, interp_load_addr);
> + load_addr, phdr_addr, interp_load_addr);
> /* N.B. passed_fileno might not be initialized? */
> if (interpreter_type == INTERPRETER_AOUT)
> current->mm->arg_start += strlen(passed_fileno) + 1;

Wooops. Wrong one. Sorry. This one could have memory leaks.

Signed-off-by: Quentin Godfroy <[email protected]>
---
--- linux-2.6.21.1/fs/binfmt_elf.c 2007-05-07 10:58:38.000000000 -0400
+++ linux-2.6.21.1-patch/fs/binfmt_elf.c 2007-05-07 13:58:14.000000000 -0400
@@ -134,6 +134,7 @@ static int padzero(unsigned long elf_bss
static int
create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
int interp_aout, unsigned long load_addr,
+ unsigned long phdr_addr,
unsigned long interp_load_addr)
{
unsigned long p = bprm->p;
@@ -190,7 +191,10 @@ create_elf_tables(struct linux_binprm *b
NEW_AUX_ENT(AT_HWCAP, ELF_HWCAP);
NEW_AUX_ENT(AT_PAGESZ, ELF_EXEC_PAGESIZE);
NEW_AUX_ENT(AT_CLKTCK, CLOCKS_PER_SEC);
- NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
+ if(phdr_addr)
+ NEW_AUX_ENT(AT_PHDR, phdr_addr);
+ else
+ NEW_AUX_ENT(AT_PHDR, load_addr + exec->e_phoff);
NEW_AUX_ENT(AT_PHENT, sizeof(struct elf_phdr));
NEW_AUX_ENT(AT_PHNUM, exec->e_phnum);
NEW_AUX_ENT(AT_BASE, interp_load_addr);
@@ -529,7 +533,7 @@ static unsigned long randomize_stack_top
static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
{
struct file *interpreter = NULL; /* to shut gcc up */
- unsigned long load_addr = 0, load_bias = 0;
+ unsigned long load_addr = 0, load_bias = 0, phdr_addr = 0;
int load_addr_set = 0;
char * elf_interpreter = NULL;
unsigned int interpreter_type = INTERPRETER_NONE;
@@ -709,14 +713,20 @@ static int load_elf_binary(struct linux_
}

elf_ppnt = elf_phdata;
- for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++)
+ for (i = 0; i < loc->elf_ex.e_phnum; i++, elf_ppnt++) {
+ if (elf_ppnt->p_type == PT_PHDR) {
+ phdr_addr = elf_ppnt->p_vaddr;
+ continue;
+ }
+
if (elf_ppnt->p_type == PT_GNU_STACK) {
if (elf_ppnt->p_flags & PF_X)
executable_stack = EXSTACK_ENABLE_X;
else
executable_stack = EXSTACK_DISABLE_X;
- break;
+ continue;
}
+ }

/* Some simple consistency checks for the interpreter */
if (elf_interpreter) {
@@ -985,9 +995,12 @@ static int load_elf_binary(struct linux_

compute_creds(bprm);
current->flags &= ~PF_FORKNOEXEC;
+ if (phdr_addr)
+ phdr_addr += load_bias;
+
create_elf_tables(bprm, &loc->elf_ex,
(interpreter_type == INTERPRETER_AOUT),
- load_addr, interp_load_addr);
+ load_addr, phdr_addr, interp_load_addr);
/* N.B. passed_fileno might not be initialized? */
if (interpreter_type == INTERPRETER_AOUT)
current->mm->arg_start += strlen(passed_fileno) + 1;