[PATCH] IA64,sparc: local DoS with corrupted ELFs
authorKirill Korotaev <dev@openvz.org>
Thu, 7 Sep 2006 10:17:04 +0000 (14:17 +0400)
committerLinus Torvalds <torvalds@g5.osdl.org>
Fri, 8 Sep 2006 15:40:46 +0000 (08:40 -0700)
This prevents cross-region mappings on IA64 and SPARC which could lead
to system crash.  They were correctly trapped for normal mmap() calls,
but not for the kernel internal calls generated by executable loading.

This code just moves the architecture-specific cross-region checks into
an arch-specific "arch_mmap_check()" macro, and defines that for the
architectures that needed it (ia64, sparc and sparc64).

Architectures that don't have any special requirements can just ignore
the new cross-region check, since the mmap() code will just notice on
its own when the macro isn't defined.

Signed-off-by: Pavel Emelianov <xemul@openvz.org>
Signed-off-by: Kirill Korotaev <dev@openvz.org>
Acked-by: David Miller <davem@davemloft.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
[ Cleaned up to not affect architectures that don't need it ]
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
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 40722d88607adf57de79dc158b08d769f767757b..9ef62a3fbfad5a37fad2df187603d7bb152f1468 100644 (file)
@@ -163,10 +163,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);
@@ -188,17 +203,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 a41c8a5c20070847a037a8b73ef4e7bcda0a53b4..94ff58c9d4a97725198e43bf47b92b83c13e1d0a 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 054d0abdb7eef49ad2db9f0a8eea114029dc7a94..bf5f14ee73def064e8d981707ad289e8f60d02a1 100644 (file)
@@ -548,6 +548,26 @@ 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 >= STACK_TOP32)
+                       return -EINVAL;
+
+               if ((flags & MAP_FIXED) && addr > STACK_TOP32 - len)
+                       return -EINVAL;
+       } else {
+               if (len >= VA_EXCLUDE_START)
+                       return -EINVAL;
+
+               if ((flags & MAP_FIXED) && invalid_64bit_range(addr, len))
+                       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,
@@ -563,27 +583,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 >= STACK_TOP32)
-                       goto out_putf;
-
-               if ((flags & MAP_FIXED) && addr > STACK_TOP32 - len)
-                       goto out_putf;
-       } else {
-               if (len >= VA_EXCLUDE_START)
-                       goto out_putf;
-
-               if ((flags & MAP_FIXED) && invalid_64bit_range(addr, len))
-                       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 c1868ecdbc5facfd8ba25835e71e05cfee9d9680..e66a0b524affef544588a8c4f8a92d3473b79a34 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);
@@ -913,6 +917,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)
@@ -1859,6 +1867,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)
@@ -1867,6 +1876,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?
         */
@@ -1907,8 +1922,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))