All of lore.kernel.org
 help / color / mirror / Atom feed
* binfmt_elf PT_INTERP gets EFAULT if no PROT_WRITE
@ 2009-09-09  0:26 John Reiser
  2009-09-09  2:49 ` Roland McGrath
  0 siblings, 1 reply; 3+ messages in thread
From: John Reiser @ 2009-09-09  0:26 UTC (permalink / raw)
  To: Alexander Viro, James Morris, Roland McGrath, David Howells,
	Andrew Morton
  Cc: linux-fsdevel, Linux Kernel Mailing List

In fs/binfmt_elf.c, routine load_elf_interp() calls padzero() for .bss
even if the PT_LOAD has no PROT_WRITE and no .bss.  This generates EFAULT.
One easy way to avoid trouble is that a PT_LOAD with no PROT_WRITE
should skip the .bss calculation entirely.

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

Signed-off-by: John Reiser <jreiser@BitWagon.com>


diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index b7c1603..3b9a097 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -488,7 +488,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
			 * keep track of the largest address we see for this.
			 */
			k = load_addr + eppnt->p_vaddr + eppnt->p_filesz;
-			if (k > elf_bss)
+			if (k > elf_bss && PROT_WRITE & elf_prot)
				elf_bss = k;

			/*
@@ -496,7 +496,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
			 * elf_bss and last_bss is the bss section.
			 */
			k = load_addr + eppnt->p_memsz + eppnt->p_vaddr;
-			if (k > last_bss)
+			if (k > last_bss && PROT_WRITE & elf_prot)
				last_bss = k;
		}
	}

-- 

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: binfmt_elf PT_INTERP gets EFAULT if no PROT_WRITE
  2009-09-09  0:26 binfmt_elf PT_INTERP gets EFAULT if no PROT_WRITE John Reiser
@ 2009-09-09  2:49 ` Roland McGrath
  2009-09-10 10:12   ` James Morris
  0 siblings, 1 reply; 3+ messages in thread
From: Roland McGrath @ 2009-09-09  2:49 UTC (permalink / raw)
  To: John Reiser
  Cc: Alexander Viro, James Morris, David Howells, Andrew Morton,
	Linus Torvalds, linux-fsdevel, Linux Kernel Mailing List

Good catch, John.  But I prefer a different fix.  

I've reproduced the bug with the test case John included,
and verified that my change fixes it too.


Thanks,
Roland

---
[PATCH] binfmt_elf: fix PT_INTERP bss handling

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>
---
 fs/binfmt_elf.c |   28 ++++++++++++++--------------
 1 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
index b7c1603..7c1e65d 100644
--- a/fs/binfmt_elf.c
+++ b/fs/binfmt_elf.c
@@ -501,22 +501,22 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
 		}
 	}
 
-	/*
-	 * Now fill out the bss section.  First pad the last page up
-	 * to the page boundary, and then perform a mmap to make sure
-	 * that there are zero-mapped pages up to and including the 
-	 * last bss page.
-	 */
-	if (padzero(elf_bss)) {
-		error = -EFAULT;
-		goto out_close;
-	}
+	if (last_bss > elf_bss) {
+		/*
+		 * Now fill out the bss section.  First pad the last page up
+		 * to the page boundary, and then perform a mmap to make sure
+		 * that there are zero-mapped pages up to and including the
+		 * last bss page.
+		 */
+		if (padzero(elf_bss)) {
+			error = -EFAULT;
+			goto out_close;
+		}
 
-	/* What we have mapped so far */
-	elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1);
+		/* What we have mapped so far */
+		elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1);
 
-	/* Map the last of the bss segment */
-	if (last_bss > elf_bss) {
+		/* Map the last of the bss segment */
 		down_write(&current->mm->mmap_sem);
 		error = do_brk(elf_bss, last_bss - elf_bss);
 		up_write(&current->mm->mmap_sem);

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: binfmt_elf PT_INTERP gets EFAULT if no PROT_WRITE
  2009-09-09  2:49 ` Roland McGrath
@ 2009-09-10 10:12   ` James Morris
  0 siblings, 0 replies; 3+ messages in thread
From: James Morris @ 2009-09-10 10:12 UTC (permalink / raw)
  To: Roland McGrath
  Cc: John Reiser, Alexander Viro, David Howells, Andrew Morton,
	Linus Torvalds, linux-fsdevel, Linux Kernel Mailing List, stable

On Tue, 8 Sep 2009, Roland McGrath wrote:

> Good catch, John.  But I prefer a different fix.  
> 
> I've reproduced the bug with the test case John included,
> and verified that my change fixes it too.

Applied to
git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/security-testing-2.6#next

(cc stable).

> 
> 
> Thanks,
> Roland
> 
> ---
> [PATCH] binfmt_elf: fix PT_INTERP bss handling
> 
> 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>
> ---
>  fs/binfmt_elf.c |   28 ++++++++++++++--------------
>  1 files changed, 14 insertions(+), 14 deletions(-)
> 
> diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
> index b7c1603..7c1e65d 100644
> --- a/fs/binfmt_elf.c
> +++ b/fs/binfmt_elf.c
> @@ -501,22 +501,22 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex,
>  		}
>  	}
>  
> -	/*
> -	 * Now fill out the bss section.  First pad the last page up
> -	 * to the page boundary, and then perform a mmap to make sure
> -	 * that there are zero-mapped pages up to and including the 
> -	 * last bss page.
> -	 */
> -	if (padzero(elf_bss)) {
> -		error = -EFAULT;
> -		goto out_close;
> -	}
> +	if (last_bss > elf_bss) {
> +		/*
> +		 * Now fill out the bss section.  First pad the last page up
> +		 * to the page boundary, and then perform a mmap to make sure
> +		 * that there are zero-mapped pages up to and including the
> +		 * last bss page.
> +		 */
> +		if (padzero(elf_bss)) {
> +			error = -EFAULT;
> +			goto out_close;
> +		}
>  
> -	/* What we have mapped so far */
> -	elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1);
> +		/* What we have mapped so far */
> +		elf_bss = ELF_PAGESTART(elf_bss + ELF_MIN_ALIGN - 1);
>  
> -	/* Map the last of the bss segment */
> -	if (last_bss > elf_bss) {
> +		/* Map the last of the bss segment */
>  		down_write(&current->mm->mmap_sem);
>  		error = do_brk(elf_bss, last_bss - elf_bss);
>  		up_write(&current->mm->mmap_sem);
> 

-- 
James Morris
<jmorris@namei.org>

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2009-09-10 10:13 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-09-09  0:26 binfmt_elf PT_INTERP gets EFAULT if no PROT_WRITE John Reiser
2009-09-09  2:49 ` Roland McGrath
2009-09-10 10:12   ` James Morris

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.