mirror of
https://github.com/torvalds/linux.git
synced 2024-11-19 18:41:48 +00:00
a257e02579
Working around Cortex-A53 erratum #843419 involves special handling of ADRP instructions that end up in the last two instruction slots of a 4k page, or whose output register gets overwritten without having been read. (Note that the latter instruction sequence is never emitted by a properly functioning compiler, which is why it is disregarded by the handling of the same erratum in the bfd.ld linker which we rely on for the core kernel) Normally, this gets taken care of by the linker, which can spot such sequences at final link time, and insert a veneer if the ADRP ends up at a vulnerable offset. However, linux kernel modules are partially linked ELF objects, and so there is no 'final link time' other than the runtime loading of the module, at which time all the static relocations are resolved. For this reason, we have implemented the #843419 workaround for modules by avoiding ADRP instructions altogether, by using the large C model, and by passing -mpc-relative-literal-loads to recent versions of GCC that may emit adrp/ldr pairs to perform literal loads. However, this workaround forces us to keep literal data mixed with the instructions in the executable .text segment, and literal data may inadvertently turn into an exploitable speculative gadget depending on the relative offsets of arbitrary symbols. So let's reimplement this workaround in a way that allows us to switch back to the small C model, and to drop the -mpc-relative-literal-loads GCC switch, by patching affected ADRP instructions at runtime: - ADRP instructions that do not appear at 4k relative offset 0xff8 or 0xffc are ignored - ADRP instructions that are within 1 MB of their target symbol are converted into ADR instructions - remaining ADRP instructions are redirected via a veneer that performs the load using an unaffected movn/movk sequence. Signed-off-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> [will: tidied up ADRP -> ADR instruction patching.] [will: use ULL suffix for 64-bit immediate] Signed-off-by: Will Deacon <will.deacon@arm.com>
82 lines
2.4 KiB
C
82 lines
2.4 KiB
C
/*
|
|
* Copyright (C) 2017 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/module.h>
|
|
|
|
int sym64_rel;
|
|
|
|
#define SYM64_ABS_VAL 0xffff880000cccccc
|
|
#define SYM32_ABS_VAL 0xf800cccc
|
|
#define SYM16_ABS_VAL 0xf8cc
|
|
|
|
#define __SET_ABS(name, val) asm(".globl " #name "; .set "#name ", " #val)
|
|
#define SET_ABS(name, val) __SET_ABS(name, val)
|
|
|
|
SET_ABS(sym64_abs, SYM64_ABS_VAL);
|
|
SET_ABS(sym32_abs, SYM32_ABS_VAL);
|
|
SET_ABS(sym16_abs, SYM16_ABS_VAL);
|
|
|
|
asmlinkage u64 absolute_data64(void);
|
|
asmlinkage u64 absolute_data32(void);
|
|
asmlinkage u64 absolute_data16(void);
|
|
asmlinkage u64 signed_movw(void);
|
|
asmlinkage u64 unsigned_movw(void);
|
|
asmlinkage u64 relative_adrp(void);
|
|
asmlinkage u64 relative_adrp_far(void);
|
|
asmlinkage u64 relative_adr(void);
|
|
asmlinkage u64 relative_data64(void);
|
|
asmlinkage u64 relative_data32(void);
|
|
asmlinkage u64 relative_data16(void);
|
|
|
|
static struct {
|
|
char name[32];
|
|
u64 (*f)(void);
|
|
u64 expect;
|
|
} const funcs[] = {
|
|
{ "R_AARCH64_ABS64", absolute_data64, UL(SYM64_ABS_VAL) },
|
|
{ "R_AARCH64_ABS32", absolute_data32, UL(SYM32_ABS_VAL) },
|
|
{ "R_AARCH64_ABS16", absolute_data16, UL(SYM16_ABS_VAL) },
|
|
{ "R_AARCH64_MOVW_SABS_Gn", signed_movw, UL(SYM64_ABS_VAL) },
|
|
{ "R_AARCH64_MOVW_UABS_Gn", unsigned_movw, UL(SYM64_ABS_VAL) },
|
|
{ "R_AARCH64_ADR_PREL_PG_HI21", relative_adrp, (u64)&sym64_rel },
|
|
{ "R_AARCH64_ADR_PREL_PG_HI21", relative_adrp_far, (u64)&printk },
|
|
{ "R_AARCH64_ADR_PREL_LO21", relative_adr, (u64)&sym64_rel },
|
|
{ "R_AARCH64_PREL64", relative_data64, (u64)&sym64_rel },
|
|
{ "R_AARCH64_PREL32", relative_data32, (u64)&sym64_rel },
|
|
{ "R_AARCH64_PREL16", relative_data16, (u64)&sym64_rel },
|
|
};
|
|
|
|
static int reloc_test_init(void)
|
|
{
|
|
int i;
|
|
|
|
pr_info("Relocation test:\n");
|
|
pr_info("-------------------------------------------------------\n");
|
|
|
|
for (i = 0; i < ARRAY_SIZE(funcs); i++) {
|
|
u64 ret = funcs[i].f();
|
|
|
|
pr_info("%-31s 0x%016llx %s\n", funcs[i].name, ret,
|
|
ret == funcs[i].expect ? "pass" : "fail");
|
|
if (ret != funcs[i].expect)
|
|
pr_err("Relocation failed, expected 0x%016llx, not 0x%016llx\n",
|
|
funcs[i].expect, ret);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void reloc_test_exit(void)
|
|
{
|
|
}
|
|
|
|
module_init(reloc_test_init);
|
|
module_exit(reloc_test_exit);
|
|
|
|
MODULE_LICENSE("GPL v2");
|