binfmt_elf: fix PT_INTERP bss handling
commit1cb8fc89d73a27843d76c27974755df1f954e18c
authorRoland McGrath <roland@redhat.com>
Wed, 9 Sep 2009 02:49:40 +0000 (8 19:49 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 24 Sep 2009 15:27:09 +0000 (24 08:27 -0700)
tree999728b3794eeb90aa76560a4f7fa25f6c35c54d
parentf1d3ff8e09a2f7bf1ba969c1d38d29725ef1a4c4
binfmt_elf: fix PT_INTERP bss handling

commit 9f0ab4a3f0fdb1ff404d150618ace2fa069bb2e1 upstream.

In fs/binfmt_elf.c, load_elf_interp() calls padzero() for .bss even if
the PT_LOAD has no PROT_WRITE and no .bss.  This generates EFAULT.

Here is a small test case.  (Yes, there are other, useful PT_INTERP
which have only .text and no .data/.bss.)

----- ptinterp.S
_start: .globl _start
 nop
 int3
-----
$ gcc -m32 -nostartfiles -nostdlib -o ptinterp ptinterp.S
$ gcc -m32 -Wl,--dynamic-linker=ptinterp -o hello hello.c
$ ./hello
Segmentation fault  # during execve() itself

After applying the patch:
$ ./hello
Trace trap  # user-mode execution after execve() finishes

If the ELF headers are actually self-inconsistent, then dying is fine.
But having no PROT_WRITE segment is perfectly normal and correct if
there is no segment with p_memsz > p_filesz (i.e. bss).  John Reiser
suggested checking for PROT_WRITE in the bss logic.  I think it makes
most sense to simply apply the bss logic only when there is bss.

This patch looks less trivial than it is due to some reindentation.
It just moves the "if (last_bss > elf_bss) {" test up to include the
partial-page bss logic as well as the more-pages bss logic.

Reported-by: John Reiser <jreiser@bitwagon.com>
Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
fs/binfmt_elf.c