Commit 05c19c43 authored by Kirill Korotaev's avatar Kirill Korotaev Committed by Adrian Bunk

ia64/sparc: fix local DoS with corrupted ELFs (CVE-2006-4538)

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: default avatarKirill Korotaev <dev@openvz.org>
Signed-off-by: default avatarAdrian Bunk <bunk@stusta.de>
parent 567e0e32
...@@ -164,10 +164,25 @@ sys_pipe (void) ...@@ -164,10 +164,25 @@ sys_pipe (void)
return retval; 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 static inline unsigned long
do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff) do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
{ {
unsigned long roff;
struct file *file = NULL; struct file *file = NULL;
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
...@@ -189,17 +204,6 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un ...@@ -189,17 +204,6 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un
goto out; 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); down_write(&current->mm->mmap_sem);
addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up_write(&current->mm->mmap_sem); up_write(&current->mm->mmap_sem);
......
...@@ -219,6 +219,21 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void __user ...@@ -219,6 +219,21 @@ asmlinkage int sys_ipc (uint call, int first, int second, int third, void __user
return err; 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 */ /* Linux version of mmap */
static unsigned long do_mmap2(unsigned long addr, unsigned long len, static unsigned long do_mmap2(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd, unsigned long prot, unsigned long flags, unsigned long fd,
...@@ -233,25 +248,13 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len, ...@@ -233,25 +248,13 @@ static unsigned long do_mmap2(unsigned long addr, unsigned long len,
goto out; goto out;
} }
retval = -EINVAL;
len = PAGE_ALIGN(len); 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); flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
down_write(&current->mm->mmap_sem); down_write(&current->mm->mmap_sem);
retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
up_write(&current->mm->mmap_sem); up_write(&current->mm->mmap_sem);
out_putf:
if (file) if (file)
fput(file); fput(file);
out: out:
......
...@@ -322,6 +322,23 @@ asmlinkage long sparc64_personality(unsigned long personality) ...@@ -322,6 +322,23 @@ asmlinkage long sparc64_personality(unsigned long personality)
return ret; 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 */ /* Linux version of mmap */
asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
unsigned long prot, unsigned long flags, unsigned long fd, unsigned long prot, unsigned long flags, unsigned long fd,
...@@ -337,24 +354,11 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, ...@@ -337,24 +354,11 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
} }
flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
len = PAGE_ALIGN(len); 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); down_write(&current->mm->mmap_sem);
retval = do_mmap(file, addr, len, prot, flags, off); retval = do_mmap(file, addr, len, prot, flags, off);
up_write(&current->mm->mmap_sem); up_write(&current->mm->mmap_sem);
out_putf:
if (file) if (file)
fput(file); fput(file);
out: out:
......
...@@ -22,4 +22,12 @@ ...@@ -22,4 +22,12 @@
#define MCL_CURRENT 1 /* lock all current mappings */ #define MCL_CURRENT 1 /* lock all current mappings */
#define MCL_FUTURE 2 /* lock all future 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 */ #endif /* _ASM_IA64_MMAN_H */
...@@ -35,4 +35,12 @@ ...@@ -35,4 +35,12 @@
#define MADV_FREE 0x5 /* (Solaris) contents can be freed */ #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__ */ #endif /* __SPARC_MMAN_H__ */
...@@ -35,4 +35,12 @@ ...@@ -35,4 +35,12 @@
#define MADV_FREE 0x5 /* (Solaris) contents can be freed */ #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__ */ #endif /* __SPARC64_MMAN_H__ */
...@@ -30,6 +30,10 @@ ...@@ -30,6 +30,10 @@
#include <asm/cacheflush.h> #include <asm/cacheflush.h>
#include <asm/tlb.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, static void unmap_region(struct mm_struct *mm,
struct vm_area_struct *vma, struct vm_area_struct *prev, struct vm_area_struct *vma, struct vm_area_struct *prev,
unsigned long start, unsigned long end); unsigned long start, unsigned long end);
...@@ -906,6 +910,10 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr, ...@@ -906,6 +910,10 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
if (!len) if (!len)
return -EINVAL; return -EINVAL;
error = arch_mmap_check(addr, len, flags);
if (error)
return error;
/* Careful about overflows.. */ /* Careful about overflows.. */
len = PAGE_ALIGN(len); len = PAGE_ALIGN(len);
if (!len || len > TASK_SIZE) if (!len || len > TASK_SIZE)
...@@ -1846,6 +1854,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len) ...@@ -1846,6 +1854,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
unsigned long flags; unsigned long flags;
struct rb_node ** rb_link, * rb_parent; struct rb_node ** rb_link, * rb_parent;
pgoff_t pgoff = addr >> PAGE_SHIFT; pgoff_t pgoff = addr >> PAGE_SHIFT;
int error;
len = PAGE_ALIGN(len); len = PAGE_ALIGN(len);
if (!len) if (!len)
...@@ -1854,6 +1863,12 @@ unsigned long do_brk(unsigned long addr, unsigned long len) ...@@ -1854,6 +1863,12 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
if ((addr + len) > TASK_SIZE || (addr + len) < addr) if ((addr + len) > TASK_SIZE || (addr + len) < addr)
return -EINVAL; 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? * mlock MCL_FUTURE?
*/ */
...@@ -1894,8 +1909,6 @@ unsigned long do_brk(unsigned long addr, unsigned long len) ...@@ -1894,8 +1909,6 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
if (security_vm_enough_memory(len >> PAGE_SHIFT)) if (security_vm_enough_memory(len >> PAGE_SHIFT))
return -ENOMEM; return -ENOMEM;
flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
/* Can we just expand an old private anonymous mapping? */ /* Can we just expand an old private anonymous mapping? */
if (vma_merge(mm, prev, addr, addr + len, flags, if (vma_merge(mm, prev, addr, addr + len, flags,
NULL, NULL, pgoff, NULL)) NULL, NULL, pgoff, NULL))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment