s390: add automatic detection of the spectre defense

Automatically decide between nobp vs. expolines if the spectre_v2=auto
kernel parameter is specified or CONFIG_EXPOLINE_AUTO=y is set.

The decision made at boot time due to CONFIG_EXPOLINE_AUTO=y being set
can be overruled with the nobp, nospec and spectre_v2 kernel parameters.

Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
Martin Schwidefsky 2018-03-23 13:04:49 +01:00
parent b2e2f43a01
commit 6e179d6412
6 changed files with 52 additions and 38 deletions

View File

@ -576,7 +576,7 @@ choice
config EXPOLINE_OFF
bool "spectre_v2=off"
config EXPOLINE_MEDIUM
config EXPOLINE_AUTO
bool "spectre_v2=auto"
config EXPOLINE_FULL

View File

@ -81,7 +81,7 @@ ifdef CONFIG_EXPOLINE
CC_FLAGS_EXPOLINE += -mfunction-return=thunk
CC_FLAGS_EXPOLINE += -mindirect-branch-table
export CC_FLAGS_EXPOLINE
cflags-y += $(CC_FLAGS_EXPOLINE)
cflags-y += $(CC_FLAGS_EXPOLINE) -DCC_USING_EXPOLINE
endif
endif

View File

@ -6,12 +6,10 @@
#include <linux/types.h>
extern int nospec_call_disable;
extern int nospec_return_disable;
extern int nospec_disable;
void nospec_init_branches(void);
void nospec_call_revert(s32 *start, s32 *end);
void nospec_return_revert(s32 *start, s32 *end);
void nospec_revert(s32 *start, s32 *end);
#endif /* __ASSEMBLY__ */

View File

@ -2,6 +2,7 @@
#include <linux/module.h>
#include <asm/alternative.h>
#include <asm/facility.h>
#include <asm/nospec-branch.h>
#define MAX_PATCH_LEN (255 - 1)

View File

@ -159,7 +159,7 @@ int module_frob_arch_sections(Elf_Ehdr *hdr, Elf_Shdr *sechdrs,
me->core_layout.size += me->arch.got_size;
me->arch.plt_offset = me->core_layout.size;
if (me->arch.plt_size) {
if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_call_disable)
if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable)
me->arch.plt_size += PLT_ENTRY_SIZE;
me->core_layout.size += me->arch.plt_size;
}
@ -318,8 +318,7 @@ static int apply_rela(Elf_Rela *rela, Elf_Addr base, Elf_Sym *symtab,
info->plt_offset;
ip[0] = 0x0d10e310; /* basr 1,0 */
ip[1] = 0x100a0004; /* lg 1,10(1) */
if (IS_ENABLED(CONFIG_EXPOLINE) &&
!nospec_call_disable) {
if (IS_ENABLED(CONFIG_EXPOLINE) && !nospec_disable) {
unsigned int *ij;
ij = me->core_layout.base +
me->arch.plt_offset +
@ -440,7 +439,7 @@ int module_finalize(const Elf_Ehdr *hdr,
void *aseg;
if (IS_ENABLED(CONFIG_EXPOLINE) &&
!nospec_call_disable && me->arch.plt_size) {
!nospec_disable && me->arch.plt_size) {
unsigned int *ij;
ij = me->core_layout.base + me->arch.plt_offset +
@ -467,11 +466,11 @@ int module_finalize(const Elf_Ehdr *hdr,
if (IS_ENABLED(CONFIG_EXPOLINE) &&
(!strcmp(".nospec_call_table", secname)))
nospec_call_revert(aseg, aseg + s->sh_size);
nospec_revert(aseg, aseg + s->sh_size);
if (IS_ENABLED(CONFIG_EXPOLINE) &&
(!strcmp(".nospec_return_table", secname)))
nospec_return_revert(aseg, aseg + s->sh_size);
nospec_revert(aseg, aseg + s->sh_size);
}
jump_label_apply_nops(me);

View File

@ -10,10 +10,17 @@ static int __init nobp_setup_early(char *str)
rc = kstrtobool(str, &enabled);
if (rc)
return rc;
if (enabled && test_facility(82))
if (enabled && test_facility(82)) {
/*
* The user explicitely requested nobp=1, enable it and
* disable the expoline support.
*/
__set_facility(82, S390_lowcore.alt_stfle_fac_list);
else
if (IS_ENABLED(CONFIG_EXPOLINE))
nospec_disable = 1;
} else {
__clear_facility(82, S390_lowcore.alt_stfle_fac_list);
}
return 0;
}
early_param("nobp", nobp_setup_early);
@ -27,31 +34,46 @@ early_param("nospec", nospec_setup_early);
#ifdef CONFIG_EXPOLINE
int nospec_call_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
int nospec_return_disable = !IS_ENABLED(CONFIG_EXPOLINE_FULL);
int nospec_disable = IS_ENABLED(CONFIG_EXPOLINE_OFF);
static int __init nospectre_v2_setup_early(char *str)
{
nospec_call_disable = 1;
nospec_return_disable = 1;
nospec_disable = 1;
return 0;
}
early_param("nospectre_v2", nospectre_v2_setup_early);
static int __init spectre_v2_auto_early(void)
{
if (IS_ENABLED(CC_USING_EXPOLINE)) {
/*
* The kernel has been compiled with expolines.
* Keep expolines enabled and disable nobp.
*/
nospec_disable = 0;
__clear_facility(82, S390_lowcore.alt_stfle_fac_list);
}
/*
* If the kernel has not been compiled with expolines the
* nobp setting decides what is done, this depends on the
* CONFIG_KERNEL_NP option and the nobp/nospec parameters.
*/
return 0;
}
#ifdef CONFIG_EXPOLINE_AUTO
early_initcall(spectre_v2_auto_early);
#endif
static int __init spectre_v2_setup_early(char *str)
{
if (str && !strncmp(str, "on", 2)) {
nospec_call_disable = 0;
nospec_return_disable = 0;
}
if (str && !strncmp(str, "off", 3)) {
nospec_call_disable = 1;
nospec_return_disable = 1;
}
if (str && !strncmp(str, "auto", 4)) {
nospec_call_disable = 0;
nospec_return_disable = 1;
nospec_disable = 0;
__clear_facility(82, S390_lowcore.alt_stfle_fac_list);
}
if (str && !strncmp(str, "off", 3))
nospec_disable = 1;
if (str && !strncmp(str, "auto", 4))
spectre_v2_auto_early();
return 0;
}
early_param("spectre_v2", spectre_v2_setup_early);
@ -104,15 +126,9 @@ static void __init_or_module __nospec_revert(s32 *start, s32 *end)
}
}
void __init_or_module nospec_call_revert(s32 *start, s32 *end)
void __init_or_module nospec_revert(s32 *start, s32 *end)
{
if (nospec_call_disable)
__nospec_revert(start, end);
}
void __init_or_module nospec_return_revert(s32 *start, s32 *end)
{
if (nospec_return_disable)
if (nospec_disable)
__nospec_revert(start, end);
}
@ -120,8 +136,8 @@ extern s32 __nospec_call_start[], __nospec_call_end[];
extern s32 __nospec_return_start[], __nospec_return_end[];
void __init nospec_init_branches(void)
{
nospec_call_revert(__nospec_call_start, __nospec_call_end);
nospec_return_revert(__nospec_return_start, __nospec_return_end);
nospec_revert(__nospec_call_start, __nospec_call_end);
nospec_revert(__nospec_return_start, __nospec_return_end);
}
#endif /* CONFIG_EXPOLINE */