linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
From: mark.rutland@arm.com (Mark Rutland)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH v3 08/21] arm64: add support for module PLTs
Date: Fri, 22 Jan 2016 16:55:20 +0000	[thread overview]
Message-ID: <20160122165517.GD11645@leverpostej> (raw)
In-Reply-To: <1452518355-4606-9-git-send-email-ard.biesheuvel@linaro.org>

Hi Ard,

This looks good.

My comments below are mostly nits, and much of the rest probably betrays
my lack of familiarity with ELF.

On Mon, Jan 11, 2016 at 02:19:01PM +0100, Ard Biesheuvel wrote:
> This adds support for emitting PLTs at module load time for relative
> branches that are out of range. This is a prerequisite for KASLR, which
> may place the kernel and the modules anywhere in the vmalloc area,
> making it more likely that branch target offsets exceed the maximum
> range of +/- 128 MB.
> 
> Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org>
> ---
>  arch/arm64/Kconfig              |   9 ++
>  arch/arm64/Makefile             |   6 +-
>  arch/arm64/include/asm/module.h |  11 ++
>  arch/arm64/kernel/Makefile      |   1 +
>  arch/arm64/kernel/module-plts.c | 137 ++++++++++++++++++++
>  arch/arm64/kernel/module.c      |  12 ++
>  arch/arm64/kernel/module.lds    |   4 +
>  7 files changed, 179 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index ffa3c549a4ba..778df20bf623 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -363,6 +363,7 @@ config ARM64_ERRATUM_843419
>  	bool "Cortex-A53: 843419: A load or store might access an incorrect address"
>  	depends on MODULES
>  	default y
> +	select ARM64_MODULE_CMODEL_LARGE
>  	help
>  	  This option builds kernel modules using the large memory model in
>  	  order to avoid the use of the ADRP instruction, which can cause
> @@ -702,6 +703,14 @@ config ARM64_LSE_ATOMICS
>  
>  endmenu
>  
> +config ARM64_MODULE_CMODEL_LARGE
> +	bool
> +
> +config ARM64_MODULE_PLTS
> +	bool
> +	select ARM64_MODULE_CMODEL_LARGE
> +	select HAVE_MOD_ARCH_SPECIFIC
> +
>  endmenu
>  
>  menu "Boot options"
> diff --git a/arch/arm64/Makefile b/arch/arm64/Makefile
> index cd822d8454c0..db462980c6be 100644
> --- a/arch/arm64/Makefile
> +++ b/arch/arm64/Makefile
> @@ -41,10 +41,14 @@ endif
>  
>  CHECKFLAGS	+= -D__aarch64__
>  
> -ifeq ($(CONFIG_ARM64_ERRATUM_843419), y)
> +ifeq ($(CONFIG_ARM64_MODULE_CMODEL_LARGE), y)
>  KBUILD_CFLAGS_MODULE	+= -mcmodel=large
>  endif
>  
> +ifeq ($(CONFIG_ARM64_MODULE_PLTS),y)
> +KBUILD_LDFLAGS_MODULE	+= -T $(srctree)/arch/arm64/kernel/module.lds
> +endif
> +
>  # Default value
>  head-y		:= arch/arm64/kernel/head.o
>  
> diff --git a/arch/arm64/include/asm/module.h b/arch/arm64/include/asm/module.h
> index e80e232b730e..7b8cd3dc9d8e 100644
> --- a/arch/arm64/include/asm/module.h
> +++ b/arch/arm64/include/asm/module.h
> @@ -20,4 +20,15 @@
>  
>  #define MODULE_ARCH_VERMAGIC	"aarch64"
>  
> +#ifdef CONFIG_ARM64_MODULE_PLTS
> +struct mod_arch_specific {
> +	struct elf64_shdr	*core_plt;
> +	struct elf64_shdr	*init_plt;
> +	int			core_plt_count;
> +	int			init_plt_count;
> +};
> +#endif
> +
> +u64 get_module_plt(struct module *mod, void *loc, u64 val);
> +
>  #endif /* __ASM_MODULE_H */
> diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile
> index 474691f8b13a..f42b0fff607f 100644
> --- a/arch/arm64/kernel/Makefile
> +++ b/arch/arm64/kernel/Makefile
> @@ -30,6 +30,7 @@ arm64-obj-$(CONFIG_COMPAT)		+= sys32.o kuser32.o signal32.o 	\
>  					   ../../arm/kernel/opcodes.o
>  arm64-obj-$(CONFIG_FUNCTION_TRACER)	+= ftrace.o entry-ftrace.o
>  arm64-obj-$(CONFIG_MODULES)		+= arm64ksyms.o module.o
> +arm64-obj-$(CONFIG_ARM64_MODULE_PLTS)	+= module-plts.o
>  arm64-obj-$(CONFIG_PERF_EVENTS)		+= perf_regs.o perf_callchain.o
>  arm64-obj-$(CONFIG_HW_PERF_EVENTS)	+= perf_event.o
>  arm64-obj-$(CONFIG_HAVE_HW_BREAKPOINT)	+= hw_breakpoint.o
> diff --git a/arch/arm64/kernel/module-plts.c b/arch/arm64/kernel/module-plts.c
> new file mode 100644
> index 000000000000..4a8ef9ea01ee
> --- /dev/null
> +++ b/arch/arm64/kernel/module-plts.c
> @@ -0,0 +1,137 @@
> +/*
> + * Copyright (C) 2014-2015 Linaro Ltd. <ard.biesheuvel@linaro.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/elf.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +
> +struct plt_entry {
> +	__le32	mov0;	/* movn	x16, #0x....			*/
> +	__le32	mov1;	/* movk	x16, #0x...., lsl #16		*/
> +	__le32	mov2;	/* movk	x16, #0x...., lsl #32		*/
> +	__le32	br;	/* br	x16				*/
> +} __aligned(8);

We only need natural alignment for the instructions, so what's the
alignment for? I can't see that anything else cares.

It might be worth a comment regarding why why use x16 (i.e. because the
AAPCS says that as IP0 it is valid for veneers/PLTs to clobber).

> +static bool in_init(const struct module *mod, void *addr)
> +{
> +	return (u64)addr - (u64)mod->module_init < mod->init_size;
> +}
> +
> +u64 get_module_plt(struct module *mod, void *loc, u64 val)
> +{
> +	struct plt_entry entry = {
> +		cpu_to_le32(0x92800010 | (((~val      ) & 0xffff)) << 5),
> +		cpu_to_le32(0xf2a00010 | ((( val >> 16) & 0xffff)) << 5),
> +		cpu_to_le32(0xf2c00010 | ((( val >> 32) & 0xffff)) << 5),
> +		cpu_to_le32(0xd61f0200)
> +	}, *plt;

It would be nice if we could un-magic this, though I see that reusing
the existing insn or reloc_insn code is painful here.

> +	int i, *count;
> +
> +	if (in_init(mod, loc)) {
> +		plt = (struct plt_entry *)mod->arch.init_plt->sh_addr;
> +		count = &mod->arch.init_plt_count;
> +	} else {
> +		plt = (struct plt_entry *)mod->arch.core_plt->sh_addr;
> +		count = &mod->arch.core_plt_count;
> +	}
> +
> +	/* Look for an existing entry pointing to 'val' */
> +	for (i = 0; i < *count; i++)
> +		if (plt[i].mov0 == entry.mov0 &&
> +		    plt[i].mov1 == entry.mov1 &&
> +		    plt[i].mov2 == entry.mov2)
> +			return (u64)&plt[i];

I think that at the cost of redundantly comparing the br x16, you could
simplify this by comparing the whole struct, e.g.

	for (i = 0; i < *count; i++)
		if (plt[i] == entry)
			return (u64)&plt[i];

Which would also work if we change the veneer for some reason.

> +
> +	i = (*count)++;

given i == *count at the end of the loop, you could just increment
*count here.

> +	plt[i] = entry;
> +	return (u64)&plt[i];
> +}
> +
> +static int duplicate_rel(Elf64_Addr base, const Elf64_Rela *rela, int num)

Perhaps: static bool is_duplicate_rel

> +{
> +	int i;
> +
> +	for (i = 0; i < num; i++) {
> +		if (rela[i].r_info == rela[num].r_info &&
> +		    rela[i].r_addend == rela[num].r_addend)
> +			return 1;
> +	}
> +	return 0;
> +}
> +
> +/* Count how many PLT entries we may need */
> +static unsigned int count_plts(Elf64_Addr base, const Elf64_Rela *rela, int num)
> +{
> +	unsigned int ret = 0;
> +	int i;
> +
> +	/*
> +	 * Sure, this is order(n^2), but it's usually short, and not
> +	 * time critical
> +	 */
> +	for (i = 0; i < num; i++)
> +		switch (ELF64_R_TYPE(rela[i].r_info)) {
> +		case R_AARCH64_JUMP26:
> +		case R_AARCH64_CALL26:
> +			if (!duplicate_rel(base, rela, i))
> +				ret++;
> +			break;
> +		}

While braces aren't strictly required on the for loop, i think it would
look better with them given the contained logic is non-trivial.

> +	return ret;
> +}
> +
> +int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs,
> +			      char *secstrings, struct module *mod)
> +{
> +	unsigned long core_plts = 0, init_plts = 0;
> +	Elf64_Shdr *s, *sechdrs_end = sechdrs + ehdr->e_shnum;
> +
> +	/*
> +	 * To store the PLTs, we expand the .text section for core module code
> +	 * and the .init.text section for initialization code.
> +	 */

That comment is a bit misleading, given we don't touch .text and
.init.text, but rather .core.plt and .init.plt, relying on
layout_sections to group those with .text and .init.text.

> +	for (s = sechdrs; s < sechdrs_end; ++s)
> +		if (strcmp(".core.plt", secstrings + s->sh_name) == 0)
> +			mod->arch.core_plt = s;
> +		else if (strcmp(".init.plt", secstrings + s->sh_name) == 0)
> +			mod->arch.init_plt = s;

This would be nicer with braces.

> +
> +	if (!mod->arch.core_plt || !mod->arch.init_plt) {
> +		pr_err("%s: sections missing\n", mod->name);
> +		return -ENOEXEC;
> +	}
> +
> +	for (s = sechdrs + 1; s < sechdrs_end; ++s) {

Could we have a comment as to why we skip the first Shdr? I recall it's
in some way special, but I can't recall why/how.

> +		const Elf64_Rela *rels = (void *)ehdr + s->sh_offset;
> +		int numrels = s->sh_size / sizeof(Elf64_Rela);
> +		Elf64_Shdr *dstsec = sechdrs + s->sh_info;
> +
> +		if (s->sh_type != SHT_RELA)
> +			continue;

We only have RELA, and no REL?

Thanks,
Mark.

> +
> +		if (strstr(secstrings + s->sh_name, ".init"))
> +			init_plts += count_plts(dstsec->sh_addr, rels, numrels);
> +		else
> +			core_plts += count_plts(dstsec->sh_addr, rels, numrels);
> +	}
> +
> +	mod->arch.core_plt->sh_type = SHT_NOBITS;
> +	mod->arch.core_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
> +	mod->arch.core_plt->sh_addralign = L1_CACHE_BYTES;
> +	mod->arch.core_plt->sh_size = core_plts * sizeof(struct plt_entry);
> +	mod->arch.core_plt_count = 0;
> +
> +	mod->arch.init_plt->sh_type = SHT_NOBITS;
> +	mod->arch.init_plt->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
> +	mod->arch.init_plt->sh_addralign = L1_CACHE_BYTES;
> +	mod->arch.init_plt->sh_size = init_plts * sizeof(struct plt_entry);
> +	mod->arch.init_plt_count = 0;
> +	pr_debug("%s: core.plt=%lld, init.plt=%lld\n", __func__,
> +		 mod->arch.core_plt->sh_size, mod->arch.init_plt->sh_size);
> +	return 0;
> +}
> diff --git a/arch/arm64/kernel/module.c b/arch/arm64/kernel/module.c
> index 93e970231ca9..3a298b0e21bb 100644
> --- a/arch/arm64/kernel/module.c
> +++ b/arch/arm64/kernel/module.c
> @@ -38,6 +38,11 @@ void *module_alloc(unsigned long size)
>  				GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
>  				NUMA_NO_NODE, __builtin_return_address(0));
>  
> +	if (!p && IS_ENABLED(CONFIG_ARM64_MODULE_PLTS))
> +		p = __vmalloc_node_range(size, MODULE_ALIGN, VMALLOC_START,
> +				VMALLOC_END, GFP_KERNEL, PAGE_KERNEL_EXEC, 0,
> +				NUMA_NO_NODE, __builtin_return_address(0));
> +
>  	if (p && (kasan_module_alloc(p, size) < 0)) {
>  		vfree(p);
>  		return NULL;
> @@ -361,6 +366,13 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
>  		case R_AARCH64_CALL26:
>  			ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2, 26,
>  					     AARCH64_INSN_IMM_26);
> +
> +			if (IS_ENABLED(CONFIG_ARM64_MODULE_PLTS) &&
> +			    ovf == -ERANGE) {
> +				val = get_module_plt(me, loc, val);
> +				ovf = reloc_insn_imm(RELOC_OP_PREL, loc, val, 2,
> +						     26, AARCH64_INSN_IMM_26);
> +			}
>  			break;
>  
>  		default:
> diff --git a/arch/arm64/kernel/module.lds b/arch/arm64/kernel/module.lds
> new file mode 100644
> index 000000000000..3682fa107918
> --- /dev/null
> +++ b/arch/arm64/kernel/module.lds
> @@ -0,0 +1,4 @@
> +SECTIONS {
> +        .core.plt : { BYTE(0) }
> +        .init.plt : { BYTE(0) }
> +}
> -- 
> 2.5.0
> 

  reply	other threads:[~2016-01-22 16:55 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-01-11 13:18 [PATCH v3 00/21] arm64: implement support for KASLR Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 01/21] of/fdt: make memblock minimum physical address arch configurable Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 02/21] arm64: introduce KIMAGE_VADDR as the virtual base of the kernel region Ard Biesheuvel
2016-01-11 16:31   ` Mark Rutland
2016-01-11 13:18 ` [PATCH v3 03/21] arm64: pgtable: add dummy pud_index() and pmd_index() definitions Ard Biesheuvel
2016-01-11 17:40   ` Mark Rutland
2016-01-12 17:25     ` Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 04/21] arm64: decouple early fixmap init from linear mapping Ard Biesheuvel
2016-01-11 16:09   ` Mark Rutland
2016-01-11 16:15     ` Ard Biesheuvel
2016-01-11 16:27       ` Mark Rutland
2016-01-11 16:51         ` Mark Rutland
2016-01-11 17:08           ` Ard Biesheuvel
2016-01-11 17:15             ` Ard Biesheuvel
2016-01-11 17:21               ` Mark Rutland
2016-01-11 13:18 ` [PATCH v3 05/21] arm64: kvm: deal with kernel symbols outside of " Ard Biesheuvel
2016-01-12 12:36   ` Mark Rutland
2016-01-12 13:23     ` Ard Biesheuvel
2016-01-11 13:18 ` [PATCH v3 06/21] arm64: pgtable: implement static [pte|pmd|pud]_offset variants Ard Biesheuvel
2016-01-11 16:24   ` Mark Rutland
2016-01-11 17:28     ` Ard Biesheuvel
2016-01-11 17:31       ` Mark Rutland
2016-01-11 13:19 ` [PATCH v3 07/21] arm64: move kernel image to base of vmalloc area Ard Biesheuvel
2016-01-12 18:14   ` Mark Rutland
2016-01-13  8:39     ` Ard Biesheuvel
2016-01-13  9:58       ` Ard Biesheuvel
2016-01-13 11:11         ` Mark Rutland
2016-01-13 11:14           ` Ard Biesheuvel
2016-01-13 13:51       ` Mark Rutland
2016-01-13 15:50         ` Ard Biesheuvel
2016-01-13 16:26           ` Mark Rutland
2016-01-14 18:57         ` Mark Rutland
2016-01-15  9:54           ` Ard Biesheuvel
2016-01-15 11:23             ` Mark Rutland
2016-01-27 14:31               ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 08/21] arm64: add support for module PLTs Ard Biesheuvel
2016-01-22 16:55   ` Mark Rutland [this message]
2016-01-22 17:06     ` Ard Biesheuvel
2016-01-22 17:19       ` Mark Rutland
2016-01-11 13:19 ` [PATCH v3 09/21] extable: add support for relative extables to search and sort routines Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 10/21] arm64: switch to relative exception tables Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 11/21] arm64: avoid R_AARCH64_ABS64 relocations for Image header fields Ard Biesheuvel
2016-01-13 18:12   ` Mark Rutland
2016-01-13 18:48     ` Ard Biesheuvel
2016-01-14  8:51       ` Ard Biesheuvel
2016-01-14  9:05         ` Ard Biesheuvel
2016-01-14 10:46           ` Mark Rutland
2016-01-14 11:22             ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 12/21] arm64: avoid dynamic relocations in early boot code Ard Biesheuvel
2016-01-14 17:09   ` Mark Rutland
2016-01-11 13:19 ` [PATCH v3 13/21] arm64: allow kernel Image to be loaded anywhere in physical memory Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 14/21] arm64: redefine SWAPPER_TABLE_SHIFT for use in asm code Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 14/21] arm64: [re]define SWAPPER_TABLE_[SHIFT|SIZE] " Ard Biesheuvel
2016-01-11 13:26   ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 15/21] arm64: split elf relocs into a separate header Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 16/21] scripts/sortextable: add support for ET_DYN binaries Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 17/21] arm64: add support for a relocatable kernel and KASLR Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 18/21] efi: stub: implement efi_get_random_bytes() based on EFI_RNG_PROTOCOL Ard Biesheuvel
2016-01-21 15:42   ` Matt Fleming
2016-01-21 16:12     ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 19/21] efi: stub: add implementation of efi_random_alloc() Ard Biesheuvel
2016-01-21 16:10   ` Matt Fleming
2016-01-21 16:16     ` Ard Biesheuvel
2016-01-11 13:19 ` [PATCH v3 20/21] efi: stub: use high allocation for converted command line Ard Biesheuvel
2016-01-21 16:20   ` Matt Fleming
2016-01-11 13:19 ` [PATCH v3 21/21] arm64: efi: invoke EFI_RNG_PROTOCOL to supply KASLR randomness Ard Biesheuvel
2016-01-21 16:31   ` Matt Fleming
2016-01-11 22:07 ` [PATCH v3 00/21] arm64: implement support for KASLR Kees Cook
2016-01-12  7:17   ` Ard Biesheuvel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160122165517.GD11645@leverpostej \
    --to=mark.rutland@arm.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).