]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
x86/its: explicitly manage permissions for ITS pages
authorPeter Zijlstra (Intel) <peterz@infradead.org>
Tue, 3 Jun 2025 11:14:44 +0000 (14:14 +0300)
committerPeter Zijlstra <peterz@infradead.org>
Wed, 11 Jun 2025 09:20:52 +0000 (11:20 +0200)
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>
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
arch/x86/kernel/alternative.c

index b50fe6ce4655e83e6bca8c97a99c62e1fbf9c68b..6455f7f751b31091f3f209b98a33875fb8d9d404 100644 (file)
@@ -116,6 +116,24 @@ 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)
@@ -151,6 +169,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)
 {
@@ -173,10 +206,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)
@@ -194,28 +225,23 @@ void its_free_mod(struct module *mod)
 
 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)
                return NULL;
 
-#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;
-
-               pages->pages = tmp;
-               pages->pages[pages->num++] = page;
+       execmem_make_temp_rw(page, PAGE_SIZE);
+       if (pages == &its_pages)
+               set_memory_x((unsigned long)page, 1);
 
-               execmem_make_temp_rw(page, PAGE_SIZE);
-       }
-#endif /* CONFIG_MODULES */
-
-       return no_free_ptr(page);
+       return page;
 }
 
 static void *its_allocate_thunk(int reg)
@@ -269,7 +295,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
@@ -2339,6 +2367,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.