Thread (17 messages) 17 messages, 4 authors, 2025-01-24

Re: [PATCH v2 06/10] module: introduce MODULE_STATE_GONE

flat view

From: Petr Pavlu <petr.pavlu@suse.com>
Date: 2025-01-24 12:59:59
Also in: linux-kselftest, linux-mm, linux-trace-kernel, linux-um, live-patching, lkml

On 1/24/25 12:06, Mike Rapoport wrote:
quoted hunk ↗ jump to hunk
On Thu, Jan 23, 2025 at 03:16:28PM +0100, Petr Pavlu wrote:
quoted
On 1/21/25 10:57, Mike Rapoport wrote:
quoted
In order to use execmem's API for temporal remapping of the memory
allocated from ROX cache as writable, there is a need to distinguish
between the state when the module is being formed and the state when it is
deconstructed and freed so that when module_memory_free() is called from
error paths during module loading it could restore ROX mappings.

Replace open coded checks for MODULE_STATE_UNFORMED with a helper
function module_is_formed() and add a new MODULE_STATE_GONE that will be
set when the module is deconstructed and freed.
I don't fully follow why this case requires a new module state. My
understanding it that the function load_module() has the necessary
context that after calling layout_and_allocate(), the updated ROX
mappings need to be restored. I would then expect the function to be
appropriately able to unwind this operation in case of an error. It
could be done by having a helper that walks the mappings and calls
execmem_restore_rox(), or if you want to keep it in module_memory_free()
as done in the patch #7 then a flag could be passed down to
module_deallocate() -> free_mod_mem() -> module_memory_free()?
Initially I wanted to track ROX <-> RW transitions in struct module_memory
so that module_memory_free() could do the right thing depending on memory
state. But that meant either ugly games with const'ness in strict_rwx.c,
an additional helper or a new global module state. The latter seemed the
most elegant to me.
If a new global module state is really that intrusive, I can drop it in
favor a helper that will be called from error handling paths. E.g.
something like the patch below (on top of this series and with this patch
reverted)
diff --git a/kernel/module/main.c b/kernel/module/main.c
index 7164cd353a78..4a02503836d7 100644
--- a/kernel/module/main.c
+++ b/kernel/module/main.c
@@ -1268,13 +1268,20 @@ static int module_memory_alloc(struct module *mod, enum mod_mem_type type)
 	return 0;
 }
 
+static void module_memory_restore_rox(struct module *mod)
+{
+	for_class_mod_mem_type(type, text) {
+		struct module_memory *mem = &mod->mem[type];
+
+		if (mem->is_rox)
+			execmem_restore_rox(mem->base, mem->size);
+	}
+}
+
 static void module_memory_free(struct module *mod, enum mod_mem_type type)
 {
 	struct module_memory *mem = &mod->mem[type];
 
-	if (mod->state == MODULE_STATE_UNFORMED && mem->is_rox)
-		execmem_restore_rox(mem->base, mem->size);
-
 	execmem_free(mem->base);
 }
 
@@ -2617,6 +2624,7 @@ static int move_module(struct module *mod, struct load_info *info)
 
 	return 0;
 out_err:
+	module_memory_restore_rox(mod);
 	for (t--; t >= 0; t--)
 		module_memory_free(mod, t);
 	if (codetag_section_found)
@@ -3372,6 +3380,7 @@ static int load_module(struct load_info *info, const char __user *uargs,
 				       mod->mem[type].size);
 	}
 
+	module_memory_restore_rox(mod);
 	module_deallocate(mod, info);
  free_copy:
 	/*
 
This looks better to me.

My view is that the module_state tracks major stages of a module during
its lifecycle. It provides information to the module loader itself,
other subsystems that need to closely interact with modules, and to the
userspace via the initstate sysfs attribute.

Adding a new state means potentially more complexity for all these
parts. In this case, the state was needed because of a logic that is
local only to the module loader, or even just to the function
load_module(). I think it is better to avoid adding a new state only for
that.

-- 
Thanks,
Petr
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help