Gentoo Websites Logo
Go to: Gentoo Home Documentation Forums Lists Bugs Planet Store Wiki Get Gentoo!
Bug 76292 - CAN-2004-1235 (Vendor-Sec)
Summary: CAN-2004-1235 (Vendor-Sec)
Status: RESOLVED DUPLICATE of bug 77025
Alias: None
Product: Gentoo Security
Classification: Unclassified
Component: Kernel (show other bugs)
Hardware: All All
: High normal (vote)
Assignee: Gentoo Security
URL:
Whiteboard: CLASSIFIED
Keywords:
Depends on:
Blocks:
 
Reported: 2005-01-01 04:13 UTC by Sune Kloppenborg Jeppesen (RETIRED)
Modified: 2008-04-19 22:42 UTC (History)
0 users

See Also:
Package list:
Runtime testing required: ---


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Sune Kloppenborg Jeppesen (RETIRED) gentoo-dev 2005-01-01 04:13:50 UTC
Fix is still being discussed.

On Thu, Dec 30, 2004 at 03:48:35PM +0100, Paul Starzetz wrote:
> On Wed, 29 Dec 2004, Marcelo Tosatti wrote:
> 
> > Hi Paul,
> > 
> > Can you describe whats going on at the kernel level? 
> > 
> > I really dont have the time to read and understand the exploit. Will take 
> > some time to try it tomorrow. 
> 
> OK no prob, it was just too obvious for me.

Well wasnt for me :) 

> > The problem is in the sys_uselib() system call to select shared library, 
> > but what is the race scenario ? 
> 
> There is a missing down() on the current->mm->mmap_sem semaphore while 
> calling do_brk(). As you see (for 2.4 as well for 2.6) there is a 
> down/up_write pair before around do_mmap, but none around do_brk. And 
> looking into the code of sys_brk it is clear that do_brk is called with 
> the semaphore held.
> 
> In other words: if do_brk() sleeps on kmalloc for the vm_area_struct it 
> may insert the VMA in old place while the VMA list/tree has been modified 
> by another thread. That is used by the exploit to create two overlapping 
> VMAs like:
> 
> [A.......B] 
Comment 1 Sune Kloppenborg Jeppesen (RETIRED) gentoo-dev 2005-01-01 04:13:50 UTC
Fix is still being discussed.

On Thu, Dec 30, 2004 at 03:48:35PM +0100, Paul Starzetz wrote:
> On Wed, 29 Dec 2004, Marcelo Tosatti wrote:
> 
> > Hi Paul,
> > 
> > Can you describe whats going on at the kernel level? 
> > 
> > I really dont have the time to read and understand the exploit. Will take 
> > some time to try it tomorrow. 
> 
> OK no prob, it was just too obvious for me.

Well wasnt for me :) 

> > The problem is in the sys_uselib() system call to select shared library, 
> > but what is the race scenario ? 
> 
> There is a missing down() on the current->mm->mmap_sem semaphore while 
> calling do_brk(). As you see (for 2.4 as well for 2.6) there is a 
> down/up_write pair before around do_mmap, but none around do_brk. And 
> looking into the code of sys_brk it is clear that do_brk is called with 
> the semaphore held.
> 
> In other words: if do_brk() sleeps on kmalloc for the vm_area_struct it 
> may insert the VMA in old place while the VMA list/tree has been modified 
> by another thread. That is used by the exploit to create two overlapping 
> VMAs like:
> 
> [A.......B]  VMA N
>   [C...D]    VMA N+1
> 
> then it exploits the features of mremap() that calculates the maximum 
> possible address for extending a VMA by taking the next VMA and building 
> the difference between the requested address and next's vm_start... but if 
> this is less than the vm_end of the previous VMA this will result in a big 
> integer, something like (unsigned)-1 & ~4093 etc... The remaining work is 
> analogous to the do_brk exploit.
> 
> Note that this is not a bug in mremap. I have found at least three 
> different ways to exploit once we have overlapping VMAs...
> 
> Attached latest version, never tested on gcc 3.x (however statically 
> linked 2.95 version works on a gcc 3.x compiled kernel). May need to be 
> run few times with -s -b switches.

OK I see, thanks for the explanation.

Here is a fix against 2.4.29-pre3 to create new do_brk() to grab the mmap_sem and call 
__do_brk (old do_brk()).

This is much simpler than fixing each caller of do_brk - there are many in the binary 
format loaders (ELF and aout).

Andrew, if you're OK with the addition of lockless __do_brk() and locking do_brk() 
as the following patch I can prepare a v2.6 patch.

With this in place I can't crash my testbox anymore (it used to crash with Paul's
exploit) - your exploit now eats all memory+swap and triggers OOM killer.


diff -Nur linux-2.4.28.orig/arch/ia64/kernel/sys_ia64.c linux-2.4.28/arch/ia64/kernel/sys_ia64.c
--- linux-2.4.28.orig/arch/ia64/kernel/sys_ia64.c       2004-12-31 15:21:15.117588248 -0200
+++ linux-2.4.28/arch/ia64/kernel/sys_ia64.c    2004-12-31 15:29:43.833251712 -0200
@@ -142,7 +142,7 @@
                goto out;
 
        /* Ok, looks good - let it rip. */
-       if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
+       if (__do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
                goto out;
 set_brk:
        mm->brk = brk;
diff -Nur linux-2.4.28.orig/arch/mips/kernel/sysirix.c linux-2.4.28/arch/mips/kernel/sysirix.c
--- linux-2.4.28.orig/arch/mips/kernel/sysirix.c        2004-12-31 15:21:15.292561648 -0200
+++ linux-2.4.28/arch/mips/kernel/sysirix.c     2004-12-31 15:30:03.311290600 -0200
@@ -588,7 +588,7 @@
         * Ok, looks good - let it rip.
         */
        mm->brk = brk;
-       do_brk(oldbrk, newbrk-oldbrk);
+       __do_brk(oldbrk, newbrk-oldbrk);
        ret = 0;
 
 out:
diff -Nur linux-2.4.28.orig/arch/sparc/kernel/sys_sunos.c linux-2.4.28/arch/sparc/kernel/sys_sunos.c
--- linux-2.4.28.orig/arch/sparc/kernel/sys_sunos.c     2004-12-31 15:21:15.389546904 -0200
+++ linux-2.4.28/arch/sparc/kernel/sys_sunos.c  2004-12-31 15:30:44.029100552 -0200
@@ -205,7 +205,7 @@
         * Ok, we have probably got enough memory - let it rip.
         */
        current->mm->brk = brk;
-       do_brk(oldbrk, newbrk-oldbrk);
+       __do_brk(oldbrk, newbrk-oldbrk);
        retval = 0;
 out:
        up_write(&current->mm->mmap_sem);
diff -Nur linux-2.4.28.orig/arch/sparc64/kernel/sys_sunos32.c linux-2.4.28/arch/sparc64/kernel/sys_sunos32.c
--- linux-2.4.28.orig/arch/sparc64/kernel/sys_sunos32.c 2004-12-31 15:21:15.571519240 -0200
+++ linux-2.4.28/arch/sparc64/kernel/sys_sunos32.c      2004-12-31 15:31:24.532943032 -0200
@@ -167,7 +167,7 @@
                goto out;
        /* Ok, we have probably got enough memory - let it rip. */
        current->mm->brk = brk;
-       do_brk(oldbrk, newbrk-oldbrk);
+       __do_brk(oldbrk, newbrk-oldbrk);
        retval = 0;
 out:
        up_write(&current->mm->mmap_sem);
diff -Nur linux-2.4.28.orig/include/linux/mm.h linux-2.4.28/include/linux/mm.h
--- linux-2.4.28.orig/include/linux/mm.h        2004-12-31 15:21:17.710194112 -0200
+++ linux-2.4.28/include/linux/mm.h     2004-12-31 15:25:20.494285320 -0200
@@ -574,6 +574,7 @@
 
 extern int do_munmap(struct mm_struct *, unsigned long, size_t);
 
+extern unsigned long __do_brk(unsigned long, unsigned long);
 extern unsigned long do_brk(unsigned long, unsigned long);
 
 static inline void __vma_unlink(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev)
diff -Nur linux-2.4.28.orig/kernel/ksyms.c linux-2.4.28/kernel/ksyms.c
--- linux-2.4.28.orig/kernel/ksyms.c    2004-12-31 15:21:15.782487168 -0200
+++ linux-2.4.28/kernel/ksyms.c 2004-12-31 15:32:30.574903128 -0200
@@ -87,6 +87,7 @@
 /* process memory management */
 EXPORT_SYMBOL(do_mmap_pgoff);
 EXPORT_SYMBOL(do_munmap);
+EXPORT_SYMBOL(__do_brk);
 EXPORT_SYMBOL(do_brk);
 EXPORT_SYMBOL(exit_mm);
 EXPORT_SYMBOL(exit_files);
diff -Nur linux-2.4.28.orig/mm/mmap.c linux-2.4.28/mm/mmap.c
--- linux-2.4.28.orig/mm/mmap.c 2004-12-31 15:21:14.880624272 -0200
+++ linux-2.4.28/mm/mmap.c      2004-12-31 15:26:41.029042176 -0200
@@ -181,7 +181,7 @@
                goto out;
 
        /* Ok, looks good - let it rip. */
-       if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
+       if (__do_brk(oldbrk, newbrk-oldbrk) != oldbrk)
                goto out;
 set_brk:
        mm->brk = brk;
@@ -1031,12 +1031,13 @@
        return ret;
 }
 
+
 /*
  *  this is really a simplified "do_mmap".  it only handles
  *  anonymous maps.  eventually we may be able to do some
  *  brk-specific accounting here.
  */
-unsigned long do_brk(unsigned long addr, unsigned long len)
+unsigned long __do_brk(unsigned long addr, unsigned long len)
 {
        struct mm_struct * mm = current->mm;
        struct vm_area_struct * vma, * prev;
@@ -1116,6 +1117,18 @@
        return addr;
 }
 
+/* locking version of __do_brk. */
+unsigned long do_brk(unsigned long addr, unsigned long len)
+{
+       unsigned long ret;
+
+       down_write(&current->mm->mmap_sem);
+       ret = __do_brk(addr, len);
+       up_write(&current->mm->mmap_sem);
+
+       return ret;
+}
+
 /* Build the RB tree corresponding to the VMA list. */
 void build_mmap_rb(struct mm_struct * mm)
 {
_______________________________________________
Comment 2 Sune Kloppenborg Jeppesen (RETIRED) gentoo-dev 2005-01-04 00:44:37 UTC
New patches for both 2.4 and 2.6:

Here they are - I haven't been able to run the exploit with success on v2.6.10 -
it segfaults earlier than expected on mmap2 - maybe its my compiler. Someone please test the following patch on v2.6.

v2.6.10-mm1 oops on my face (during console related routines) with or without the fix.

The v2.4 version has been tested with success and does stop the exploit. 

Kudos to Paul Starzetz for finding this!


diff -Nur linux-2.4.28.orig/arch/mips/kernel/irixelf.c linux-2.4.28/arch/mips/kernel/irixelf.c
--- linux-2.4.28.orig/arch/mips/kernel/irixelf.c        2005-01-03 20:07:13.325584848 -0200
+++ linux-2.4.28/arch/mips/kernel/irixelf.c     2005-01-03 20:17:00.192367536 -0200
@@ -130,7 +130,7 @@
        end = PAGE_ALIGN(end);
        if (end <= start)
                return;
-       do_brk(start, end - start);
+       do_brk_locked(start, end - start);
 }
 
 
@@ -379,7 +379,7 @@
 
        /* Map the last of the bss segment */
        if (last_bss > len) {
-               do_brk(len, (last_bss - len));
+               do_brk_locked(len, (last_bss - len));
        }
        kfree(elf_phdata);
 
@@ -567,7 +567,7 @@
        unsigned long v;
        struct prda *pp;
 
-       v =  do_brk (PRDA_ADDRESS, PAGE_SIZE);
+       v =  do_brk_locked (PRDA_ADDRESS, PAGE_SIZE);
 
        if (v < 0)
                return;
@@ -859,7 +859,7 @@
        len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000;
        bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
        if (bss > len)
-         do_brk(len, bss-len);
+         do_brk_locked(len, bss-len);
        kfree(elf_phdata);
        return 0;
 }
diff -Nur linux-2.4.28.orig/arch/sparc64/kernel/binfmt_aout32.c linux-2.4.28/arch/sparc64/kernel/binfmt_aout32.c
--- linux-2.4.28.orig/arch/sparc64/kernel/binfmt_aout32.c       2005-01-03 20:07:13.922494104 -0200
+++ linux-2.4.28/arch/sparc64/kernel/binfmt_aout32.c    2005-01-03 20:19:03.241661200 -0200
@@ -49,7 +49,7 @@
        end = PAGE_ALIGN(end);
        if (end <= start)
                return;
-       do_brk(start, end - start);
+       do_brk_locked(start, end - start);
 }
 
 /*
@@ -246,10 +246,10 @@
        if (N_MAGIC(ex) == NMAGIC) {
                loff_t pos = fd_offset;
                /* Fuck me plenty... */
-               error = do_brk(N_TXTADDR(ex), ex.a_text);
+               error = do_brk_locked(N_TXTADDR(ex), ex.a_text);
                bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
                          ex.a_text, &pos);
-               error = do_brk(N_DATADDR(ex), ex.a_data);
+               error = do_brk_locked(N_DATADDR(ex), ex.a_data);
                bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
                          ex.a_data, &pos);
                goto beyond_if;
@@ -257,7 +257,7 @@
 
        if (N_MAGIC(ex) == OMAGIC) {
                loff_t pos = fd_offset;
-               do_brk(N_TXTADDR(ex) & PAGE_MASK,
+               do_brk_locked(N_TXTADDR(ex) & PAGE_MASK,
                        ex.a_text+ex.a_data + PAGE_SIZE - 1);
                bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
                          ex.a_text+ex.a_data, &pos);
@@ -272,7 +272,7 @@
 
                if (!bprm->file->f_op->mmap) {
                        loff_t pos = fd_offset;
-                       do_brk(0, ex.a_text+ex.a_data);
+                       do_brk_locked(0, ex.a_text+ex.a_data);
                        bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
                                  ex.a_text+ex.a_data, &pos);
                        goto beyond_if;
@@ -388,7 +388,7 @@
        len = PAGE_ALIGN(ex.a_text + ex.a_data);
        bss = ex.a_text + ex.a_data + ex.a_bss;
        if (bss > len) {
-               error = do_brk(start_addr + len, bss - len);
+               error = do_brk_locked(start_addr + len, bss - len);
                retval = error;
                if (error != start_addr + len)
                        goto out;
diff -Nur linux-2.4.28.orig/fs/binfmt_aout.c linux-2.4.28/fs/binfmt_aout.c
--- linux-2.4.28.orig/fs/binfmt_aout.c  2005-01-03 20:07:15.971182656 -0200
+++ linux-2.4.28/fs/binfmt_aout.c       2005-01-03 20:21:35.590500656 -0200
@@ -45,7 +45,7 @@
        end = PAGE_ALIGN(end);
        if (end <= start)
                return;
-       do_brk(start, end - start);
+       do_brk_locked(start, end - start);
 }
 
 /*
@@ -312,10 +312,10 @@
                loff_t pos = fd_offset;
                /* Fuck me plenty... */
                /* <AOL></AOL> */
-               error = do_brk(N_TXTADDR(ex), ex.a_text);
+               error = do_brk_locked(N_TXTADDR(ex), ex.a_text);
                bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
                          ex.a_text, &pos);
-               error = do_brk(N_DATADDR(ex), ex.a_data);
+               error = do_brk_locked(N_DATADDR(ex), ex.a_data);
                bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
                          ex.a_data, &pos);
                goto beyond_if;
@@ -336,7 +336,7 @@
                map_size = ex.a_text+ex.a_data;
 #endif
 
-               error = do_brk(text_addr & PAGE_MASK, map_size);
+               error = do_brk_locked(text_addr & PAGE_MASK, map_size);
                if (error != (text_addr & PAGE_MASK)) {
                        send_sig(SIGKILL, current, 0);
                        return error;
@@ -370,7 +370,7 @@
 
                if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
                        loff_t pos = fd_offset;
-                       do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
+                       do_brk_locked(N_TXTADDR(ex), ex.a_text+ex.a_data);
                        bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
                                        ex.a_text+ex.a_data, &pos);
                        flush_icache_range((unsigned long) N_TXTADDR(ex),
@@ -467,7 +467,7 @@
                        error_time = jiffies;
                }
 
-               do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
+               do_brk_locked(start_addr, ex.a_text + ex.a_data + ex.a_bss);
                
                file->f_op->read(file, (char *)start_addr,
                        ex.a_text + ex.a_data, &pos);
@@ -491,7 +491,7 @@
        len = PAGE_ALIGN(ex.a_text + ex.a_data);
        bss = ex.a_text + ex.a_data + ex.a_bss;
        if (bss > len) {
-               error = do_brk(start_addr + len, bss - len);
+               error = do_brk_locked(start_addr + len, bss - len);
                retval = error;
                if (error != start_addr + len)
                        goto out;
diff -Nur linux-2.4.28.orig/fs/binfmt_elf.c linux-2.4.28/fs/binfmt_elf.c
--- linux-2.4.28.orig/fs/binfmt_elf.c   2005-01-03 20:07:15.863199072 -0200
+++ linux-2.4.28/fs/binfmt_elf.c        2005-01-03 20:20:08.155792752 -0200
@@ -85,7 +85,7 @@
        end = ELF_PAGEALIGN(end);
        if (end <= start)
                return;
-       do_brk(start, end - start);
+       do_brk_locked(start, end - start);
 }
 
 
@@ -361,7 +361,7 @@
 
        /* Map the last of the bss segment */
        if (last_bss > elf_bss)
-               do_brk(elf_bss, last_bss - elf_bss);
+               do_brk_locked(elf_bss, last_bss - elf_bss);
 
        *interp_load_addr = load_addr;
        error = ((unsigned long) interp_elf_ex->e_entry) + load_addr;
@@ -398,7 +398,7 @@
                goto out;
        }
 
-       do_brk(0, text_data);
+       do_brk_locked(0, text_data);
        if (!interpreter->f_op || !interpreter->f_op->read)
                goto out;
        if (interpreter->f_op->read(interpreter, addr, text_data, &offset) < 0)
@@ -406,7 +406,7 @@
        flush_icache_range((unsigned long)addr,
                           (unsigned long)addr + text_data);
 
-       do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1),
+       do_brk_locked(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1),
                interp_ex->a_bss);
        elf_entry = interp_ex->a_entry;
 
@@ -920,7 +920,7 @@
        len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + ELF_MIN_ALIGN - 1);
        bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
        if (bss > len)
-               do_brk(len, bss - len);
+               do_brk_locked(len, bss - len);
        error = 0;
 
 out_free_ph:
diff -Nur linux-2.4.28.orig/include/linux/mm.h linux-2.4.28/include/linux/mm.h
--- linux-2.4.28.orig/include/linux/mm.h        2005-01-03 20:07:14.320433608 -0200
+++ linux-2.4.28/include/linux/mm.h     2005-01-03 20:23:23.420108064 -0200
@@ -570,6 +570,7 @@
 extern int do_munmap(struct mm_struct *, unsigned long, size_t);
 
 extern unsigned long do_brk(unsigned long, unsigned long);
+extern unsigned long do_brk_locked(unsigned long, unsigned long);
 
 static inline void __vma_unlink(struct mm_struct * mm, struct vm_area_struct * vma, struct vm_area_struct * prev)
 {
diff -Nur linux-2.4.28.orig/kernel/ksyms.c linux-2.4.28/kernel/ksyms.c
--- linux-2.4.28.orig/kernel/ksyms.c    2005-01-03 20:07:15.845201808 -0200
+++ linux-2.4.28/kernel/ksyms.c 2005-01-03 20:13:58.702958120 -0200
@@ -88,6 +88,7 @@
 EXPORT_SYMBOL(do_mmap_pgoff);
 EXPORT_SYMBOL(do_munmap);
 EXPORT_SYMBOL(do_brk);
+EXPORT_SYMBOL(do_brk_locked);
 EXPORT_SYMBOL(exit_mm);
 EXPORT_SYMBOL(exit_files);
 EXPORT_SYMBOL(exit_fs);
diff -Nur linux-2.4.28.orig/mm/mmap.c linux-2.4.28/mm/mmap.c
--- linux-2.4.28.orig/mm/mmap.c 2005-01-03 20:07:15.852200744 -0200
+++ linux-2.4.28/mm/mmap.c      2005-01-03 20:13:14.122735344 -0200
@@ -1116,6 +1116,21 @@
        return addr;
 }
 
+/* locking version of do_brk. */
+unsigned long do_brk_locked(unsigned long addr, unsigned long len)
+{
+       unsigned long ret;
+
+       down_write(&current->mm->mmap_sem);
+       ret = do_brk(addr, len);
+       up_write(&current->mm->mmap_sem);
+
+       return ret;
+}
+
+
+
+
 /* Build the RB tree corresponding to the VMA list. */
 void build_mmap_rb(struct mm_struct * mm)
 {
diff -Nur linux-2.6.10.orig/arch/mips/kernel/irixelf.c linux-2.6.10/arch/mips/kernel/irixelf.c
--- linux-2.6.10.orig/arch/mips/kernel/irixelf.c        2005-01-03 16:17:00.000000000 -0200
+++ linux-2.6.10/arch/mips/kernel/irixelf.c     2005-01-03 16:44:59.909144520 -0200
@@ -127,7 +127,7 @@
        end = PAGE_ALIGN(end);
        if (end <= start)
                return;
-       do_brk(start, end - start);
+       do_brk_locked(start, end - start);
 }
 
 
diff -Nur linux-2.6.10.orig/arch/x86_64/ia32/ia32_aout.c linux-2.6.10/arch/x86_64/ia32/ia32_aout.c
--- linux-2.6.10.orig/arch/x86_64/ia32/ia32_aout.c      2005-01-03 16:17:04.000000000 -0200
+++ linux-2.6.10/arch/x86_64/ia32/ia32_aout.c   2005-01-03 16:46:53.846823360 -0200
@@ -115,7 +115,7 @@
        end = PAGE_ALIGN(end);
        if (end <= start)
                return;
-       do_brk(start, end - start);
+       do_brk_locked(start, end - start);
 }
 
 #if CORE_DUMP
@@ -325,7 +325,7 @@
                pos = 32;
                map_size = ex.a_text+ex.a_data;
 
-               error = do_brk(text_addr & PAGE_MASK, map_size);
+               error = do_brk_locked(text_addr & PAGE_MASK, map_size);
                if (error != (text_addr & PAGE_MASK)) {
                        send_sig(SIGKILL, current, 0);
                        return error;
@@ -361,7 +361,7 @@
 
                if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
                        loff_t pos = fd_offset;
-                       do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
+                       do_brk_locked(N_TXTADDR(ex), ex.a_text+ex.a_data);
                        bprm->file->f_op->read(bprm->file,(char *)N_TXTADDR(ex),
                                        ex.a_text+ex.a_data, &pos);
                        flush_icache_range((unsigned long) N_TXTADDR(ex),
@@ -470,7 +470,7 @@
                }
 #endif
 
-               do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
+               do_brk_locked(start_addr, ex.a_text + ex.a_data + ex.a_bss);
                
                file->f_op->read(file, (char *)start_addr,
                        ex.a_text + ex.a_data, &pos);
@@ -494,7 +494,7 @@
        len = PAGE_ALIGN(ex.a_text + ex.a_data);
        bss = ex.a_text + ex.a_data + ex.a_bss;
        if (bss > len) {
-               error = do_brk(start_addr + len, bss - len);
+               error = do_brk_locked(start_addr + len, bss - len);
                retval = error;
                if (error != start_addr + len)
                        goto out;
diff -Nur linux-2.6.10.orig/fs/binfmt_aout.c linux-2.6.10/fs/binfmt_aout.c
--- linux-2.6.10.orig/fs/binfmt_aout.c  2005-01-03 16:17:07.000000000 -0200
+++ linux-2.6.10/fs/binfmt_aout.c       2005-01-03 16:42:25.212661960 -0200
@@ -50,7 +50,7 @@
        start = PAGE_ALIGN(start);
        end = PAGE_ALIGN(end);
        if (end > start) {
-               unsigned long addr = do_brk(start, end - start);
+               unsigned long addr = do_brk_locked(start, end - start);
                if (BAD_ADDR(addr))
                        return addr;
        }
@@ -323,10 +323,10 @@
                loff_t pos = fd_offset;
                /* Fuck me plenty... */
                /* <AOL></AOL> */
-               error = do_brk(N_TXTADDR(ex), ex.a_text);
+               error = do_brk_locked(N_TXTADDR(ex), ex.a_text);
                bprm->file->f_op->read(bprm->file, (char *) N_TXTADDR(ex),
                          ex.a_text, &pos);
-               error = do_brk(N_DATADDR(ex), ex.a_data);
+               error = do_brk_locked(N_DATADDR(ex), ex.a_data);
                bprm->file->f_op->read(bprm->file, (char *) N_DATADDR(ex),
                          ex.a_data, &pos);
                goto beyond_if;
@@ -347,7 +347,7 @@
                map_size = ex.a_text+ex.a_data;
 #endif
 
-               error = do_brk(text_addr & PAGE_MASK, map_size);
+               error = do_brk_locked(text_addr & PAGE_MASK, map_size);
                if (error != (text_addr & PAGE_MASK)) {
                        send_sig(SIGKILL, current, 0);
                        return error;
@@ -382,7 +382,7 @@
 
                if (!bprm->file->f_op->mmap||((fd_offset & ~PAGE_MASK) != 0)) {
                        loff_t pos = fd_offset;
-                       do_brk(N_TXTADDR(ex), ex.a_text+ex.a_data);
+                       do_brk_locked(N_TXTADDR(ex), ex.a_text+ex.a_data);
                        bprm->file->f_op->read(bprm->file,
                                        (char __user *)N_TXTADDR(ex),
                                        ex.a_text+ex.a_data, &pos);
@@ -488,7 +488,7 @@
                        error_time = jiffies;
                }
 
-               do_brk(start_addr, ex.a_text + ex.a_data + ex.a_bss);
+               do_brk_locked(start_addr, ex.a_text + ex.a_data + ex.a_bss);
                
                file->f_op->read(file, (char __user *)start_addr,
                        ex.a_text + ex.a_data, &pos);
@@ -512,7 +512,7 @@
        len = PAGE_ALIGN(ex.a_text + ex.a_data);
        bss = ex.a_text + ex.a_data + ex.a_bss;
        if (bss > len) {
-               error = do_brk(start_addr + len, bss - len);
+               error = do_brk_locked(start_addr + len, bss - len);
                retval = error;
                if (error != start_addr + len)
                        goto out;
diff -Nur linux-2.6.10.orig/fs/binfmt_elf.c linux-2.6.10/fs/binfmt_elf.c
--- linux-2.6.10.orig/fs/binfmt_elf.c   2005-01-03 16:17:07.000000000 -0200
+++ linux-2.6.10/fs/binfmt_elf.c        2005-01-03 16:43:03.265876992 -0200
@@ -88,7 +88,7 @@
        start = ELF_PAGEALIGN(start);
        end = ELF_PAGEALIGN(end);
        if (end > start) {
-               unsigned long addr = do_brk(start, end - start);
+               unsigned long addr = do_brk_locked(start, end - start);
                if (BAD_ADDR(addr))
                        return addr;
        }
@@ -408,7 +408,7 @@
 
        /* Map the last of the bss segment */
        if (last_bss > elf_bss) {
-               error = do_brk(elf_bss, last_bss - elf_bss);
+               error = do_brk_locked(elf_bss, last_bss - elf_bss);
                if (BAD_ADDR(error))
                        goto out_close;
        }
@@ -448,7 +448,7 @@
                goto out;
        }
 
-       do_brk(0, text_data);
+       do_brk_locked(0, text_data);
        if (!interpreter->f_op || !interpreter->f_op->read)
                goto out;
        if (interpreter->f_op->read(interpreter, addr, text_data, &offset) < 0)
@@ -456,7 +456,7 @@
        flush_icache_range((unsigned long)addr,
                           (unsigned long)addr + text_data);
 
-       do_brk(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1),
+       do_brk_locked(ELF_PAGESTART(text_data + ELF_MIN_ALIGN - 1),
                interp_ex->a_bss);
        elf_entry = interp_ex->a_entry;
 
@@ -1025,7 +1025,7 @@
        len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + ELF_MIN_ALIGN - 1);
        bss = elf_phdata->p_memsz + elf_phdata->p_vaddr;
        if (bss > len)
-               do_brk(len, bss - len);
+               do_brk_locked(len, bss - len);
        error = 0;
 
 out_free_ph:
diff -Nur linux-2.6.10.orig/include/linux/mm.h linux-2.6.10/include/linux/mm.h
--- linux-2.6.10.orig/include/linux/mm.h        2005-01-03 16:17:05.000000000 -0200
+++ linux-2.6.10/include/linux/mm.h     2005-01-03 16:51:55.686936688 -0200
@@ -751,6 +751,7 @@
 extern int do_munmap(struct mm_struct *, unsigned long, size_t);
 
 extern unsigned long do_brk(unsigned long, unsigned long);
+extern unsigned long do_brk_locked(unsigned long, unsigned long);
 
 /* filemap.c */
 extern unsigned long page_unuse(struct page *);
diff -Nur linux-2.6.10.orig/mm/mmap.c linux-2.6.10/mm/mmap.c
--- linux-2.6.10.orig/mm/mmap.c 2005-01-03 16:17:07.000000000 -0200
+++ linux-2.6.10/mm/mmap.c      2005-01-03 16:51:17.000000000 -0200
@@ -1859,6 +1859,20 @@
 
 EXPORT_SYMBOL(do_brk);
 
+/* locking version of do_brk. */
+unsigned long do_brk_locked(unsigned long addr, unsigned long len)
+{
+       unsigned long ret;
+
+       down_write(&current->mm->mmap_sem);
+       ret = do_brk(addr, len);
+       up_write(&current->mm->mmap_sem);
+
+       return ret;
+}
+
+EXPORT_SYMBOL(do_brk_locked);
+
 /* Release all mmaps. */
 void exit_mmap(struct mm_struct *mm)
 {
diff -Nur linux-2.6.10.orig/mm/nommu.c linux-2.6.10/mm/nommu.c
--- linux-2.6.10.orig/mm/nommu.c        2005-01-03 16:17:07.000000000 -0200
+++ linux-2.6.10/mm/nommu.c     2005-01-03 16:52:31.000000000 -0200
@@ -857,6 +857,11 @@
        return -ENOMEM;
 }
 
+unsigned long do_brk_locked(unsigned long addr, unsigned long len)
+{
+       return -ENOMEM;
+}
+
 /*
  * Expand (or shrink) an existing mapping, potentially moving it at the
  * same time (controlled by the MREMAP_MAYMOVE flag and available VM space)
Comment 3 Thierry Carrez (RETIRED) gentoo-dev 2005-01-07 07:45:49 UTC
Now public through bug 77025
This one is CLASSIFIED so it must stay Restricted (forever)

*** This bug has been marked as a duplicate of 77025 ***