Commit a82b2645 authored by Peter Zijlstra (Intel)'s avatar Peter Zijlstra (Intel) Committed by Peter Zijlstra
Browse files

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: 872df34d ("x86/its: Use dynamic thunks for indirect branches")
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Co-developed-by: default avatarMike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: default avatarMike Rapoport (Microsoft) <rppt@kernel.org>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: default avatarNikolay Borisov <nik.borisov@suse.com>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/20250603111446.2609381-5-rppt@kernel.org
parent 0b0cae71
Loading
Loading
Loading
Loading
+52 −22
Original line number Diff line number Diff line
@@ -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;

	if (!page)
		return NULL;
#ifdef CONFIG_MODULE
	if (its_mod)
		pages = &its_mod->arch.its_pages;
#endif

#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)
	page = __its_alloc(pages);
	if (!page)
		return NULL;

		pages->pages = tmp;
		pages->pages[pages->num++] = page;

	execmem_make_temp_rw(page, PAGE_SIZE);
	}
#endif /* CONFIG_MODULES */
	if (pages == &its_pages)
		set_memory_x((unsigned long)page, 1);

	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.