x86/its: explicitly manage permissions for ITS pages

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: 872df34d7c ("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>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Nikolay Borisov <nik.borisov@suse.com>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/20250603111446.2609381-5-rppt@kernel.org
This commit is contained in:
Peter Zijlstra (Intel) 2025-06-03 14:14:44 +03:00 committed by Peter Zijlstra
parent 0b0cae7119
commit a82b26451d

View File

@ -116,6 +116,24 @@ static struct module *its_mod;
#endif #endif
static void *its_page; static void *its_page;
static unsigned int its_offset; 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. */ /* Initialize a thunk with the "jmp *reg; int3" instructions. */
static void *its_init_thunk(void *thunk, int reg) static void *its_init_thunk(void *thunk, int reg)
@ -151,6 +169,21 @@ static void *its_init_thunk(void *thunk, int reg)
return thunk + offset; 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 #ifdef CONFIG_MODULES
void its_init_mod(struct module *mod) void its_init_mod(struct module *mod)
{ {
@ -173,10 +206,8 @@ void its_fini_mod(struct module *mod)
its_page = NULL; its_page = NULL;
mutex_unlock(&text_mutex); mutex_unlock(&text_mutex);
for (int i = 0; i < mod->arch.its_pages.num; i++) { if (IS_ENABLED(CONFIG_STRICT_MODULE_RWX))
void *page = mod->arch.its_pages.pages[i]; its_pages_protect(&mod->arch.its_pages);
execmem_restore_rox(page, PAGE_SIZE);
}
} }
void its_free_mod(struct module *mod) void its_free_mod(struct module *mod)
@ -194,28 +225,23 @@ void its_free_mod(struct module *mod)
static void *its_alloc(void) static void *its_alloc(void)
{ {
void *page __free(execmem) = execmem_alloc(EXECMEM_MODULE_TEXT, PAGE_SIZE); struct its_array *pages = &its_pages;
void *page;
#ifdef CONFIG_MODULE
if (its_mod)
pages = &its_mod->arch.its_pages;
#endif
page = __its_alloc(pages);
if (!page) if (!page)
return NULL; return NULL;
#ifdef CONFIG_MODULES execmem_make_temp_rw(page, PAGE_SIZE);
if (its_mod) { if (pages == &its_pages)
struct its_array *pages = &its_mod->arch.its_pages; set_memory_x((unsigned long)page, 1);
void *tmp = krealloc(pages->pages,
(pages->num+1) * sizeof(void *),
GFP_KERNEL);
if (!tmp)
return NULL;
pages->pages = tmp; return page;
pages->pages[pages->num++] = page;
execmem_make_temp_rw(page, PAGE_SIZE);
}
#endif /* CONFIG_MODULES */
return no_free_ptr(page);
} }
static void *its_allocate_thunk(int reg) static void *its_allocate_thunk(int reg)
@ -269,7 +295,9 @@ u8 *its_static_thunk(int reg)
return thunk; 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 * Nomenclature for variable names to simplify and clarify this code and ease
@ -2339,6 +2367,8 @@ void __init alternative_instructions(void)
apply_retpolines(__retpoline_sites, __retpoline_sites_end); apply_retpolines(__retpoline_sites, __retpoline_sites_end);
apply_returns(__return_sites, __return_sites_end); apply_returns(__return_sites, __return_sites_end);
its_fini_core();
/* /*
* Adjust all CALL instructions to point to func()-10, including * Adjust all CALL instructions to point to func()-10, including
* those in .altinstr_replacement. * those in .altinstr_replacement.