aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKirill Korotaev <dev@openvz.org>2006-09-07 06:17:04 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-08 11:40:46 -0400
commit3a459756810912d2c2bf188cef566af255936b4d (patch)
tree1b52d90a2412811ebf5078b4f55112864e1890df
parent10387e5eb45c6e48d67102b88229f5bc6037461c (diff)
[PATCH] IA64,sparc: local DoS with corrupted ELFs
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>
-rw-r--r--arch/ia64/kernel/sys_ia64.c28
-rw-r--r--arch/sparc/kernel/sys_sparc.c27
-rw-r--r--arch/sparc64/kernel/sys_sparc.c36
-rw-r--r--include/asm-ia64/mman.h8
-rw-r--r--include/asm-sparc/mman.h8
-rw-r--r--include/asm-sparc64/mman.h8
-rw-r--r--mm/mmap.c17
7 files changed, 90 insertions, 42 deletions
diff --git a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c
index 40722d88607a..9ef62a3fbfad 100644
--- a/arch/ia64/kernel/sys_ia64.c
+++ b/arch/ia64/kernel/sys_ia64.c
@@ -163,10 +163,25 @@ sys_pipe (void)
163 return retval; 163 return retval;
164} 164}
165 165
166int ia64_mmap_check(unsigned long addr, unsigned long len,
167 unsigned long flags)
168{
169 unsigned long roff;
170
171 /*
172 * Don't permit mappings into unmapped space, the virtual page table
173 * of a region, or across a region boundary. Note: RGN_MAP_LIMIT is
174 * equal to 2^n-PAGE_SIZE (for some integer n <= 61) and len > 0.
175 */
176 roff = REGION_OFFSET(addr);
177 if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len)))
178 return -EINVAL;
179 return 0;
180}
181
166static inline unsigned long 182static inline unsigned long
167do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff) 183do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff)
168{ 184{
169 unsigned long roff;
170 struct file *file = NULL; 185 struct file *file = NULL;
171 186
172 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 187 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
@@ -188,17 +203,6 @@ do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, un
188 goto out; 203 goto out;
189 } 204 }
190 205
191 /*
192 * Don't permit mappings into unmapped space, the virtual page table of a region,
193 * or across a region boundary. Note: RGN_MAP_LIMIT is equal to 2^n-PAGE_SIZE
194 * (for some integer n <= 61) and len > 0.
195 */
196 roff = REGION_OFFSET(addr);
197 if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len))) {
198 addr = -EINVAL;
199 goto out;
200 }
201
202 down_write(&current->mm->mmap_sem); 206 down_write(&current->mm->mmap_sem);
203 addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); 207 addr = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
204 up_write(&current->mm->mmap_sem); 208 up_write(&current->mm->mmap_sem);
diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c
index a41c8a5c2007..94ff58c9d4a9 100644
--- a/arch/sparc/kernel/sys_sparc.c
+++ b/arch/sparc/kernel/sys_sparc.c
@@ -219,6 +219,21 @@ out:
219 return err; 219 return err;
220} 220}
221 221
222int sparc_mmap_check(unsigned long addr, unsigned long len, unsigned long flags)
223{
224 if (ARCH_SUN4C_SUN4 &&
225 (len > 0x20000000 ||
226 ((flags & MAP_FIXED) &&
227 addr < 0xe0000000 && addr + len > 0x20000000)))
228 return -EINVAL;
229
230 /* See asm-sparc/uaccess.h */
231 if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
232 return -EINVAL;
233
234 return 0;
235}
236
222/* Linux version of mmap */ 237/* Linux version of mmap */
223static unsigned long do_mmap2(unsigned long addr, unsigned long len, 238static unsigned long do_mmap2(unsigned long addr, unsigned long len,
224 unsigned long prot, unsigned long flags, unsigned long fd, 239 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 goto out; 248 goto out;
234 } 249 }
235 250
236 retval = -EINVAL;
237 len = PAGE_ALIGN(len); 251 len = PAGE_ALIGN(len);
238 if (ARCH_SUN4C_SUN4 &&
239 (len > 0x20000000 ||
240 ((flags & MAP_FIXED) &&
241 addr < 0xe0000000 && addr + len > 0x20000000)))
242 goto out_putf;
243
244 /* See asm-sparc/uaccess.h */
245 if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE)
246 goto out_putf;
247
248 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 252 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
249 253
250 down_write(&current->mm->mmap_sem); 254 down_write(&current->mm->mmap_sem);
251 retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); 255 retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff);
252 up_write(&current->mm->mmap_sem); 256 up_write(&current->mm->mmap_sem);
253 257
254out_putf:
255 if (file) 258 if (file)
256 fput(file); 259 fput(file);
257out: 260out:
diff --git a/arch/sparc64/kernel/sys_sparc.c b/arch/sparc64/kernel/sys_sparc.c
index 054d0abdb7ee..bf5f14ee73de 100644
--- a/arch/sparc64/kernel/sys_sparc.c
+++ b/arch/sparc64/kernel/sys_sparc.c
@@ -548,6 +548,26 @@ asmlinkage long sparc64_personality(unsigned long personality)
548 return ret; 548 return ret;
549} 549}
550 550
551int sparc64_mmap_check(unsigned long addr, unsigned long len,
552 unsigned long flags)
553{
554 if (test_thread_flag(TIF_32BIT)) {
555 if (len >= STACK_TOP32)
556 return -EINVAL;
557
558 if ((flags & MAP_FIXED) && addr > STACK_TOP32 - len)
559 return -EINVAL;
560 } else {
561 if (len >= VA_EXCLUDE_START)
562 return -EINVAL;
563
564 if ((flags & MAP_FIXED) && invalid_64bit_range(addr, len))
565 return -EINVAL;
566 }
567
568 return 0;
569}
570
551/* Linux version of mmap */ 571/* Linux version of mmap */
552asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, 572asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
553 unsigned long prot, unsigned long flags, unsigned long fd, 573 unsigned long prot, unsigned long flags, unsigned long fd,
@@ -563,27 +583,11 @@ asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len,
563 } 583 }
564 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 584 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE);
565 len = PAGE_ALIGN(len); 585 len = PAGE_ALIGN(len);
566 retval = -EINVAL;
567
568 if (test_thread_flag(TIF_32BIT)) {
569 if (len >= STACK_TOP32)
570 goto out_putf;
571
572 if ((flags & MAP_FIXED) && addr > STACK_TOP32 - len)
573 goto out_putf;
574 } else {
575 if (len >= VA_EXCLUDE_START)
576 goto out_putf;
577
578 if ((flags & MAP_FIXED) && invalid_64bit_range(addr, len))
579 goto out_putf;
580 }
581 586
582 down_write(&current->mm->mmap_sem); 587 down_write(&current->mm->mmap_sem);
583 retval = do_mmap(file, addr, len, prot, flags, off); 588 retval = do_mmap(file, addr, len, prot, flags, off);
584 up_write(&current->mm->mmap_sem); 589 up_write(&current->mm->mmap_sem);
585 590
586out_putf:
587 if (file) 591 if (file)
588 fput(file); 592 fput(file);
589out: 593out:
diff --git a/include/asm-ia64/mman.h b/include/asm-ia64/mman.h
index 6ba179f12718..c73b87832a1e 100644
--- a/include/asm-ia64/mman.h
+++ b/include/asm-ia64/mman.h
@@ -22,4 +22,12 @@
22#define MCL_CURRENT 1 /* lock all current mappings */ 22#define MCL_CURRENT 1 /* lock all current mappings */
23#define MCL_FUTURE 2 /* lock all future mappings */ 23#define MCL_FUTURE 2 /* lock all future mappings */
24 24
25#ifdef __KERNEL__
26#ifndef __ASSEMBLY__
27#define arch_mmap_check ia64_mmap_check
28int ia64_mmap_check(unsigned long addr, unsigned long len,
29 unsigned long flags);
30#endif
31#endif
32
25#endif /* _ASM_IA64_MMAN_H */ 33#endif /* _ASM_IA64_MMAN_H */
diff --git a/include/asm-sparc/mman.h b/include/asm-sparc/mman.h
index 88d1886abf3b..b7dc40bc68f4 100644
--- a/include/asm-sparc/mman.h
+++ b/include/asm-sparc/mman.h
@@ -35,4 +35,12 @@
35 35
36#define MADV_FREE 0x5 /* (Solaris) contents can be freed */ 36#define MADV_FREE 0x5 /* (Solaris) contents can be freed */
37 37
38#ifdef __KERNEL__
39#ifndef __ASSEMBLY__
40#define arch_mmap_check sparc_mmap_check
41int sparc_mmap_check(unsigned long addr, unsigned long len,
42 unsigned long flags);
43#endif
44#endif
45
38#endif /* __SPARC_MMAN_H__ */ 46#endif /* __SPARC_MMAN_H__ */
diff --git a/include/asm-sparc64/mman.h b/include/asm-sparc64/mman.h
index 6fd878e61435..8cc1860be630 100644
--- a/include/asm-sparc64/mman.h
+++ b/include/asm-sparc64/mman.h
@@ -35,4 +35,12 @@
35 35
36#define MADV_FREE 0x5 /* (Solaris) contents can be freed */ 36#define MADV_FREE 0x5 /* (Solaris) contents can be freed */
37 37
38#ifdef __KERNEL__
39#ifndef __ASSEMBLY__
40#define arch_mmap_check sparc64_mmap_check
41int sparc64_mmap_check(unsigned long addr, unsigned long len,
42 unsigned long flags);
43#endif
44#endif
45
38#endif /* __SPARC64_MMAN_H__ */ 46#endif /* __SPARC64_MMAN_H__ */
diff --git a/mm/mmap.c b/mm/mmap.c
index c1868ecdbc5f..e66a0b524aff 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -30,6 +30,10 @@
30#include <asm/cacheflush.h> 30#include <asm/cacheflush.h>
31#include <asm/tlb.h> 31#include <asm/tlb.h>
32 32
33#ifndef arch_mmap_check
34#define arch_mmap_check(addr, len, flags) (0)
35#endif
36
33static void unmap_region(struct mm_struct *mm, 37static void unmap_region(struct mm_struct *mm,
34 struct vm_area_struct *vma, struct vm_area_struct *prev, 38 struct vm_area_struct *vma, struct vm_area_struct *prev,
35 unsigned long start, unsigned long end); 39 unsigned long start, unsigned long end);
@@ -913,6 +917,10 @@ unsigned long do_mmap_pgoff(struct file * file, unsigned long addr,
913 if (!len) 917 if (!len)
914 return -EINVAL; 918 return -EINVAL;
915 919
920 error = arch_mmap_check(addr, len, flags);
921 if (error)
922 return error;
923
916 /* Careful about overflows.. */ 924 /* Careful about overflows.. */
917 len = PAGE_ALIGN(len); 925 len = PAGE_ALIGN(len);
918 if (!len || len > TASK_SIZE) 926 if (!len || len > TASK_SIZE)
@@ -1859,6 +1867,7 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
1859 unsigned long flags; 1867 unsigned long flags;
1860 struct rb_node ** rb_link, * rb_parent; 1868 struct rb_node ** rb_link, * rb_parent;
1861 pgoff_t pgoff = addr >> PAGE_SHIFT; 1869 pgoff_t pgoff = addr >> PAGE_SHIFT;
1870 int error;
1862 1871
1863 len = PAGE_ALIGN(len); 1872 len = PAGE_ALIGN(len);
1864 if (!len) 1873 if (!len)
@@ -1867,6 +1876,12 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
1867 if ((addr + len) > TASK_SIZE || (addr + len) < addr) 1876 if ((addr + len) > TASK_SIZE || (addr + len) < addr)
1868 return -EINVAL; 1877 return -EINVAL;
1869 1878
1879 flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
1880
1881 error = arch_mmap_check(addr, len, flags);
1882 if (error)
1883 return error;
1884
1870 /* 1885 /*
1871 * mlock MCL_FUTURE? 1886 * mlock MCL_FUTURE?
1872 */ 1887 */
@@ -1907,8 +1922,6 @@ unsigned long do_brk(unsigned long addr, unsigned long len)
1907 if (security_vm_enough_memory(len >> PAGE_SHIFT)) 1922 if (security_vm_enough_memory(len >> PAGE_SHIFT))
1908 return -ENOMEM; 1923 return -ENOMEM;
1909 1924
1910 flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags;
1911
1912 /* Can we just expand an old private anonymous mapping? */ 1925 /* Can we just expand an old private anonymous mapping? */
1913 if (vma_merge(mm, prev, addr, addr + len, flags, 1926 if (vma_merge(mm, prev, addr, addr + len, flags,
1914 NULL, NULL, pgoff, NULL)) 1927 NULL, NULL, pgoff, NULL))