From mboxrd@z Thu Jan 1 00:00:00 1970 From: Quentin Godfroy Subject: [PATCH] VFS: fix passing of AT_PHDR value in auxv to ELF interpreter Date: Thu, 4 Sep 2008 00:32:33 +0200 Message-ID: <20080903223232.GA2860@goelette.ens.fr> Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Cc: Bart van der Werf To: linux-fsdevel@vger.kernel.org Return-path: Received: from nef2.ens.fr ([129.199.96.40]:3474 "EHLO nef2.ens.fr" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751048AbYICXSa (ORCPT ); Wed, 3 Sep 2008 19:18:30 -0400 Content-Disposition: inline Sender: linux-fsdevel-owner@vger.kernel.org List-ID: 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. An example of such a binary on x86 can be found on http://www.eleves.ens.fr/~godfroy/sample_code_32 The following patch corrects the behaviour. It applies cleanly on 2.6.26.2 (probably 2.6.26.3 too), and was tested on debian x86_64 (both ELF32 and ELF64) as well as on x86. Normal executables, static and PIE continue to work. Signed-off-by: Quentin Godfroy --- diff -ruNp linux-2.6.26.2/fs/binfmt_elf.c linux-2.6.26.2-patch/fs/binfmt_elf.c --- linux-2.6.26.2/fs/binfmt_elf.c 2008-08-06 18:19:01.000000000 +0200 +++ linux-2.6.26.2-patch/fs/binfmt_elf.c 2008-08-09 02:39:03.033179733 +0200 @@ -133,7 +133,7 @@ static int padzero(unsigned long elf_bss static int create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, - unsigned long load_addr, unsigned long interp_load_addr) + unsigned long phdr_addr, unsigned long interp_load_addr) { unsigned long p = bprm->p; int argc = bprm->argc; @@ -193,7 +193,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 +529,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 long error; @@ -685,14 +685,19 @@ 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) { @@ -861,6 +866,10 @@ static int load_elf_binary(struct linux_ end_code += load_bias; start_data += load_bias; end_data += load_bias; + if (phdr_addr) + phdr_addr += load_bias; + else + phdr_addr = load_addr + loc->elf_ex.e_phoff; /* Calling set_brk effectively mmaps the pages that we need * for the bss and break sections. We must do this before @@ -930,7 +939,7 @@ static int load_elf_binary(struct linux_ compute_creds(bprm); current->flags &= ~PF_FORKNOEXEC; retval = create_elf_tables(bprm, &loc->elf_ex, - load_addr, interp_load_addr); + phdr_addr, interp_load_addr); if (retval < 0) { send_sig(SIGKILL, current, 0); goto out;