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:
Linus Torvalds 2019-11-25 19:43:48 -08:00
commit f838767555
17 changed files with 902 additions and 25 deletions

View file

@ -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

View file

@ -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);
}
}

View file

@ -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
View 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
View 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 */

View file

@ -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);
}
/*