2008-01-30 13:31:07 +01:00
/*
2008-01-30 13:31:10 +01:00
* Flexible mmap layout support
2008-01-30 13:31:07 +01:00
*
* Based on code by Ingo Molnar and Andi Kleen , copyrighted
* as follows :
*
2009-01-31 02:03:42 +01:00
* Copyright 2003 - 2009 Red Hat Inc .
2008-01-30 13:31:07 +01:00
* All Rights Reserved .
* Copyright 2005 Andi Kleen , SUSE Labs .
* Copyright 2007 Jiri Kosina , SUSE Labs .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2006-01-17 07:03:38 +01:00
*/
2008-01-30 13:31:07 +01:00
# include <linux/personality.h>
2006-01-17 07:03:38 +01:00
# include <linux/mm.h>
# include <linux/random.h>
2008-01-30 13:31:07 +01:00
# include <linux/limits.h>
2017-02-08 18:51:30 +01:00
# include <linux/sched/signal.h>
2017-02-08 18:51:31 +01:00
# include <linux/sched/mm.h>
2017-03-14 14:41:26 +03:00
# include <linux/compat.h>
2009-09-08 11:01:55 +02:00
# include <asm/elf.h>
2014-09-05 16:16:45 -07:00
struct va_alignment __read_mostly va_align = {
2011-08-06 14:31:38 +02:00
. flags = - 1 ,
} ;
2017-03-06 17:17:19 +03:00
unsigned long tasksize_32bit ( void )
2017-03-06 17:17:18 +03:00
{
return IA32_PAGE_OFFSET ;
}
2017-03-06 17:17:19 +03:00
unsigned long tasksize_64bit ( void )
{
return TASK_SIZE_MAX ;
}
2017-03-06 17:17:18 +03:00
static unsigned long stack_maxrandom_size ( unsigned long task_size )
2009-09-08 11:01:55 +02:00
{
2015-02-14 09:33:50 -08:00
unsigned long max = 0 ;
2009-09-08 11:01:55 +02:00
if ( ( current - > flags & PF_RANDOMIZE ) & &
! ( current - > personality & ADDR_NO_RANDOMIZE ) ) {
2017-03-06 17:17:18 +03:00
max = ( - 1UL ) & __STACK_RND_MASK ( task_size = = tasksize_32bit ( ) ) ;
max < < = PAGE_SHIFT ;
2009-09-08 11:01:55 +02:00
}
return max ;
}
2017-03-06 17:17:17 +03:00
# ifdef CONFIG_COMPAT
# define mmap32_rnd_bits mmap_rnd_compat_bits
# define mmap64_rnd_bits mmap_rnd_bits
# else
# define mmap32_rnd_bits mmap_rnd_bits
# define mmap64_rnd_bits mmap_rnd_bits
# endif
2017-03-06 17:17:18 +03:00
# define SIZE_128M (128 * 1024 * 1024UL)
2008-01-30 13:31:07 +01:00
static int mmap_is_legacy ( void )
2008-01-30 13:31:07 +01:00
{
if ( current - > personality & ADDR_COMPAT_LAYOUT )
return 1 ;
return sysctl_legacy_va_layout ;
}
2017-03-06 17:17:17 +03:00
static unsigned long arch_rnd ( unsigned int rndbits )
2008-01-30 13:31:10 +01:00
{
2017-03-06 17:17:17 +03:00
return ( get_random_long ( ) & ( ( 1UL < < rndbits ) - 1 ) ) < < PAGE_SHIFT ;
}
2015-04-14 15:47:45 -07:00
2017-03-06 17:17:17 +03:00
unsigned long arch_mmap_rnd ( void )
{
2017-03-06 17:17:19 +03:00
if ( ! ( current - > flags & PF_RANDOMIZE ) )
return 0 ;
2017-03-06 17:17:17 +03:00
return arch_rnd ( mmap_is_ia32 ( ) ? mmap32_rnd_bits : mmap64_rnd_bits ) ;
2008-01-30 13:31:10 +01:00
}
2017-03-06 17:17:18 +03:00
static unsigned long mmap_base ( unsigned long rnd , unsigned long task_size )
2008-01-30 13:31:10 +01:00
{
2010-01-27 17:32:22 +01:00
unsigned long gap = rlimit ( RLIMIT_STACK ) ;
2017-07-12 14:36:33 -07:00
unsigned long pad = stack_maxrandom_size ( task_size ) + stack_guard_gap ;
2017-03-06 17:17:18 +03:00
unsigned long gap_min , gap_max ;
2017-07-12 14:36:33 -07:00
/* Values close to RLIM_INFINITY can overflow. */
if ( gap + pad > gap )
gap + = pad ;
2017-03-06 17:17:18 +03:00
/*
* Top of mmap area ( just below the process stack ) .
* Leave an at least ~ 128 MB hole with possible stack randomization .
*/
2017-07-12 14:36:33 -07:00
gap_min = SIZE_128M ;
2017-03-06 17:17:18 +03:00
gap_max = ( task_size / 6 ) * 5 ;
2008-01-30 13:31:10 +01:00
2017-03-06 17:17:18 +03:00
if ( gap < gap_min )
gap = gap_min ;
else if ( gap > gap_max )
gap = gap_max ;
2008-01-30 13:31:10 +01:00
2017-03-06 17:17:18 +03:00
return PAGE_ALIGN ( task_size - gap - rnd ) ;
}
static unsigned long mmap_legacy_base ( unsigned long rnd ,
unsigned long task_size )
{
return __TASK_UNMAPPED_BASE ( task_size ) + rnd ;
2008-01-30 13:31:10 +01:00
}
2008-01-30 13:31:07 +01:00
/*
* This function , called very early during the creation of a new
* process VM image , sets up which VM layout function to use :
*/
2017-03-06 17:17:19 +03:00
static void arch_pick_mmap_base ( unsigned long * base , unsigned long * legacy_base ,
unsigned long random_factor , unsigned long task_size )
2008-01-30 13:31:07 +01:00
{
2017-03-06 17:17:19 +03:00
* legacy_base = mmap_legacy_base ( random_factor , task_size ) ;
if ( mmap_is_legacy ( ) )
* base = * legacy_base ;
else
* base = mmap_base ( random_factor , task_size ) ;
}
2013-08-21 20:55:59 +03:00
2017-03-06 17:17:19 +03:00
void arch_pick_mmap_layout ( struct mm_struct * mm )
{
if ( mmap_is_legacy ( ) )
2008-01-30 13:31:07 +01:00
mm - > get_unmapped_area = arch_get_unmapped_area ;
2017-03-06 17:17:19 +03:00
else
2008-01-30 13:31:07 +01:00
mm - > get_unmapped_area = arch_get_unmapped_area_topdown ;
2017-03-06 17:17:19 +03:00
arch_pick_mmap_base ( & mm - > mmap_base , & mm - > mmap_legacy_base ,
arch_rnd ( mmap64_rnd_bits ) , tasksize_64bit ( ) ) ;
# ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
/*
* The mmap syscall mapping base decision depends solely on the
* syscall type ( 64 - bit or compat ) . This applies for 64 bit
* applications and 32 bit applications . The 64 bit syscall uses
* mmap_base , the compat syscall uses mmap_compat_base .
*/
arch_pick_mmap_base ( & mm - > mmap_compat_base , & mm - > mmap_compat_legacy_base ,
arch_rnd ( mmap32_rnd_bits ) , tasksize_32bit ( ) ) ;
# endif
2006-01-17 07:03:38 +01:00
}
2015-07-20 14:29:58 -07:00
2017-03-14 14:41:26 +03:00
unsigned long get_mmap_base ( int is_legacy )
{
struct mm_struct * mm = current - > mm ;
# ifdef CONFIG_HAVE_ARCH_COMPAT_MMAP_BASES
if ( in_compat_syscall ( ) ) {
return is_legacy ? mm - > mmap_compat_legacy_base
: mm - > mmap_compat_base ;
}
# endif
return is_legacy ? mm - > mmap_legacy_base : mm - > mmap_base ;
}
2015-07-20 14:29:58 -07:00
const char * arch_vma_name ( struct vm_area_struct * vma )
{
if ( vma - > vm_flags & VM_MPX )
return " [mpx] " ;
return NULL ;
}