From: Andi Kleen Date: Thu, 4 Jan 2018 14:37:08 +0000 (+0000) Subject: retpoline/module: Taint kernel for missing retpoline in module X-Git-Tag: v4.1.12-124.31.3~1167 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=0c2b670605d6ae8df9abdae476d676f6a9ebd6e5;p=users%2Fjedix%2Flinux-maple.git retpoline/module: Taint kernel for missing retpoline in module There's a risk that a kernel that has full retpoline mitigations becomes vulnerable when a module gets loaded that hasn't been compiled with the right compiler or the right option. We cannot fix it, but should at least warn the user when that happens. Add a flag to each module if it has been compiled with RETPOLINE When the a module hasn't been compiled with a retpoline aware compiler, print a warning and set a taint flag. For modules it is checked at compile time, however it cannot check assembler or other non compiled objects used in the module link. Due to lack of better letter it uses taint option 'Z' We only set the taint flag for incorrectly compiled modules now, not for the main kernel, which already has other report mechanisms. Also make sure to report vulnerable for spectre if such a module has been loaded. v2: Change warning message v3: Port to latest tree Cc: jeyu@kernel.org Signed-off-by: Andi Kleen Signed-off-by: David Woodhouse (cherry picked from commit abc01f3c4bbd927f5e47cc6dff99a76a393d1bbe) Orabug: 27477743 CVE: CVE-2017-5715 Signed-off-by: Daniel Jordan Conflicts: Documentation/oops-tracing.txt (dmj: patch had Documentation/admin-guide/tainted-kernels.rst) arch/x86/kernel/cpu/bugs_64.c (dmj: patch had arch/x86/kernel/cpu/bugs.c) include/linux/kernel.h kernel/module.c kernel/panic.c Signed-off-by: Konrad Rzeszutek Wilk Reviewed-by: Darren Kenny Reviewed-by: Pavel Tatashin --- diff --git a/Documentation/oops-tracing.txt b/Documentation/oops-tracing.txt index f3ac05cc23e4..ed510bafa33e 100644 --- a/Documentation/oops-tracing.txt +++ b/Documentation/oops-tracing.txt @@ -272,6 +272,9 @@ characters, each representing a particular tainted value. 16: 'K' if the kernel has been live patched. + 17: 'Z' if a module hasn't been compiled with + a retpoline aware compiler and may be vulnerable to data leaks. + The primary reason for the 'Tainted: ' string is to tell kernel debuggers if this is a clean kernel or if anything unusual has occurred. Tainting is permanent: even if an offending module is diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h index 3154597bfb17..540dd04d58f8 100644 --- a/arch/x86/include/asm/processor.h +++ b/arch/x86/include/asm/processor.h @@ -979,4 +979,8 @@ bool xen_set_default_idle(void); void stop_this_cpu(void *dummy); void df_debug(struct pt_regs *regs, long error_code); + +void disable_retpoline(void); +bool retpoline_enabled(void); + #endif /* _ASM_X86_PROCESSOR_H */ diff --git a/arch/x86/kernel/cpu/bugs_64.c b/arch/x86/kernel/cpu/bugs_64.c index d1e84a93f70a..719344592677 100644 --- a/arch/x86/kernel/cpu/bugs_64.c +++ b/arch/x86/kernel/cpu/bugs_64.c @@ -112,6 +112,18 @@ static const char *spectre_v2_strings[] = { static enum spectre_v2_mitigation spectre_v2_enabled = SPECTRE_V2_NONE; +/* A module has been loaded. Disable reporting that we're good. */ +void disable_retpoline(void) +{ + spectre_v2_enabled = SPECTRE_V2_NONE; + pr_err("system may be vulnerable to spectre\n"); +} + +bool retpoline_enabled(void) +{ + return spectre_v2_enabled != SPECTRE_V2_NONE; +} + static void __init spec2_print_if_insecure(const char *reason) { if (boot_cpu_has_bug(X86_BUG_SPECTRE_V2)) diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 624c1ae82928..991c600eaf60 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -494,6 +494,7 @@ extern enum system_states { #define TAINT_UNSIGNED_MODULE 13 #define TAINT_SOFTLOCKUP 14 #define TAINT_LIVEPATCH 15 +#define TAINT_NO_RETPOLINE 16 extern const char hex_asc[]; #define hex_asc_lo(x) hex_asc[((x) & 0x0f)] diff --git a/kernel/module.c b/kernel/module.c index c85dd07e0871..634ecdbcb9ad 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -2713,7 +2713,16 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags) if (!get_modinfo(info, "intree")) add_taint_module(mod, TAINT_OOT_MODULE, LOCKDEP_STILL_OK); - +#ifdef RETPOLINE + if (retpoline_enabled() && !get_modinfo(info, "retpoline")) { + if (!test_taint(TAINT_NO_RETPOLINE)) { + pr_warn("%s: loading module not compiled with retpoline compiler.\n", + mod->name); + } + add_taint_module(mod, TAINT_NO_RETPOLINE, LOCKDEP_STILL_OK); + disable_retpoline(); + } +#endif if (get_modinfo(info, "staging")) { add_taint_module(mod, TAINT_CRAP, LOCKDEP_STILL_OK); pr_warn("%s: module is from the staging directory, the quality " diff --git a/kernel/panic.c b/kernel/panic.c index 21cf87a71e18..e9999af53078 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -279,6 +279,7 @@ static const struct tnt tnts[] = { { TAINT_UNSIGNED_MODULE, 'E', ' ' }, { TAINT_SOFTLOCKUP, 'L', ' ' }, { TAINT_LIVEPATCH, 'K', ' ' }, + { TAINT_NO_RETPOLINE, 'Z', ' ' }, }; /** @@ -300,6 +301,7 @@ static const struct tnt tnts[] = { * 'E' - Unsigned module has been loaded. * 'L' - A soft lockup has previously occurred. * 'K' - Kernel has been live patched. + * 'Z' - Module with no retpoline has been loaded * * The string is overwritten by the next call to print_tainted(). */ diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index b4b9ba8b6e75..a767c895aa6c 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -2139,6 +2139,14 @@ static void add_intree_flag(struct buffer *b, int is_intree) buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n"); } +/* Cannot check for assembler */ +static void add_retpoline(struct buffer *b) +{ + buf_printf(b, "\n#ifdef RETPOLINE\n"); + buf_printf(b, "MODULE_INFO(retpoline, \"Y\");\n"); + buf_printf(b, "#endif\n"); +} + static void add_staging_flag(struct buffer *b, const char *name) { static const char *staging_dir = "drivers/staging"; @@ -2471,6 +2479,7 @@ int main(int argc, char **argv) add_header(&buf, mod); add_intree_flag(&buf, !external_module); + add_retpoline(&buf); add_staging_flag(&buf, mod->name); err |= add_versions(&buf, mod); add_depends(&buf, mod, modules);