Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

authored by

Kirill Korotaev and committed by
Linus Torvalds
3a459756 10387e5e

+90 -42
+16 -12
arch/ia64/kernel/sys_ia64.c
··· 163 163 return retval; 164 164 } 165 165 166 + int 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 + 166 182 static inline unsigned long 167 183 do_mmap2 (unsigned long addr, unsigned long len, int prot, int flags, int fd, unsigned long pgoff) 168 184 { 169 - unsigned long roff; 170 185 struct file *file = NULL; 171 186 172 187 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); ··· 199 184 /* Careful about overflows.. */ 200 185 len = PAGE_ALIGN(len); 201 186 if (!len || len > TASK_SIZE) { 202 - addr = -EINVAL; 203 - goto out; 204 - } 205 - 206 - /* 207 - * Don't permit mappings into unmapped space, the virtual page table of a region, 208 - * or across a region boundary. Note: RGN_MAP_LIMIT is equal to 2^n-PAGE_SIZE 209 - * (for some integer n <= 61) and len > 0. 210 - */ 211 - roff = REGION_OFFSET(addr); 212 - if ((len > RGN_MAP_LIMIT) || (roff > (RGN_MAP_LIMIT - len))) { 213 187 addr = -EINVAL; 214 188 goto out; 215 189 }
+15 -12
arch/sparc/kernel/sys_sparc.c
··· 219 219 return err; 220 220 } 221 221 222 + int 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 237 /* Linux version of mmap */ 223 238 static unsigned long do_mmap2(unsigned long addr, unsigned long len, 224 239 unsigned long prot, unsigned long flags, unsigned long fd, ··· 248 233 goto out; 249 234 } 250 235 251 - retval = -EINVAL; 252 236 len = PAGE_ALIGN(len); 253 - if (ARCH_SUN4C_SUN4 && 254 - (len > 0x20000000 || 255 - ((flags & MAP_FIXED) && 256 - addr < 0xe0000000 && addr + len > 0x20000000))) 257 - goto out_putf; 258 - 259 - /* See asm-sparc/uaccess.h */ 260 - if (len > TASK_SIZE - PAGE_SIZE || addr + len > TASK_SIZE - PAGE_SIZE) 261 - goto out_putf; 262 - 263 237 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 264 238 265 239 down_write(&current->mm->mmap_sem); 266 240 retval = do_mmap_pgoff(file, addr, len, prot, flags, pgoff); 267 241 up_write(&current->mm->mmap_sem); 268 242 269 - out_putf: 270 243 if (file) 271 244 fput(file); 272 245 out:
+20 -16
arch/sparc64/kernel/sys_sparc.c
··· 548 548 return ret; 549 549 } 550 550 551 + int 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 571 /* Linux version of mmap */ 552 572 asmlinkage unsigned long sys_mmap(unsigned long addr, unsigned long len, 553 573 unsigned long prot, unsigned long flags, unsigned long fd, ··· 583 563 } 584 564 flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); 585 565 len = PAGE_ALIGN(len); 586 - retval = -EINVAL; 587 - 588 - if (test_thread_flag(TIF_32BIT)) { 589 - if (len >= STACK_TOP32) 590 - goto out_putf; 591 - 592 - if ((flags & MAP_FIXED) && addr > STACK_TOP32 - len) 593 - goto out_putf; 594 - } else { 595 - if (len >= VA_EXCLUDE_START) 596 - goto out_putf; 597 - 598 - if ((flags & MAP_FIXED) && invalid_64bit_range(addr, len)) 599 - goto out_putf; 600 - } 601 566 602 567 down_write(&current->mm->mmap_sem); 603 568 retval = do_mmap(file, addr, len, prot, flags, off); 604 569 up_write(&current->mm->mmap_sem); 605 570 606 - out_putf: 607 571 if (file) 608 572 fput(file); 609 573 out:
+8
include/asm-ia64/mman.h
··· 22 22 #define MCL_CURRENT 1 /* lock all current mappings */ 23 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 28 + int ia64_mmap_check(unsigned long addr, unsigned long len, 29 + unsigned long flags); 30 + #endif 31 + #endif 32 + 25 33 #endif /* _ASM_IA64_MMAN_H */
+8
include/asm-sparc/mman.h
··· 35 35 36 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 41 + int sparc_mmap_check(unsigned long addr, unsigned long len, 42 + unsigned long flags); 43 + #endif 44 + #endif 45 + 38 46 #endif /* __SPARC_MMAN_H__ */
+8
include/asm-sparc64/mman.h
··· 35 35 36 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 41 + int sparc64_mmap_check(unsigned long addr, unsigned long len, 42 + unsigned long flags); 43 + #endif 44 + #endif 45 + 38 46 #endif /* __SPARC64_MMAN_H__ */
+15 -2
mm/mmap.c
··· 30 30 #include <asm/cacheflush.h> 31 31 #include <asm/tlb.h> 32 32 33 + #ifndef arch_mmap_check 34 + #define arch_mmap_check(addr, len, flags) (0) 35 + #endif 36 + 33 37 static void unmap_region(struct mm_struct *mm, 34 38 struct vm_area_struct *vma, struct vm_area_struct *prev, 35 39 unsigned long start, unsigned long end); ··· 916 912 917 913 if (!len) 918 914 return -EINVAL; 915 + 916 + error = arch_mmap_check(addr, len, flags); 917 + if (error) 918 + return error; 919 919 920 920 /* Careful about overflows.. */ 921 921 len = PAGE_ALIGN(len); ··· 1867 1859 unsigned long flags; 1868 1860 struct rb_node ** rb_link, * rb_parent; 1869 1861 pgoff_t pgoff = addr >> PAGE_SHIFT; 1862 + int error; 1870 1863 1871 1864 len = PAGE_ALIGN(len); 1872 1865 if (!len) ··· 1875 1866 1876 1867 if ((addr + len) > TASK_SIZE || (addr + len) < addr) 1877 1868 return -EINVAL; 1869 + 1870 + flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags; 1871 + 1872 + error = arch_mmap_check(addr, len, flags); 1873 + if (error) 1874 + return error; 1878 1875 1879 1876 /* 1880 1877 * mlock MCL_FUTURE? ··· 1921 1906 1922 1907 if (security_vm_enough_memory(len >> PAGE_SHIFT)) 1923 1908 return -ENOMEM; 1924 - 1925 - flags = VM_DATA_DEFAULT_FLAGS | VM_ACCOUNT | mm->def_flags; 1926 1909 1927 1910 /* Can we just expand an old private anonymous mapping? */ 1928 1911 if (vma_merge(mm, prev, addr, addr + len, flags,