Commit 223b5e57 authored by Mike Rapoport (IBM)'s avatar Mike Rapoport (IBM) Committed by Luis Chamberlain
Browse files

mm/execmem, arch: convert remaining overrides of module_alloc to execmem



Extend execmem parameters to accommodate more complex overrides of
module_alloc() by architectures.

This includes specification of a fallback range required by arm, arm64
and powerpc, EXECMEM_MODULE_DATA type required by powerpc, support for
allocation of KASAN shadow required by s390 and x86 and support for
late initialization of execmem required by arm64.

The core implementation of execmem_alloc() takes care of suppressing
warnings when the initial allocation fails but there is a fallback range
defined.

Signed-off-by: default avatarMike Rapoport (IBM) <rppt@kernel.org>
Acked-by: default avatarWill Deacon <will@kernel.org>
Acked-by: default avatarSong Liu <song@kernel.org>
Tested-by: default avatarLiviu Dudau <liviu@dudau.co.uk>
Signed-off-by: default avatarLuis Chamberlain <mcgrof@kernel.org>
parent f6bec26c
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -977,6 +977,14 @@ config ARCH_WANTS_MODULES_DATA_IN_VMALLOC
	  For architectures like powerpc/32 which have constraints on module
	  allocation and need to allocate module data outside of module area.

config ARCH_WANTS_EXECMEM_LATE
	bool
	help
	  For architectures that do not allocate executable memory early on
	  boot, but rather require its initialization late when there is
	  enough entropy for module space randomization, for instance
	  arm64.

config HAVE_IRQ_EXIT_ON_IRQ_STACK
	bool
	help
+25 −16
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/gfp.h>
#include <linux/execmem.h>

#include <asm/sections.h>
#include <asm/smp_plat.h>
@@ -34,23 +35,31 @@
#endif

#ifdef CONFIG_MMU
void *module_alloc(unsigned long size)
static struct execmem_info execmem_info __ro_after_init;

struct execmem_info __init *execmem_arch_setup(void)
{
	gfp_t gfp_mask = GFP_KERNEL;
	void *p;

	/* Silence the initial allocation */
	if (IS_ENABLED(CONFIG_ARM_MODULE_PLTS))
		gfp_mask |= __GFP_NOWARN;

	p = __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
				gfp_mask, PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
				__builtin_return_address(0));
	if (!IS_ENABLED(CONFIG_ARM_MODULE_PLTS) || p)
		return p;
	return __vmalloc_node_range(size, 1,  VMALLOC_START, VMALLOC_END,
				GFP_KERNEL, PAGE_KERNEL_EXEC, 0, NUMA_NO_NODE,
				__builtin_return_address(0));
	unsigned long fallback_start = 0, fallback_end = 0;

	if (IS_ENABLED(CONFIG_ARM_MODULE_PLTS)) {
		fallback_start = VMALLOC_START;
		fallback_end = VMALLOC_END;
	}

	execmem_info = (struct execmem_info){
		.ranges = {
			[EXECMEM_DEFAULT] = {
				.start	= MODULES_VADDR,
				.end	= MODULES_END,
				.pgprot	= PAGE_KERNEL_EXEC,
				.alignment = 1,
				.fallback_start	= fallback_start,
				.fallback_end	= fallback_end,
			},
		},
	};

	return &execmem_info;
}
#endif

+1 −0
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ config ARM64
	select ARCH_WANT_FRAME_POINTERS
	select ARCH_WANT_HUGE_PMD_SHARE if ARM64_4K_PAGES || (ARM64_16K_PAGES && !ARM64_VA_BITS_36)
	select ARCH_WANT_LD_ORPHAN_WARN
	select ARCH_WANTS_EXECMEM_LATE if EXECMEM
	select ARCH_WANTS_NO_INSTR
	select ARCH_WANTS_THP_SWAP if ARM64_4K_PAGES
	select ARCH_HAS_UBSAN
+31 −24
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/random.h>
#include <linux/scs.h>
#include <linux/vmalloc.h>
#include <linux/execmem.h>

#include <asm/alternative.h>
#include <asm/insn.h>
@@ -108,41 +109,47 @@ static int __init module_init_limits(void)

	return 0;
}
subsys_initcall(module_init_limits);

void *module_alloc(unsigned long size)
static struct execmem_info execmem_info __ro_after_init;

struct execmem_info __init *execmem_arch_setup(void)
{
	void *p = NULL;
	unsigned long fallback_start = 0, fallback_end = 0;
	unsigned long start = 0, end = 0;

	module_init_limits();

	/*
	 * Where possible, prefer to allocate within direct branch range of the
	 * kernel such that no PLTs are necessary.
	 */
	if (module_direct_base) {
		p = __vmalloc_node_range(size, MODULE_ALIGN,
					 module_direct_base,
					 module_direct_base + SZ_128M,
					 GFP_KERNEL | __GFP_NOWARN,
					 PAGE_KERNEL, 0, NUMA_NO_NODE,
					 __builtin_return_address(0));
	}
		start = module_direct_base;
		end = module_direct_base + SZ_128M;

	if (!p && module_plt_base) {
		p = __vmalloc_node_range(size, MODULE_ALIGN,
					 module_plt_base,
					 module_plt_base + SZ_2G,
					 GFP_KERNEL | __GFP_NOWARN,
					 PAGE_KERNEL, 0, NUMA_NO_NODE,
					 __builtin_return_address(0));
		if (module_plt_base) {
			fallback_start = module_plt_base;
			fallback_end = module_plt_base + SZ_2G;
		}

	if (!p) {
		pr_warn_ratelimited("%s: unable to allocate memory\n",
				    __func__);
	} else if (module_plt_base) {
		start = module_plt_base;
		end = module_plt_base + SZ_2G;
	}

	/* Memory is intended to be executable, reset the pointer tag. */
	return kasan_reset_tag(p);
	execmem_info = (struct execmem_info){
		.ranges = {
			[EXECMEM_DEFAULT] = {
				.start	= start,
				.end	= end,
				.pgprot	= PAGE_KERNEL,
				.alignment = 1,
				.fallback_start	= fallback_start,
				.fallback_end	= fallback_end,
			},
		},
	};

	return &execmem_info;
}

enum aarch64_reloc_op {
+39 −21
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/bug.h>
#include <linux/execmem.h>
#include <asm/module.h>
#include <linux/uaccess.h>
#include <asm/firmware.h>
@@ -89,39 +90,56 @@ int module_finalize(const Elf_Ehdr *hdr,
	return 0;
}

static __always_inline void *
__module_alloc(unsigned long size, unsigned long start, unsigned long end, bool nowarn)
static struct execmem_info execmem_info __ro_after_init;

struct execmem_info __init *execmem_arch_setup(void)
{
	pgprot_t prot = strict_module_rwx_enabled() ? PAGE_KERNEL : PAGE_KERNEL_EXEC;
	gfp_t gfp = GFP_KERNEL | (nowarn ? __GFP_NOWARN : 0);
	unsigned long fallback_start = 0, fallback_end = 0;
	unsigned long start, end;

	/*
	 * Don't do huge page allocations for modules yet until more testing
	 * is done. STRICT_MODULE_RWX may require extra work to support this
	 * too.
	 * BOOK3S_32 and 8xx define MODULES_VADDR for text allocations and
	 * allow allocating data in the entire vmalloc space
	 */
	return __vmalloc_node_range(size, 1, start, end, gfp, prot,
				    VM_FLUSH_RESET_PERMS,
				    NUMA_NO_NODE, __builtin_return_address(0));
}

void *module_alloc(unsigned long size)
{
#ifdef MODULES_VADDR
	unsigned long limit = (unsigned long)_etext - SZ_32M;
	void *ptr = NULL;

	BUILD_BUG_ON(TASK_SIZE > MODULES_VADDR);

	/* First try within 32M limit from _etext to avoid branch trampolines */
	if (MODULES_VADDR < PAGE_OFFSET && MODULES_END > limit)
		ptr = __module_alloc(size, limit, MODULES_END, true);

	if (!ptr)
		ptr = __module_alloc(size, MODULES_VADDR, MODULES_END, false);
	if (MODULES_VADDR < PAGE_OFFSET && MODULES_END > limit) {
		start = limit;
		fallback_start = MODULES_VADDR;
		fallback_end = MODULES_END;
	} else {
		start = MODULES_VADDR;
	}

	return ptr;
	end = MODULES_END;
#else
	return __module_alloc(size, VMALLOC_START, VMALLOC_END, false);
	start = VMALLOC_START;
	end = VMALLOC_END;
#endif

	execmem_info = (struct execmem_info){
		.ranges = {
			[EXECMEM_DEFAULT] = {
				.start	= start,
				.end	= end,
				.pgprot	= prot,
				.alignment = 1,
				.fallback_start	= fallback_start,
				.fallback_end	= fallback_end,
			},
			[EXECMEM_MODULE_DATA] = {
				.start	= VMALLOC_START,
				.end	= VMALLOC_END,
				.pgprot	= PAGE_KERNEL,
				.alignment = 1,
			},
		},
	};

	return &execmem_info;
}
Loading