From: "Peter Zijlstra (Intel)" peterz@infradead.org
execmem_alloc() sets permissions differently depending on the kernel configuration, CPU support for PSE and whether a page is allocated before or after mark_rodata_ro().
Add tracking for pages allocated for ITS when patching the core kernel and make sure the permissions for ITS pages are explicitly managed for both kernel and module allocations.
Fixes: 872df34d7c51 ("x86/its: Use dynamic thunks for indirect branches") Signed-off-by: Peter Zijlstra (Intel) peterz@infradead.org Co-developed-by: Mike Rapoport (Microsoft) rppt@kernel.org Signed-off-by: Mike Rapoport (Microsoft) rppt@kernel.org --- arch/x86/kernel/alternative.c | 84 ++++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 21 deletions(-)
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c index 372ef5dff631..8289e9e1f954 100644 --- a/arch/x86/kernel/alternative.c +++ b/arch/x86/kernel/alternative.c @@ -138,6 +138,25 @@ static struct module *its_mod; #endif static void *its_page; static unsigned int its_offset; +struct its_array its_pages; + +static void *__its_alloc(struct its_array *pages) +{ + void *page __free(execmem) = execmem_alloc(EXECMEM_MODULE_TEXT, PAGE_SIZE); + + if (!page) + return NULL; + + void *tmp = krealloc(pages->pages, (pages->num+1) * sizeof(void *), + GFP_KERNEL); + if (!tmp) + return NULL; + + pages->pages = tmp; + pages->pages[pages->num++] = page; + + return no_free_ptr(page); +}
/* Initialize a thunk with the "jmp *reg; int3" instructions. */ static void *its_init_thunk(void *thunk, int reg) @@ -173,6 +192,21 @@ static void *its_init_thunk(void *thunk, int reg) return thunk + offset; }
+static void its_pages_protect(struct its_array *pages) +{ + for (int i = 0; i < pages->num; i++) { + void *page = pages->pages[i]; + execmem_restore_rox(page, PAGE_SIZE); + } +} + +static void its_fini_core(void) +{ + if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX)) + its_pages_protect(&its_pages); + kfree(its_pages.pages); +} + #ifdef CONFIG_MODULES void its_init_mod(struct module *mod) { @@ -195,10 +229,8 @@ void its_fini_mod(struct module *mod) its_page = NULL; mutex_unlock(&text_mutex);
- for (int i = 0; i < mod->arch.its_pages.num; i++) { - void *page = mod->arch.its_pages.pages[i]; - execmem_restore_rox(page, PAGE_SIZE); - } + if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX)) + its_pages_protect(&mod->arch.its_pages); }
void its_free_mod(struct module *mod) @@ -212,32 +244,38 @@ void its_free_mod(struct module *mod) } kfree(mod->arch.its_pages.pages); } -#endif /* CONFIG_MODULES */
-static void *its_alloc(void) +static void *its_alloc_mod(void) { - void *page __free(execmem) = execmem_alloc(EXECMEM_MODULE_TEXT, PAGE_SIZE); + void *page = __its_alloc(&its_mod->arch.its_pages);
- if (!page) - return NULL; + if (page) + execmem_make_temp_rw(page, PAGE_SIZE);
-#ifdef CONFIG_MODULES - if (its_mod) { - struct its_array *pages = &its_mod->arch.its_pages; - void *tmp = krealloc(pages->pages, - (pages->num+1) * sizeof(void *), - GFP_KERNEL); - if (!tmp) - return NULL; + return page; +} +#endif /* CONFIG_MODULES */
- pages->pages = tmp; - pages->pages[pages->num++] = page; +static void *its_alloc_core(void) +{ + void *page = __its_alloc(&its_pages);
+ if (page) { execmem_make_temp_rw(page, PAGE_SIZE); + set_memory_x((unsigned long)page, 1); } + + return page; +} + +static void *its_alloc(void) +{ +#ifdef CONFIG_MODULES + if (its_mod) + return its_alloc_mod(); #endif /* CONFIG_MODULES */
- return no_free_ptr(page); + return its_alloc_core(); }
static void *its_allocate_thunk(int reg) @@ -291,7 +329,9 @@ u8 *its_static_thunk(int reg) return thunk; }
-#endif +#else +static inline void its_fini_core(void) {} +#endif /* CONFIG_MITIGATION_ITS */
/* * Nomenclature for variable names to simplify and clarify this code and ease @@ -2368,6 +2408,8 @@ void __init alternative_instructions(void) apply_retpolines(__retpoline_sites, __retpoline_sites_end); apply_returns(__return_sites, __return_sites_end);
+ its_fini_core(); + /* * Adjust all CALL instructions to point to func()-10, including * those in .altinstr_replacement.