[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250115082431.5550-3-pmladek@suse.com>
Date: Wed, 15 Jan 2025 09:24:14 +0100
From: Petr Mladek <pmladek@...e.com>
To: Josh Poimboeuf <jpoimboe@...nel.org>,
Miroslav Benes <mbenes@...e.cz>
Cc: Joe Lawrence <joe.lawrence@...hat.com>,
Nicolai Stange <nstange@...e.de>,
live-patching@...r.kernel.org,
linux-kernel@...r.kernel.org,
Petr Mladek <pmladek@...e.com>
Subject: [PATCH v1 02/19] livepatch: Allow to handle lifetime of shadow variables using the livepatch state
Managing the lifetime of shadow variables becomes challenging when atomic
replace is used. The new patch cannot determine whether a shadow variable
has already been used by a previous live patch or if there is a shadow
variable that is no longer in use.
Shadow variables are typically used alongside callbacks. At a minimum,
the @post_unpatch callback is called to free shadow variables that are
no longer needed. Additionally, @post_patch and @pre_unpatch callbacks
are sometimes used to enable or disable the use of shadow variables.
This is necessary when the shadow variable can only be used when
the entire system is capable of handling it.
The complexity increases when using the atomic replace feature,
as only the callbacks from the new live patch are executed.
Newly created live patches might manage obsolete shadow variables,
ensuring the upgrade functions correctly. However, older live
patches are unaware of shadow variables introduced later, which
could lead to leaks during a downgrade. Additionally, these
leaked variables might retain outdated information, potentially
causing issues if those variables are reused in a subsequent upgrade.
These issues are better addressed with the new callbacks associated
with a live patch state. These callbacks are triggered both when
the states are first introduced and when they become obsolete.
Additionally, the callbacks are invoked from the patch that
originally supported the state, ensuring that even downgrades are
handled safely.
Let’s formalize the process: Associate a shadow variable with a live
patch state by setting the "state.is_shadow" flag and using the same
"id" in both struct klp_shadow and struct klp_state.
The shadow variable will then share the same lifetime as the livepatch
state, allowing obsolete shadow variables to be automatically freed
without requiring an additional callback.
A generic callback will free the shadow variables using
the state->callbacks.shadow_dtor callback, if provided.
Signed-off-by: Petr Mladek <pmladek@...e.com>
---
include/linux/livepatch.h | 15 ++++++++++-----
kernel/livepatch/state.c | 3 +++
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 79dddf3dbd52..c624f1105663 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -132,6 +132,11 @@ struct klp_object {
struct klp_patch;
struct klp_state;
+typedef int (*klp_shadow_ctor_t)(void *obj,
+ void *shadow_data,
+ void *ctor_data);
+typedef void (*klp_shadow_dtor_t)(void *obj, void *shadow_data);
+
/**
* struct klp_state_callbacks - callbacks manipulating the state
* @pre_patch: executed only when the state is being enabled
@@ -142,6 +147,7 @@ struct klp_state;
* before code unpatching
* @post_unpatch: executed only when the state is being disabled
* after code unpatching
+ * @shadow_dtor: destructor for the related shadow variable
* @pre_patch_succeeded: internal state used by a rollback on error
*
* All callbacks are optional.
@@ -158,6 +164,7 @@ struct klp_state_callbacks {
void (*post_patch)(struct klp_patch *patch, struct klp_state *state);
void (*pre_unpatch)(struct klp_patch *patch, struct klp_state *state);
void (*post_unpatch)(struct klp_patch *patch, struct klp_state *state);
+ klp_shadow_dtor_t shadow_dtor;
bool pre_patch_succeeded;
};
@@ -166,12 +173,15 @@ struct klp_state_callbacks {
* @id: system state identifier (non-zero)
* @version: version of the change
* @callbacks: optional callbacks used when enabling or disabling the state
+ * @is_shadow: the state handles lifetime of a shadow variable with
+ * the same @id
* @data: custom data
*/
struct klp_state {
unsigned long id;
unsigned int version;
struct klp_state_callbacks callbacks;
+ bool is_shadow;
void *data;
};
@@ -246,11 +256,6 @@ static inline bool klp_have_reliable_stack(void)
IS_ENABLED(CONFIG_HAVE_RELIABLE_STACKTRACE);
}
-typedef int (*klp_shadow_ctor_t)(void *obj,
- void *shadow_data,
- void *ctor_data);
-typedef void (*klp_shadow_dtor_t)(void *obj, void *shadow_data);
-
void *klp_shadow_get(void *obj, unsigned long id);
void *klp_shadow_alloc(void *obj, unsigned long id,
size_t size, gfp_t gfp_flags,
diff --git a/kernel/livepatch/state.c b/kernel/livepatch/state.c
index bf7ed988d2bb..16ad695b1e88 100644
--- a/kernel/livepatch/state.c
+++ b/kernel/livepatch/state.c
@@ -201,6 +201,9 @@ void klp_states_post_unpatch(struct klp_patch *patch)
if (state->callbacks.post_unpatch)
state->callbacks.post_unpatch(patch, state);
+ if (state->is_shadow)
+ klp_shadow_free_all(state->id, state->callbacks.shadow_dtor);
+
state->callbacks.pre_patch_succeeded = 0;
}
}
--
2.47.1
Powered by blists - more mailing lists