mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-06-22 14:41:27 +00:00
Livepatching changes for 5.5
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEESH4wyp42V4tXvYsjUqAMR0iAlPIFAl3bz1gACgkQUqAMR0iA lPI5fw//db5dOqAvBu/fz4k38Mc30okgCjtRh0+vhFXCCUXauQv2IhI19J2IiPpy 4t/CjaUk2QSB06NDNUxt7XsuR0yAF4E0nJHUmkDKkN8UFsi7jAjxJ/92zH3x/LE0 YWtVoWjGduO+QfLVlP22VVYh1pX5kOxXG2WTEiJtnkWYdkqtkEy7Cw2Rlzzrrym6 6kIVi3nEPtn/hOnlF/Ii5SJWh+jJrSf+XwXiuIiBupT49Ujoa4KscmhkiHnAccXb ICJpsxBIdZLxHLe/c0YO3b8r4Hvms124vlIC19Z0l9VEJ++sphDOWRrL3Zq9tFw8 FwIKq8Ex9UFfldOnpD5q/PRhM9Xgfsw8UYYZZpXQeW6z7qzv1iVpM6BQlt3dYQaL pb21UXIfzaTdZIsUgnetypbqWIFNZxovrhORpjqxo6WV4lSaVx4MPE2/Le/G8xPR DT+a6yyzTyM0XbZ0MCVDfuZ+ZRaA1zfKEcsYEu883b7yK4z+TZbT61vnEKqq8+40 EgOZnNjBENZLRQY0RofQsO5zGwcaanVOtBOmYDXtP/fup8/1SRZ25zmmIVhvChJG iQwCDw6IMqnae/FsMb+kBTDCRJXpN028iYGY7UAoiCuvzc0qm0gJXsGdZLqMvjEh nEdKKN2ze+03s6I9AcISKdnbUVphhb/xeDKRBkMgcooWLrfWj5E= =i37E -----END PGP SIGNATURE----- Merge tag 'livepatching-for-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching Pull livepatching updates from Petr Mladek: - New API to track system state changes done be livepatch callbacks. It helps to maintain compatibility between livepatches. - Update Kconfig help text. ORC is another reliable unwinder. - Disable generic selftest timeout. Livepatch selftests have their own per-operation fine-grained timeouts. * tag 'livepatching-for-5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching: x86/stacktrace: update kconfig help text for reliable unwinders livepatch: Selftests of the API for tracking system state changes livepatch: Documentation of the new API for tracking system state changes livepatch: Allow to distinguish different version of system state changes livepatch: Basic API to track system state changes livepatch: Keep replaced patches until post_patch callback is called selftests/livepatch: Disable the timeout
This commit is contained in:
commit
f838767555
17 changed files with 902 additions and 25 deletions
|
@ -1,4 +1,4 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_LIVEPATCH) += livepatch.o
|
||||
|
||||
livepatch-objs := core.o patch.o shadow.o transition.o
|
||||
livepatch-objs := core.o patch.o shadow.o state.o transition.o
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <asm/cacheflush.h>
|
||||
#include "core.h"
|
||||
#include "patch.h"
|
||||
#include "state.h"
|
||||
#include "transition.h"
|
||||
|
||||
/*
|
||||
|
@ -632,7 +633,7 @@ static void klp_free_objects_dynamic(struct klp_patch *patch)
|
|||
* The operation must be completed by calling klp_free_patch_finish()
|
||||
* outside klp_mutex.
|
||||
*/
|
||||
void klp_free_patch_start(struct klp_patch *patch)
|
||||
static void klp_free_patch_start(struct klp_patch *patch)
|
||||
{
|
||||
if (!list_empty(&patch->list))
|
||||
list_del(&patch->list);
|
||||
|
@ -677,6 +678,23 @@ static void klp_free_patch_work_fn(struct work_struct *work)
|
|||
klp_free_patch_finish(patch);
|
||||
}
|
||||
|
||||
void klp_free_patch_async(struct klp_patch *patch)
|
||||
{
|
||||
klp_free_patch_start(patch);
|
||||
schedule_work(&patch->free_work);
|
||||
}
|
||||
|
||||
void klp_free_replaced_patches_async(struct klp_patch *new_patch)
|
||||
{
|
||||
struct klp_patch *old_patch, *tmp_patch;
|
||||
|
||||
klp_for_each_patch_safe(old_patch, tmp_patch) {
|
||||
if (old_patch == new_patch)
|
||||
return;
|
||||
klp_free_patch_async(old_patch);
|
||||
}
|
||||
}
|
||||
|
||||
static int klp_init_func(struct klp_object *obj, struct klp_func *func)
|
||||
{
|
||||
if (!func->old_name)
|
||||
|
@ -992,6 +1010,13 @@ int klp_enable_patch(struct klp_patch *patch)
|
|||
|
||||
mutex_lock(&klp_mutex);
|
||||
|
||||
if (!klp_is_patch_compatible(patch)) {
|
||||
pr_err("Livepatch patch (%s) is not compatible with the already installed livepatches.\n",
|
||||
patch->mod->name);
|
||||
mutex_unlock(&klp_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = klp_init_patch_early(patch);
|
||||
if (ret) {
|
||||
mutex_unlock(&klp_mutex);
|
||||
|
@ -1022,12 +1047,13 @@ err:
|
|||
EXPORT_SYMBOL_GPL(klp_enable_patch);
|
||||
|
||||
/*
|
||||
* This function removes replaced patches.
|
||||
* This function unpatches objects from the replaced livepatches.
|
||||
*
|
||||
* We could be pretty aggressive here. It is called in the situation where
|
||||
* these structures are no longer accessible. All functions are redirected
|
||||
* by the klp_transition_patch. They use either a new code or they are in
|
||||
* the original code because of the special nop function patches.
|
||||
* these structures are no longer accessed from the ftrace handler.
|
||||
* All functions are redirected by the klp_transition_patch. They
|
||||
* use either a new code or they are in the original code because
|
||||
* of the special nop function patches.
|
||||
*
|
||||
* The only exception is when the transition was forced. In this case,
|
||||
* klp_ftrace_handler() might still see the replaced patch on the stack.
|
||||
|
@ -1035,18 +1061,16 @@ EXPORT_SYMBOL_GPL(klp_enable_patch);
|
|||
* thanks to RCU. We only have to keep the patches on the system. Also
|
||||
* this is handled transparently by patch->module_put.
|
||||
*/
|
||||
void klp_discard_replaced_patches(struct klp_patch *new_patch)
|
||||
void klp_unpatch_replaced_patches(struct klp_patch *new_patch)
|
||||
{
|
||||
struct klp_patch *old_patch, *tmp_patch;
|
||||
struct klp_patch *old_patch;
|
||||
|
||||
klp_for_each_patch_safe(old_patch, tmp_patch) {
|
||||
klp_for_each_patch(old_patch) {
|
||||
if (old_patch == new_patch)
|
||||
return;
|
||||
|
||||
old_patch->enabled = false;
|
||||
klp_unpatch_objects(old_patch);
|
||||
klp_free_patch_start(old_patch);
|
||||
schedule_work(&old_patch->free_work);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,9 @@ extern struct list_head klp_patches;
|
|||
#define klp_for_each_patch(patch) \
|
||||
list_for_each_entry(patch, &klp_patches, list)
|
||||
|
||||
void klp_free_patch_start(struct klp_patch *patch);
|
||||
void klp_discard_replaced_patches(struct klp_patch *new_patch);
|
||||
void klp_free_patch_async(struct klp_patch *patch);
|
||||
void klp_free_replaced_patches_async(struct klp_patch *new_patch);
|
||||
void klp_unpatch_replaced_patches(struct klp_patch *new_patch);
|
||||
void klp_discard_nops(struct klp_patch *new_patch);
|
||||
|
||||
static inline bool klp_is_object_loaded(struct klp_object *obj)
|
||||
|
|
119
kernel/livepatch/state.c
Normal file
119
kernel/livepatch/state.c
Normal file
|
@ -0,0 +1,119 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* system_state.c - State of the system modified by livepatches
|
||||
*
|
||||
* Copyright (C) 2019 SUSE
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/livepatch.h>
|
||||
#include "core.h"
|
||||
#include "state.h"
|
||||
#include "transition.h"
|
||||
|
||||
#define klp_for_each_state(patch, state) \
|
||||
for (state = patch->states; state && state->id; state++)
|
||||
|
||||
/**
|
||||
* klp_get_state() - get information about system state modified by
|
||||
* the given patch
|
||||
* @patch: livepatch that modifies the given system state
|
||||
* @id: custom identifier of the modified system state
|
||||
*
|
||||
* Checks whether the given patch modifies the given system state.
|
||||
*
|
||||
* The function can be called either from pre/post (un)patch
|
||||
* callbacks or from the kernel code added by the livepatch.
|
||||
*
|
||||
* Return: pointer to struct klp_state when found, otherwise NULL.
|
||||
*/
|
||||
struct klp_state *klp_get_state(struct klp_patch *patch, unsigned long id)
|
||||
{
|
||||
struct klp_state *state;
|
||||
|
||||
klp_for_each_state(patch, state) {
|
||||
if (state->id == id)
|
||||
return state;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(klp_get_state);
|
||||
|
||||
/**
|
||||
* klp_get_prev_state() - get information about system state modified by
|
||||
* the already installed livepatches
|
||||
* @id: custom identifier of the modified system state
|
||||
*
|
||||
* Checks whether already installed livepatches modify the given
|
||||
* system state.
|
||||
*
|
||||
* The same system state can be modified by more non-cumulative
|
||||
* livepatches. It is expected that the latest livepatch has
|
||||
* the most up-to-date information.
|
||||
*
|
||||
* The function can be called only during transition when a new
|
||||
* livepatch is being enabled or when such a transition is reverted.
|
||||
* It is typically called only from from pre/post (un)patch
|
||||
* callbacks.
|
||||
*
|
||||
* Return: pointer to the latest struct klp_state from already
|
||||
* installed livepatches, NULL when not found.
|
||||
*/
|
||||
struct klp_state *klp_get_prev_state(unsigned long id)
|
||||
{
|
||||
struct klp_patch *patch;
|
||||
struct klp_state *state, *last_state = NULL;
|
||||
|
||||
if (WARN_ON_ONCE(!klp_transition_patch))
|
||||
return NULL;
|
||||
|
||||
klp_for_each_patch(patch) {
|
||||
if (patch == klp_transition_patch)
|
||||
goto out;
|
||||
|
||||
state = klp_get_state(patch, id);
|
||||
if (state)
|
||||
last_state = state;
|
||||
}
|
||||
|
||||
out:
|
||||
return last_state;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(klp_get_prev_state);
|
||||
|
||||
/* Check if the patch is able to deal with the existing system state. */
|
||||
static bool klp_is_state_compatible(struct klp_patch *patch,
|
||||
struct klp_state *old_state)
|
||||
{
|
||||
struct klp_state *state;
|
||||
|
||||
state = klp_get_state(patch, old_state->id);
|
||||
|
||||
/* A cumulative livepatch must handle all already modified states. */
|
||||
if (!state)
|
||||
return !patch->replace;
|
||||
|
||||
return state->version >= old_state->version;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the new livepatch will not break the existing system states.
|
||||
* Cumulative patches must handle all already modified states.
|
||||
* Non-cumulative patches can touch already modified states.
|
||||
*/
|
||||
bool klp_is_patch_compatible(struct klp_patch *patch)
|
||||
{
|
||||
struct klp_patch *old_patch;
|
||||
struct klp_state *old_state;
|
||||
|
||||
klp_for_each_patch(old_patch) {
|
||||
klp_for_each_state(old_patch, old_state) {
|
||||
if (!klp_is_state_compatible(patch, old_state))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
9
kernel/livepatch/state.h
Normal file
9
kernel/livepatch/state.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LIVEPATCH_STATE_H
|
||||
#define _LIVEPATCH_STATE_H
|
||||
|
||||
#include <linux/livepatch.h>
|
||||
|
||||
bool klp_is_patch_compatible(struct klp_patch *patch);
|
||||
|
||||
#endif /* _LIVEPATCH_STATE_H */
|
|
@ -78,7 +78,7 @@ static void klp_complete_transition(void)
|
|||
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
|
||||
|
||||
if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
|
||||
klp_discard_replaced_patches(klp_transition_patch);
|
||||
klp_unpatch_replaced_patches(klp_transition_patch);
|
||||
klp_discard_nops(klp_transition_patch);
|
||||
}
|
||||
|
||||
|
@ -446,14 +446,14 @@ void klp_try_complete_transition(void)
|
|||
klp_complete_transition();
|
||||
|
||||
/*
|
||||
* It would make more sense to free the patch in
|
||||
* It would make more sense to free the unused patches in
|
||||
* klp_complete_transition() but it is called also
|
||||
* from klp_cancel_transition().
|
||||
*/
|
||||
if (!patch->enabled) {
|
||||
klp_free_patch_start(patch);
|
||||
schedule_work(&patch->free_work);
|
||||
}
|
||||
if (!patch->enabled)
|
||||
klp_free_patch_async(patch);
|
||||
else if (patch->replace)
|
||||
klp_free_replaced_patches_async(patch);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue