Re: [PATCH v3 04/13] mm/execmem, arch: convert remaining overrides of module_alloc to execmem
From: Will Deacon
Date: Mon Oct 23 2023 - 13:14:34 EST
Hi Mike,
On Mon, Sep 18, 2023 at 10:29:46AM +0300, Mike Rapoport wrote:
> From: "Mike Rapoport (IBM)" <rppt@xxxxxxxxxx>
>
> 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 and support for allocation of KASAN shadow required by
> arm64, s390 and x86.
>
> 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: Mike Rapoport (IBM) <rppt@xxxxxxxxxx>
> ---
> arch/arm/kernel/module.c | 38 ++++++++++++---------
> arch/arm64/kernel/module.c | 57 ++++++++++++++------------------
> arch/powerpc/kernel/module.c | 52 ++++++++++++++---------------
> arch/s390/kernel/module.c | 52 +++++++++++------------------
> arch/x86/kernel/module.c | 64 +++++++++++-------------------------
> include/linux/execmem.h | 14 ++++++++
> mm/execmem.c | 43 ++++++++++++++++++++++--
> 7 files changed, 167 insertions(+), 153 deletions(-)
[...]
> diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
> index dd851297596e..cd6320de1c54 100644
> --- a/arch/arm64/kernel/module.c
> +++ b/arch/arm64/kernel/module.c
> @@ -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,46 +109,38 @@ static int __init module_init_limits(void)
>
> return 0;
> }
> -subsys_initcall(module_init_limits);
>
> -void *module_alloc(unsigned long size)
> +static struct execmem_params execmem_params __ro_after_init = {
> + .ranges = {
> + [EXECMEM_DEFAULT] = {
> + .flags = EXECMEM_KASAN_SHADOW,
> + .alignment = MODULE_ALIGN,
> + },
> + },
> +};
> +
> +struct execmem_params __init *execmem_arch_params(void)
> {
> - void *p = NULL;
> + struct execmem_range *r = &execmem_params.ranges[EXECMEM_DEFAULT];
>
> - /*
> - * Where possible, prefer to allocate within direct branch range of the
> - * kernel such that no PLTs are necessary.
> - */
Why are you removing this comment? I think you could just move it next
to the part where we set a 128MiB range.
> - 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));
> - }
> + module_init_limits();
Hmm, this used to be run from subsys_initcall(), but now you're running
it _really_ early, before random_init(), so randomization of the module
space is no longer going to be very random if we don't have early entropy
from the firmware or the CPU, which is likely to be the case on most SoCs.
>
> - 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));
> - }
> + r->pgprot = PAGE_KERNEL;
>
> - if (!p) {
> - pr_warn_ratelimited("%s: unable to allocate memory\n",
> - __func__);
> - }
> + if (module_direct_base) {
> + r->start = module_direct_base;
> + r->end = module_direct_base + SZ_128M;
>
> - if (p && (kasan_alloc_module_shadow(p, size, GFP_KERNEL) < 0)) {
> - vfree(p);
> - return NULL;
> + if (module_plt_base) {
> + r->fallback_start = module_plt_base;
> + r->fallback_end = module_plt_base + SZ_2G;
> + }
> + } else if (module_plt_base) {
> + r->start = module_plt_base;
> + r->end = module_plt_base + SZ_2G;
> }
>
> - /* Memory is intended to be executable, reset the pointer tag. */
> - return kasan_reset_tag(p);
> + return &execmem_params;
> }
>
> enum aarch64_reloc_op {
[...]
> diff --git a/include/linux/execmem.h b/include/linux/execmem.h
> index 44e213625053..806ad1a0088d 100644
> --- a/include/linux/execmem.h
> +++ b/include/linux/execmem.h
> @@ -32,19 +32,33 @@ enum execmem_type {
> EXECMEM_TYPE_MAX,
> };
>
> +/**
> + * enum execmem_module_flags - options for executable memory allocations
> + * @EXECMEM_KASAN_SHADOW: allocate kasan shadow
> + */
> +enum execmem_range_flags {
> + EXECMEM_KASAN_SHADOW = (1 << 0),
> +};
> +
> /**
> * struct execmem_range - definition of a memory range suitable for code and
> * related data allocations
> * @start: address space start
> * @end: address space end (inclusive)
> + * @fallback_start: start of the range for fallback allocations
> + * @fallback_end: end of the range for fallback allocations (inclusive)
> * @pgprot: permissions for memory in this address space
> * @alignment: alignment required for text allocations
> + * @flags: options for memory allocations for this range
> */
> struct execmem_range {
> unsigned long start;
> unsigned long end;
> + unsigned long fallback_start;
> + unsigned long fallback_end;
> pgprot_t pgprot;
> unsigned int alignment;
> + enum execmem_range_flags flags;
> };
>
> /**
> diff --git a/mm/execmem.c b/mm/execmem.c
> index f25a5e064886..a8c2f44d0133 100644
> --- a/mm/execmem.c
> +++ b/mm/execmem.c
> @@ -11,12 +11,46 @@ static void *execmem_alloc(size_t size, struct execmem_range *range)
> {
> unsigned long start = range->start;
> unsigned long end = range->end;
> + unsigned long fallback_start = range->fallback_start;
> + unsigned long fallback_end = range->fallback_end;
> unsigned int align = range->alignment;
> pgprot_t pgprot = range->pgprot;
> + bool kasan = range->flags & EXECMEM_KASAN_SHADOW;
> + unsigned long vm_flags = VM_FLUSH_RESET_PERMS;
> + bool fallback = !!fallback_start;
> + gfp_t gfp_flags = GFP_KERNEL;
> + void *p;
>
> - return __vmalloc_node_range(size, align, start, end,
> - GFP_KERNEL, pgprot, VM_FLUSH_RESET_PERMS,
> - NUMA_NO_NODE, __builtin_return_address(0));
> + if (PAGE_ALIGN(size) > (end - start))
> + return NULL;
> +
> + if (kasan)
> + vm_flags |= VM_DEFER_KMEMLEAK;
Hmm, I don't think we passed this before on arm64, should we have done?
Will