]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ia64/sparc: fix local DoS with corrupted ELFs (CVE-2006-4538)
authorKirill Korotaev <dev@openvz.org>
Sat, 11 Nov 2006 00:08:49 +0000 (01:08 +0100)
committerAdrian Bunk <bunk@stusta.de>
Sat, 11 Nov 2006 00:08:49 +0000 (01:08 +0100)
This patch prevents cross-region mappings
on IA64 and SPARC which could lead to system crash.

Adrian Bunk:
Adapted to 2.6.16.

Signed-Off-By: Kirill Korotaev <dev@openvz.org>
Signed-off-by: Adrian Bunk <bunk@stusta.de>
arch/ia64/kernel/sys_ia64.c
arch/sparc/kernel/sys_sparc.c
arch/sparc64/kernel/sys_sparc.c
include/asm-ia64/mman.h
include/asm-sparc/mman.h
include/asm-sparc64/mman.h
mm/mmap.c

index c7b943f1019981d4c5c16b2a93d7e623b516f263..302e5b9bc9ae443a1900b36be59b407710bedde4 100644 (file)
@@ -164,10 +164,25 @@ sys_pipe (void)
        return retval;
 }
 
+int ia64_mmap_check(unsigned long addr, unsigned long len,
+               unsigned long flags)
+{
+       unsigned long roff;
+
+       /*
+        * Don't permit mappings into unmapped space, the virtual page table
+        * of a region, or across a region boundary.  Note: RGN_MAP_LIMIT is
+        * equal to 2^n-PAGE_SIZE (for some integer n <= 61) and len > 0.
+        */
+       roff = REGION_OFFSET(addr);
+       if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len)))
+               return -EINVAL;
+       return 0;
+}
+
 static inline unsigned long
 do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
 {
-       unsigned long roff;
        struct file *file = NULL;
 
        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
@@ -189,17 +204,6 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un
                goto out;
        }
 
-       /*
-        * Don't permit mappings into unmapped space, the virtual page table of a region,
-        * or across a region boundary.  Note: RGN_MAP_LIMIT is equal to 2^n-PAGE_SIZE
-        * (for some integer n <= 61) and len > 0.
-        */
-       roff = REGION_OFFSET(addr);
-       if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len))) {
-               addr = -EINVAL;
-               goto out;
-       }
-
        down_write(&current->mm->mmap_sem);
        addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
        up_write(&current->mm->mmap_sem);
index 0cdfc9d294b45a777b20ec249ed79094862b650f..fc8cdcc206b81024e467523326cabc740e3eedf3 100644 (file)
@@ -219,6 +219,21 @@ out:
        return err;
 }
 
+int sparc_mmap_check(unsigned long addr, unsigned long len, unsigned long flags)
+{
+       if (ARCH_SUN4C_SUN4 &&
+           (len > 0x20000000 ||
+            ((flags & MAP_FIXED) &&
+             addr < 0xe0000000 && addr + len > 0x20000000)))
+               return -EINVAL;
+
+       /* See asm-sparc/uaccess.h */
+       if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
+               return -EINVAL;
+
+       return 0;
+}
+
 /* Linux version of mmap */
 static unsigned long do_mmap2(unsigned long addr, unsigned long len,
        unsigned long prot, unsigned long flags, unsigned long fd,
@@ -233,25 +248,13 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len,
                        goto out;
        }
 
-       retval = -EINVAL;
        len = PAGE_ALIGN(len);
-       if (ARCH_SUN4C_SUN4 &&
-           (len > 0x20000000 ||
-            ((flags & MAP_FIXED) &&
-             addr < 0xe0000000 && addr + len > 0x20000000)))
-               goto out_putf;
-
-       /* See asm-sparc/uaccess.h */
-       if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
-               goto out_putf;
-
        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
 
        down_write(&current->mm->mmap_sem);
        retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
        up_write(&current->mm->mmap_sem);
 
-out_putf:
        if (file)
                fput(file);
 out:
index 5f8c822a2b4a4a3dbcdab26809d94a90679682d8..b7ebd48da1124d93bae7b01bad4186c76bd32d3f 100644 (file)
@@ -322,6 +322,23 @@ asmlinkage long sparc64_personality(unsigned long personality)
        return ret;
 }
 
+int sparc64_mmap_check(unsigned long addr, unsigned long len,
+               unsigned long flags)
+{
+       if (test_thread_flag(TIF_32BIT)) {
+               if (len > 0xf0000000UL ||
+                   ((flags & MAP_FIXED) && addr > 0xf0000000UL - len))
+                            return -EINVAL;
+       } else {
+               if (len > -PAGE_OFFSET ||
+                   ((flags & MAP_FIXED) &&
+                        addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
+                            return -EINVAL;
+        }
+
+       return 0;
+}
+
 /* Linux version of mmap */
 asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
        unsigned long prot, unsigned long flags, unsigned long fd,
@@ -337,24 +354,11 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
        }
        flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
        len = PAGE_ALIGN(len);
-       retval = -EINVAL;
-
-       if (test_thread_flag(TIF_32BIT)) {
-               if (len > 0xf0000000UL ||
-                   ((flags & MAP_FIXED) && addr > 0xf0000000UL - len))
-                       goto out_putf;
-       } else {
-               if (len > -PAGE_OFFSET ||
-                   ((flags & MAP_FIXED) &&
-                    addr < PAGE_OFFSET && addr + len > -PAGE_OFFSET))
-                       goto out_putf;
-       }
 
        down_write(&current->mm->mmap_sem);
        retval = do_mmap(file, addr, len, prot, flags, off);
        up_write(&current->mm->mmap_sem);
 
-out_putf:
        if (file)
                fput(file);
 out:
index 6ba179f1271845f92732ab46226f02874fbb714b..c73b87832a1e56a3b9cae408ed0d6f2f504e8195 100644 (file)
 #define MCL_CURRENT    1               /* lock all current mappings */
 #define MCL_FUTURE     2               /* lock all future mappings */
 
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+#define arch_mmap_check        ia64_mmap_check
+int ia64_mmap_check(unsigned long addr, unsigned long len,
+               unsigned long flags);
+#endif
+#endif
+
 #endif /* _ASM_IA64_MMAN_H */
index 88d1886abf3b4fabb8371bb6486c67f3937218be..b7dc40bc68f401cabbc89c556cb424070c652852 100644 (file)
 
 #define MADV_FREE      0x5             /* (Solaris) contents can be freed */
 
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+#define arch_mmap_check        sparc_mmap_check
+int sparc_mmap_check(unsigned long addr, unsigned long len,
+               unsigned long flags);
+#endif
+#endif
+
 #endif /* __SPARC_MMAN_H__ */
index 6fd878e614350d6ad5212b3795d1655f7077aaa6..8cc1860be63077f312eaef0832f31fb47de06b74 100644 (file)
 
 #define MADV_FREE      0x5             /* (Solaris) contents can be freed */
 
+#ifdef __KERNEL__
+#ifndef __ASSEMBLY__
+#define arch_mmap_check        sparc64_mmap_check
+int sparc64_mmap_check(unsigned long addr, unsigned long len,
+               unsigned long flags);
+#endif
+#endif
+
 #endif /* __SPARC64_MMAN_H__ */
index 47556d2b3e90ddbde4b0b8e69430ef5b3726e3af..fba355c065283091eff630d9719731ca6dced6b2 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
 #include <asm/cacheflush.h>
 #include <asm/tlb.h>
 
+#ifndef arch_mmap_check
+#define arch_mmap_check(addr, len, flags)      (0)
+#endif
+
 static void unmap_region(struct mm_struct *mm,
                struct vm_area_struct *vma, struct vm_area_struct *prev,
                unsigned long start, unsigned long end);
@@ -906,6 +910,10 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
        if (!len)
                return -EINVAL;
 
+       error = arch_mmap_check(addr, len, flags);
+       if (error)
+               return error;
+
        /* Careful about overflows.. */
        len = PAGE_ALIGN(len);
        if (!len || len > TASK_SIZE)
@@ -1846,6 +1854,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
        unsigned long flags;
        struct rb_node ** rb_link, * rb_parent;
        pgoff_t pgoff = addr >> PAGE_SHIFT;
+       int error;
 
        len = PAGE_ALIGN(len);
        if (!len)
@@ -1854,6 +1863,12 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
        if ((addr + len) > TASK_SIZE || (addr + len) < addr)
                return -EINVAL;
 
+       flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
+
+       error = arch_mmap_check(addr, len, flags);
+       if (error)
+               return error;
+
        /*
         * mlock MCL_FUTURE?
         */
@@ -1894,8 +1909,6 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
        if (security_vm_enough_memory(len >> PAGE_SHIFT))
                return -ENOMEM;
 
-       flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
-
        /* Can we just expand an old private anonymous mapping? */
        if (vma_merge(mm, prev, addr, addr + len, flags,
                                        NULL, NULL, pgoff, NULL))