mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* RE: [PATCH 1/2] setup_arg_pages can insert overlapping vma
@ 2004-11-24  1:04 Zou, Nanhai
  2004-11-24  1:23 ` Andrew Morton
                   ` (2 more replies)
  0 siblings, 3 replies; 13+ messages in thread
From: Zou, Nanhai @ 2004-11-24  1:04 UTC (permalink / raw)
  To: Hugh Dickins, Chris Wright
  Cc: Andrew Morton, Linus Torvalds, Luck, Tony, Martin Schwidefsky,
	Andi Kleen, linux-kernel, linux-ia64

[-- Attachment #1: Type: text/plain, Size: 5060 bytes --]

 <<ia64-vm-overlap.tar.gz>>  <<vma-overlap-fix.patch>> I think ia64 ia32
subsystem is not vulnerable to this kind of overlapping vm problem,
because it does not support a.out binary format, 
X84_64 is vulnerable to this. 

just do a 
perl -e'print"\x07\x01".("\x00"x10)."\x00\xe0\xff\xff".("\x00"x16)'>
evilaout
you will get it.
 
and IA64 is also vulnerable to this kind of bug in 64 bit elf support,
it just insert a vma of zero page without checking overlap, so user can
construct a elf with section begin from 0x0 to trigger this BUGON().I
attach a testcase to trigger this bug
I don't know what about s390. However, I think it's safe to check
overlap before we actually insert a vma into vma list.
 
And I also feel check vma overlap everywhere is unnecessary, because
invert_vm_struct will check it again, so the check is duplicated. It's
better to have invert_vm_struct return a value then let caller check if
it successes.
Here is a patch against 2.6.10.rc2-mm3
I have tested it on i386, x86_64 and ia64 machines.

Signed-off-by: Tony Luck <tony.luck@intel.com>
Signed-off-by: Zou Nan hai <Nanhai.zou@intel.com>


-----Original Message-----
From: linux-kernel-owner@vger.kernel.org
[mailto:linux-kernel-owner@vger.kernel.org] On Behalf Of Hugh Dickins
Sent: Friday, November 19, 2004 1:40 AM
To: Chris Wright
Cc: Andrew Morton; Linus Torvalds; Luck, Tony; Martin Schwidefsky; Andi
Kleen; linux-kernel@vger.kernel.org
Subject: Re: [PATCH 1/2] setup_arg_pages can insert overlapping vma

On Tue, 16 Nov 2004, Chris Wright wrote:
> Florian Heinz built an a.out binary that could map bss from 0x0 to
> 0xc0000000, and setup_arg_pages() would BUG() in insert_vma_struct
> because the arg pages overlapped.  This just checks before inserting,
> and bails out if it would overlap.

Chris, shouldn't your patch also cover the setup_arg_pages clones for
32-bit support on 64-bit architectures, with something - uncompiled,
untested - like the below?  I'm not sure how necessary the additional
vma->vm_start < mpnt->vm_end test is, but suspect ia64 might need it.

Signed-off-by: Hugh Dickins <hugh@veritas.com>

--- 2.6.10-rc2-bk2/arch/ia64/ia32/binfmt_elf32.c	2004-10-18
22:57:03.000000000 +0100
+++ linux/arch/ia64/ia32/binfmt_elf32.c	2004-11-18 17:17:57.000000000
+0000
@@ -214,6 +214,8 @@ ia32_setup_arg_pages (struct linux_binpr
 
 	down_write(&current->mm->mmap_sem);
 	{
+		struct vm_area_struct *vma;
+
 		mpnt->vm_mm = current->mm;
 		mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
 		mpnt->vm_end = IA32_STACK_TOP;
@@ -225,6 +227,12 @@ ia32_setup_arg_pages (struct linux_binpr
 			mpnt->vm_flags = VM_STACK_FLAGS;
 		mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC)?
 					PAGE_COPY_EXEC: PAGE_COPY;
+		vma = find_vma(current->mm, mpnt->vm_start);
+		if (vma && vma->vm_start < mpnt->vm_end) {
+			up_write(&current->mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return -ENOMEM;
+		}
 		insert_vm_struct(current->mm, mpnt);
 		current->mm->stack_vm = current->mm->total_vm =
vma_pages(mpnt);
 	}
--- 2.6.10-rc2-bk2/arch/s390/kernel/compat_exec.c	2004-10-18
22:56:50.000000000 +0100
+++ linux/arch/s390/kernel/compat_exec.c	2004-11-18
17:17:57.000000000 +0000
@@ -62,12 +62,20 @@ int setup_arg_pages32(struct linux_binpr
 
 	down_write(&mm->mmap_sem);
 	{
+		struct vm_area_struct *vma;
+
 		mpnt->vm_mm = mm;
 		mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
 		mpnt->vm_end = STACK_TOP;
 		/* executable stack setting would be applied here */
 		mpnt->vm_page_prot = PAGE_COPY;
 		mpnt->vm_flags = VM_STACK_FLAGS;
+		vma = find_vma(mm, mpnt->vm_start);
+		if (vma && vma->vm_start < mpnt->vm_end) {
+			up_write(&mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return -ENOMEM;
+		}
 		insert_vm_struct(mm, mpnt);
 		mm->stack_vm = mm->total_vm = vma_pages(mpnt);
 	} 
--- 2.6.10-rc2-bk2/arch/x86_64/ia32/ia32_binfmt.c	2004-11-15
16:20:34.000000000 +0000
+++ linux/arch/x86_64/ia32/ia32_binfmt.c	2004-11-18
17:17:57.000000000 +0000
@@ -357,6 +357,8 @@ int setup_arg_pages(struct linux_binprm 
 
 	down_write(&mm->mmap_sem);
 	{
+		struct vm_area_struct *vma;
+
 		mpnt->vm_mm = mm;
 		mpnt->vm_start = PAGE_MASK & (unsigned long) bprm->p;
 		mpnt->vm_end = IA32_STACK_TOP;
@@ -368,6 +370,12 @@ int setup_arg_pages(struct linux_binprm 
 			mpnt->vm_flags = VM_STACK_FLAGS;
  		mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? 
  			PAGE_COPY_EXEC : PAGE_COPY;
+		vma = find_vma(mm, mpnt->vm_start);
+		if (vma && vma->vm_start < mpnt->vm_end) {
+			up_write(&mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return -ENOMEM;
+		}
 		insert_vm_struct(mm, mpnt);
 		mm->stack_vm = mm->total_vm = vma_pages(mpnt);
 	} 

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

[-- Attachment #2: ia64-vm-overlap.tar.gz --]
[-- Type: application/x-gzip, Size: 2741 bytes --]

[-- Attachment #3: vma-overlap-fix.patch --]
[-- Type: application/octet-stream, Size: 7815 bytes --]

diff -Nraup a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c
--- a/arch/ia64/ia32/binfmt_elf32.c	2004-11-21 23:58:21.344700219 -0800
+++ b/arch/ia64/ia32/binfmt_elf32.c	2004-11-21 23:58:45.143528053 -0800
@@ -100,7 +100,11 @@ ia64_elf32_init (struct pt_regs *regs)
 		vma->vm_ops = &ia32_shared_page_vm_ops;
 		down_write(&current->mm->mmap_sem);
 		{
-			insert_vm_struct(current->mm, vma);
+			if (insert_vm_struct(current->mm, vma)) {
+				kmem_cache_free(vm_area_cachep, vma);
+				up_write(&current->mm->mmap_sem);
+				return;
+			}
 		}
 		up_write(&current->mm->mmap_sem);
 	}
@@ -123,7 +127,11 @@ ia64_elf32_init (struct pt_regs *regs)
 		vma->vm_ops = &ia32_gate_page_vm_ops;
 		down_write(&current->mm->mmap_sem);
 		{
-			insert_vm_struct(current->mm, vma);
+			if (insert_vm_struct(current->mm, vma)) {
+				kmem_cache_free(vm_area_cachep, vma);
+				up_write(&current->mm->mmap_sem);
+				return;
+			}
 		}
 		up_write(&current->mm->mmap_sem);
 	}
@@ -142,7 +150,11 @@ ia64_elf32_init (struct pt_regs *regs)
 		vma->vm_flags = VM_READ|VM_WRITE|VM_MAYREAD|VM_MAYWRITE;
 		down_write(&current->mm->mmap_sem);
 		{
-			insert_vm_struct(current->mm, vma);
+			if (insert_vm_struct(current->mm, vma)) {
+				kmem_cache_free(vm_area_cachep, vma);
+				up_write(&current->mm->mmap_sem);
+				return;
+			}
 		}
 		up_write(&current->mm->mmap_sem);
 	}
@@ -190,7 +202,7 @@ ia32_setup_arg_pages (struct linux_binpr
 	unsigned long stack_base;
 	struct vm_area_struct *mpnt;
 	struct mm_struct *mm = current->mm;
-	int i;
+	int i, ret;
 
 	stack_base = IA32_STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
 	mm->arg_start = bprm->p + stack_base;
@@ -225,7 +237,11 @@ ia32_setup_arg_pages (struct linux_binpr
 			mpnt->vm_flags = VM_STACK_FLAGS;
 		mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC)?
 					PAGE_COPY_EXEC: PAGE_COPY;
-		insert_vm_struct(current->mm, mpnt);
+		if ((ret = insert_vm_struct(current->mm, mpnt))) {
+			up_write(&current->mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return ret;
+		}
 		current->mm->stack_vm = current->mm->total_vm = vma_pages(mpnt);
 	}
 
diff -Nraup a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c
--- a/arch/ia64/mm/init.c	2004-11-21 23:58:21.345676782 -0800
+++ b/arch/ia64/mm/init.c	2004-11-22 00:00:54.527315530 -0800
@@ -131,7 +131,13 @@ ia64_init_addr_space (void)
 		vma->vm_end = vma->vm_start + PAGE_SIZE;
 		vma->vm_page_prot = protection_map[VM_DATA_DEFAULT_FLAGS & 0x7];
 		vma->vm_flags = VM_DATA_DEFAULT_FLAGS | VM_GROWSUP;
-		insert_vm_struct(current->mm, vma);
+		down_write(&current->mm->mmap_sem);
+		if (insert_vm_struct(current->mm, vma)) {
+			up_write(&current->mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, vma);
+			return;
+		}
+		up_write(&current->mm->mmap_sem);
 	}
 
 	/* map NaT-page at address zero to speed up speculative dereferencing of NULL: */
@@ -143,7 +149,13 @@ ia64_init_addr_space (void)
 			vma->vm_end = PAGE_SIZE;
 			vma->vm_page_prot = __pgprot(pgprot_val(PAGE_READONLY) | _PAGE_MA_NAT);
 			vma->vm_flags = VM_READ | VM_MAYREAD | VM_IO | VM_RESERVED;
-			insert_vm_struct(current->mm, vma);
+			down_write(&current->mm->mmap_sem);
+			if (insert_vm_struct(current->mm, vma)) {
+				up_write(&current->mm->mmap_sem);
+				kmem_cache_free(vm_area_cachep, vma);
+				return;
+			}
+			up_write(&current->mm->mmap_sem);
 		}
 	}
 }
diff -Nraup a/arch/s390/kernel/compat_exec.c b/arch/s390/kernel/compat_exec.c
--- a/arch/s390/kernel/compat_exec.c	2004-11-21 23:58:21.428684593 -0800
+++ b/arch/s390/kernel/compat_exec.c	2004-11-21 23:58:45.144504615 -0800
@@ -39,7 +39,7 @@ int setup_arg_pages32(struct linux_binpr
 	unsigned long stack_base;
 	struct vm_area_struct *mpnt;
 	struct mm_struct *mm = current->mm;
-	int i;
+	int i, ret;
 
 	stack_base = STACK_TOP - MAX_ARG_PAGES*PAGE_SIZE;
 	mm->arg_start = bprm->p + stack_base;
@@ -68,7 +68,11 @@ int setup_arg_pages32(struct linux_binpr
 		/* executable stack setting would be applied here */
 		mpnt->vm_page_prot = PAGE_COPY;
 		mpnt->vm_flags = VM_STACK_FLAGS;
-		insert_vm_struct(mm, mpnt);
+		if ((ret = insert_vm_struct(mm, mpnt))) {
+			up_write(&mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return ret;
+		}
 		mm->stack_vm = mm->total_vm = vma_pages(mpnt);
 	} 
 
diff -Nraup a/arch/x86_64/ia32/ia32_binfmt.c b/arch/x86_64/ia32/ia32_binfmt.c
--- a/arch/x86_64/ia32/ia32_binfmt.c	2004-11-21 23:58:21.423801781 -0800
+++ b/arch/x86_64/ia32/ia32_binfmt.c	2004-11-21 23:58:45.145481178 -0800
@@ -335,7 +335,7 @@ int setup_arg_pages(struct linux_binprm 
 	unsigned long stack_base;
 	struct vm_area_struct *mpnt;
 	struct mm_struct *mm = current->mm;
-	int i;
+	int i, ret;
 
 	stack_base = IA32_STACK_TOP - MAX_ARG_PAGES * PAGE_SIZE;
 	mm->arg_start = bprm->p + stack_base;
@@ -369,7 +369,11 @@ int setup_arg_pages(struct linux_binprm 
 			mpnt->vm_flags = VM_STACK_FLAGS;
  		mpnt->vm_page_prot = (mpnt->vm_flags & VM_EXEC) ? 
  			PAGE_COPY_EXEC : PAGE_COPY;
-		insert_vm_struct(mm, mpnt);
+		if ((ret = insert_vm_struct(mm, mpnt))) {
+			up_write(&mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return ret;
+		}
 		mm->stack_vm = mm->total_vm = vma_pages(mpnt);
 	} 
 
diff -Nraup a/fs/exec.c b/fs/exec.c
--- a/fs/exec.c	2004-11-21 23:58:27.971653263 -0800
+++ b/fs/exec.c	2004-11-22 00:25:39.885695772 -0800
@@ -351,7 +351,7 @@ int setup_arg_pages(struct linux_binprm 
 	unsigned long stack_base;
 	struct vm_area_struct *mpnt;
 	struct mm_struct *mm = current->mm;
-	int i;
+	int i, ret;
 	long arg_size;
 
 #ifdef CONFIG_STACK_GROWSUP
@@ -424,7 +424,6 @@ int setup_arg_pages(struct linux_binprm 
 
 	down_write(&mm->mmap_sem);
 	{
-		struct vm_area_struct *vma;
 		mpnt->vm_mm = mm;
 #ifdef CONFIG_STACK_GROWSUP
 		mpnt->vm_start = stack_base;
@@ -444,13 +443,11 @@ int setup_arg_pages(struct linux_binprm 
 			mpnt->vm_flags = VM_STACK_FLAGS;
 		mpnt->vm_flags |= mm->def_flags;
 		mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7];
-		vma = find_vma(mm, mpnt->vm_start);
-		if (vma) {
+		if ((ret = insert_vm_struct(mm, mpnt))) {
 			up_write(&mm->mmap_sem);
 			kmem_cache_free(vm_area_cachep, mpnt);
-			return -ENOMEM;
+			return ret;
 		}
-		insert_vm_struct(mm, mpnt);
 		mm->stack_vm = mm->total_vm = vma_pages(mpnt);
 	}
 
diff -Nraup a/include/linux/mm.h b/include/linux/mm.h
--- a/include/linux/mm.h	2004-11-21 23:58:21.575168966 -0800
+++ b/include/linux/mm.h	2004-11-21 23:58:45.147434303 -0800
@@ -743,7 +743,7 @@ extern struct vm_area_struct *vma_merge(
 extern struct anon_vma *find_mergeable_anon_vma(struct vm_area_struct *);
 extern int split_vma(struct mm_struct *,
 	struct vm_area_struct *, unsigned long addr, int new_below);
-extern void insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
+extern int insert_vm_struct(struct mm_struct *, struct vm_area_struct *);
 extern void __vma_link_rb(struct mm_struct *, struct vm_area_struct *,
 	struct rb_node **, struct rb_node *);
 extern struct vm_area_struct *copy_vma(struct vm_area_struct **,
diff -Nraup a/mm/mmap.c b/mm/mmap.c
--- a/mm/mmap.c	2004-11-21 23:58:29.059543875 -0800
+++ b/mm/mmap.c	2004-11-21 23:58:45.149387428 -0800
@@ -1913,7 +1913,7 @@ void exit_mmap(struct mm_struct *mm)
  * and into the inode's i_mmap tree.  If vm_file is non-NULL
  * then i_mmap_lock is taken here.
  */
-void insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
+int insert_vm_struct(struct mm_struct * mm, struct vm_area_struct * vma)
 {
 	struct vm_area_struct * __vma, * prev;
 	struct rb_node ** rb_link, * rb_parent;
@@ -1936,8 +1936,9 @@ void insert_vm_struct(struct mm_struct *
 	}
 	__vma = find_vma_prepare(mm,vma->vm_start,&prev,&rb_link,&rb_parent);
 	if (__vma && __vma->vm_start < vma->vm_end)
-		BUG();
+		return -ENOMEM;
 	vma_link(mm, vma, prev, rb_link, rb_parent);
+	return 0;
 }
 
 /*

^ permalink raw reply	[flat|nested] 13+ messages in thread
* RE: [PATCH 1/2] setup_arg_pages can insert overlapping vma
@ 2004-11-25  0:44 Zou, Nanhai
  0 siblings, 0 replies; 13+ messages in thread
From: Zou, Nanhai @ 2004-11-25  0:44 UTC (permalink / raw)
  To: Hugh Dickins
  Cc: Chris Wright, Andrew Morton, Linus Torvalds, Luck, Tony,
	Martin Schwidefsky, Andi Kleen, linux-kernel, linux-ia64



> -----Original Message-----
> From: Hugh Dickins [mailto:hugh@veritas.com]
> Sent: Thursday, November 25, 2004 12:31 AM
> To: Zou, Nanhai
> Cc: Chris Wright; Andrew Morton; Linus Torvalds; Luck, Tony; Martin
Schwidefsky;
> Andi Kleen; linux-kernel@vger.kernel.org; linux-ia64@vger.kernel.org
> Subject: RE: [PATCH 1/2] setup_arg_pages can insert overlapping vma
> 
> Thanks a lot for taking this further.
> 
> Yes, I agree, that's a welcome improvement.  I'm surprised if all
> those ia64_elf32_init checks are necessary, but better safe than
sorry.
> 
> Something crosses my mind, you'll know better than I: is it possible
to
> construct ELFs or A.OUTs which would need the check in
insert_vm_struct
> to be even more defensive?  That is, should it also be checking that
> vma->vm_end > vma->vm_start (vma being the one to be inserted)?
> Or that vma->vm_end <= TASK_SIZE?  If I remember rightly, a 0-length
> vma can cause confusion but survive quite well until exit_mmap's
> BUG_ON(mm->map_count).
  Since all elf and a.out sections are inserted with do_mmap which takes
start_addr and an unsigned length as parameters. And do_mmap also check
for zero lenth mapping.
I think we could not have vma with (vma->vm_end <= vm->vm_start) by
construct a bad binary executable.

Zou Nan hai 

^ permalink raw reply	[flat|nested] 13+ messages in thread
* RE: [PATCH 1/2] setup_arg_pages can insert overlapping vma
@ 2004-11-24 17:51 Luck, Tony
  0 siblings, 0 replies; 13+ messages in thread
From: Luck, Tony @ 2004-11-24 17:51 UTC (permalink / raw)
  To: Andrew Morton, Zou, Nanhai
  Cc: hugh, chrisw, torvalds, schwidefsky, ak, linux-kernel, linux-ia64

>"Zou, Nanhai" <nanhai.zou@intel.com> wrote:
>>
>> I think ia64 ia32
>> subsystem is not vulnerable to this kind of overlapping vm problem,
>> because it does not support a.out binary format, 
>> X84_64 is vulnerable to this. 
>
>Martin, Andi and Tony: could we please get a 2.6.10 ack on 
>this from you?

I can "Ack" it too ... but I gave Nanhai one of my "Signed-off-by:"
lines which he already attached to this patch :-)

-Tony

^ permalink raw reply	[flat|nested] 13+ messages in thread
[parent not found: <20041124152250.GA4797@mschwid3.boeblingen.de.ibm.com>]
* [PATCH 1/2] setup_arg_pages can insert overlapping vma
@ 2004-11-16 23:19 Chris Wright
  2004-11-18 17:39 ` Hugh Dickins
  0 siblings, 1 reply; 13+ messages in thread
From: Chris Wright @ 2004-11-16 23:19 UTC (permalink / raw)
  To: akpm, torvalds; +Cc: linux-kernel

Florian Heinz built an a.out binary that could map bss from 0x0 to
0xc0000000, and setup_arg_pages() would BUG() in insert_vma_struct
because the arg pages overlapped.  This just checks before inserting,
and bails out if it would overlap.

Signed-off-by: Chris Wright <chrisw@osdl.org>

===== fs/exec.c 1.143 vs edited =====
--- 1.143/fs/exec.c	2004-10-28 00:40:03 -07:00
+++ edited/fs/exec.c	2004-11-11 19:24:54 -08:00
@@ -413,6 +413,7 @@
 
 	down_write(&mm->mmap_sem);
 	{
+		struct vm_area_struct *vma;
 		mpnt->vm_mm = mm;
 #ifdef CONFIG_STACK_GROWSUP
 		mpnt->vm_start = stack_base;
@@ -433,6 +434,12 @@
 			mpnt->vm_flags = VM_STACK_FLAGS;
 		mpnt->vm_flags |= mm->def_flags;
 		mpnt->vm_page_prot = protection_map[mpnt->vm_flags & 0x7];
+		vma = find_vma(mm, mpnt->vm_start);
+		if (vma) {
+			up_write(&mm->mmap_sem);
+			kmem_cache_free(vm_area_cachep, mpnt);
+			return -ENOMEM;
+		}
 		insert_vm_struct(mm, mpnt);
 		mm->stack_vm = mm->total_vm = vma_pages(mpnt);
 	}

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

end of thread, other threads:[~2004-11-25  2:45 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2004-11-24  1:04 [PATCH 1/2] setup_arg_pages can insert overlapping vma Zou, Nanhai
2004-11-24  1:23 ` Andrew Morton
2004-11-24 10:45   ` Andi Kleen
2004-11-24 16:30 ` Hugh Dickins
2004-11-24 20:38 ` Chris Wright
  -- strict thread matches above, loose matches on Subject: below --
2004-11-25  0:44 Zou, Nanhai
2004-11-24 17:51 Luck, Tony
     [not found] <20041124152250.GA4797@mschwid3.boeblingen.de.ibm.com>
2004-11-24 16:41 ` Hugh Dickins
2004-11-16 23:19 Chris Wright
2004-11-18 17:39 ` Hugh Dickins
2004-11-18 18:55   ` Chris Wright
2004-11-18 19:41     ` Hugh Dickins
2004-11-18 20:07       ` Chris Wright

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

Powered by JetHome