162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  flexible mmap layout support
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2003-2004 Red Hat Inc., Durham, North Carolina.
662306a36Sopenharmony_ci * All Rights Reserved.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Started by Ingo Molnar <mingo@elte.hu>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/elf-randomize.h>
1262306a36Sopenharmony_ci#include <linux/personality.h>
1362306a36Sopenharmony_ci#include <linux/mm.h>
1462306a36Sopenharmony_ci#include <linux/mman.h>
1562306a36Sopenharmony_ci#include <linux/sched/signal.h>
1662306a36Sopenharmony_ci#include <linux/sched/mm.h>
1762306a36Sopenharmony_ci#include <linux/random.h>
1862306a36Sopenharmony_ci#include <linux/compat.h>
1962306a36Sopenharmony_ci#include <linux/security.h>
2062306a36Sopenharmony_ci#include <asm/elf.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic unsigned long stack_maxrandom_size(void)
2362306a36Sopenharmony_ci{
2462306a36Sopenharmony_ci	if (!(current->flags & PF_RANDOMIZE))
2562306a36Sopenharmony_ci		return 0;
2662306a36Sopenharmony_ci	return STACK_RND_MASK << PAGE_SHIFT;
2762306a36Sopenharmony_ci}
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_cistatic inline int mmap_is_legacy(struct rlimit *rlim_stack)
3062306a36Sopenharmony_ci{
3162306a36Sopenharmony_ci	if (current->personality & ADDR_COMPAT_LAYOUT)
3262306a36Sopenharmony_ci		return 1;
3362306a36Sopenharmony_ci	if (rlim_stack->rlim_cur == RLIM_INFINITY)
3462306a36Sopenharmony_ci		return 1;
3562306a36Sopenharmony_ci	return sysctl_legacy_va_layout;
3662306a36Sopenharmony_ci}
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ciunsigned long arch_mmap_rnd(void)
3962306a36Sopenharmony_ci{
4062306a36Sopenharmony_ci	return (get_random_u32() & MMAP_RND_MASK) << PAGE_SHIFT;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic unsigned long mmap_base_legacy(unsigned long rnd)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	return TASK_UNMAPPED_BASE + rnd;
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic inline unsigned long mmap_base(unsigned long rnd,
4962306a36Sopenharmony_ci				      struct rlimit *rlim_stack)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	unsigned long gap = rlim_stack->rlim_cur;
5262306a36Sopenharmony_ci	unsigned long pad = stack_maxrandom_size() + stack_guard_gap;
5362306a36Sopenharmony_ci	unsigned long gap_min, gap_max;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* Values close to RLIM_INFINITY can overflow. */
5662306a36Sopenharmony_ci	if (gap + pad > gap)
5762306a36Sopenharmony_ci		gap += pad;
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	/*
6062306a36Sopenharmony_ci	 * Top of mmap area (just below the process stack).
6162306a36Sopenharmony_ci	 * Leave at least a ~128 MB hole.
6262306a36Sopenharmony_ci	 */
6362306a36Sopenharmony_ci	gap_min = SZ_128M;
6462306a36Sopenharmony_ci	gap_max = (STACK_TOP / 6) * 5;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (gap < gap_min)
6762306a36Sopenharmony_ci		gap = gap_min;
6862306a36Sopenharmony_ci	else if (gap > gap_max)
6962306a36Sopenharmony_ci		gap = gap_max;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return PAGE_ALIGN(STACK_TOP - gap - rnd);
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ciunsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr,
7562306a36Sopenharmony_ci				     unsigned long len, unsigned long pgoff,
7662306a36Sopenharmony_ci				     unsigned long flags)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	struct mm_struct *mm = current->mm;
7962306a36Sopenharmony_ci	struct vm_area_struct *vma;
8062306a36Sopenharmony_ci	struct vm_unmapped_area_info info;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	if (len > TASK_SIZE - mmap_min_addr)
8362306a36Sopenharmony_ci		return -ENOMEM;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (flags & MAP_FIXED)
8662306a36Sopenharmony_ci		goto check_asce_limit;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (addr) {
8962306a36Sopenharmony_ci		addr = PAGE_ALIGN(addr);
9062306a36Sopenharmony_ci		vma = find_vma(mm, addr);
9162306a36Sopenharmony_ci		if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
9262306a36Sopenharmony_ci		    (!vma || addr + len <= vm_start_gap(vma)))
9362306a36Sopenharmony_ci			goto check_asce_limit;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	info.flags = 0;
9762306a36Sopenharmony_ci	info.length = len;
9862306a36Sopenharmony_ci	info.low_limit = mm->mmap_base;
9962306a36Sopenharmony_ci	info.high_limit = TASK_SIZE;
10062306a36Sopenharmony_ci	if (filp || (flags & MAP_SHARED))
10162306a36Sopenharmony_ci		info.align_mask = MMAP_ALIGN_MASK << PAGE_SHIFT;
10262306a36Sopenharmony_ci	else
10362306a36Sopenharmony_ci		info.align_mask = 0;
10462306a36Sopenharmony_ci	info.align_offset = pgoff << PAGE_SHIFT;
10562306a36Sopenharmony_ci	addr = vm_unmapped_area(&info);
10662306a36Sopenharmony_ci	if (offset_in_page(addr))
10762306a36Sopenharmony_ci		return addr;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cicheck_asce_limit:
11062306a36Sopenharmony_ci	return check_asce_limit(mm, addr, len);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciunsigned long arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
11462306a36Sopenharmony_ci					     unsigned long len, unsigned long pgoff,
11562306a36Sopenharmony_ci					     unsigned long flags)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	struct vm_area_struct *vma;
11862306a36Sopenharmony_ci	struct mm_struct *mm = current->mm;
11962306a36Sopenharmony_ci	struct vm_unmapped_area_info info;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/* requested length too big for entire address space */
12262306a36Sopenharmony_ci	if (len > TASK_SIZE - mmap_min_addr)
12362306a36Sopenharmony_ci		return -ENOMEM;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (flags & MAP_FIXED)
12662306a36Sopenharmony_ci		goto check_asce_limit;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	/* requesting a specific address */
12962306a36Sopenharmony_ci	if (addr) {
13062306a36Sopenharmony_ci		addr = PAGE_ALIGN(addr);
13162306a36Sopenharmony_ci		vma = find_vma(mm, addr);
13262306a36Sopenharmony_ci		if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
13362306a36Sopenharmony_ci				(!vma || addr + len <= vm_start_gap(vma)))
13462306a36Sopenharmony_ci			goto check_asce_limit;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	info.flags = VM_UNMAPPED_AREA_TOPDOWN;
13862306a36Sopenharmony_ci	info.length = len;
13962306a36Sopenharmony_ci	info.low_limit = PAGE_SIZE;
14062306a36Sopenharmony_ci	info.high_limit = mm->mmap_base;
14162306a36Sopenharmony_ci	if (filp || (flags & MAP_SHARED))
14262306a36Sopenharmony_ci		info.align_mask = MMAP_ALIGN_MASK << PAGE_SHIFT;
14362306a36Sopenharmony_ci	else
14462306a36Sopenharmony_ci		info.align_mask = 0;
14562306a36Sopenharmony_ci	info.align_offset = pgoff << PAGE_SHIFT;
14662306a36Sopenharmony_ci	addr = vm_unmapped_area(&info);
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	/*
14962306a36Sopenharmony_ci	 * A failed mmap() very likely causes application failure,
15062306a36Sopenharmony_ci	 * so fall back to the bottom-up function here. This scenario
15162306a36Sopenharmony_ci	 * can happen with large stack limits and large mmap()
15262306a36Sopenharmony_ci	 * allocations.
15362306a36Sopenharmony_ci	 */
15462306a36Sopenharmony_ci	if (offset_in_page(addr)) {
15562306a36Sopenharmony_ci		VM_BUG_ON(addr != -ENOMEM);
15662306a36Sopenharmony_ci		info.flags = 0;
15762306a36Sopenharmony_ci		info.low_limit = TASK_UNMAPPED_BASE;
15862306a36Sopenharmony_ci		info.high_limit = TASK_SIZE;
15962306a36Sopenharmony_ci		addr = vm_unmapped_area(&info);
16062306a36Sopenharmony_ci		if (offset_in_page(addr))
16162306a36Sopenharmony_ci			return addr;
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cicheck_asce_limit:
16562306a36Sopenharmony_ci	return check_asce_limit(mm, addr, len);
16662306a36Sopenharmony_ci}
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci/*
16962306a36Sopenharmony_ci * This function, called very early during the creation of a new
17062306a36Sopenharmony_ci * process VM image, sets up which VM layout function to use:
17162306a36Sopenharmony_ci */
17262306a36Sopenharmony_civoid arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	unsigned long random_factor = 0UL;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	if (current->flags & PF_RANDOMIZE)
17762306a36Sopenharmony_ci		random_factor = arch_mmap_rnd();
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	/*
18062306a36Sopenharmony_ci	 * Fall back to the standard layout if the personality
18162306a36Sopenharmony_ci	 * bit is set, or if the expected stack growth is unlimited:
18262306a36Sopenharmony_ci	 */
18362306a36Sopenharmony_ci	if (mmap_is_legacy(rlim_stack)) {
18462306a36Sopenharmony_ci		mm->mmap_base = mmap_base_legacy(random_factor);
18562306a36Sopenharmony_ci		mm->get_unmapped_area = arch_get_unmapped_area;
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		mm->mmap_base = mmap_base(random_factor, rlim_stack);
18862306a36Sopenharmony_ci		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
18962306a36Sopenharmony_ci	}
19062306a36Sopenharmony_ci}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_cistatic const pgprot_t protection_map[16] = {
19362306a36Sopenharmony_ci	[VM_NONE]					= PAGE_NONE,
19462306a36Sopenharmony_ci	[VM_READ]					= PAGE_RO,
19562306a36Sopenharmony_ci	[VM_WRITE]					= PAGE_RO,
19662306a36Sopenharmony_ci	[VM_WRITE | VM_READ]				= PAGE_RO,
19762306a36Sopenharmony_ci	[VM_EXEC]					= PAGE_RX,
19862306a36Sopenharmony_ci	[VM_EXEC | VM_READ]				= PAGE_RX,
19962306a36Sopenharmony_ci	[VM_EXEC | VM_WRITE]				= PAGE_RX,
20062306a36Sopenharmony_ci	[VM_EXEC | VM_WRITE | VM_READ]			= PAGE_RX,
20162306a36Sopenharmony_ci	[VM_SHARED]					= PAGE_NONE,
20262306a36Sopenharmony_ci	[VM_SHARED | VM_READ]				= PAGE_RO,
20362306a36Sopenharmony_ci	[VM_SHARED | VM_WRITE]				= PAGE_RW,
20462306a36Sopenharmony_ci	[VM_SHARED | VM_WRITE | VM_READ]		= PAGE_RW,
20562306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC]				= PAGE_RX,
20662306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC | VM_READ]			= PAGE_RX,
20762306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC | VM_WRITE]		= PAGE_RWX,
20862306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]	= PAGE_RWX
20962306a36Sopenharmony_ci};
21062306a36Sopenharmony_ciDECLARE_VM_GET_PAGE_PROT
211