Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
0e672adc
Commit
0e672adc
authored
Nov 25, 2019
by
Petr Mladek
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'for-5.5/system-state' into for-linus
parents
d891433b
ecd25094
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
901 additions
and
21 deletions
+901
-21
Documentation/livepatch/index.rst
Documentation/livepatch/index.rst
+1
-0
Documentation/livepatch/system-state.rst
Documentation/livepatch/system-state.rst
+167
-0
include/linux/livepatch.h
include/linux/livepatch.h
+17
-0
kernel/livepatch/Makefile
kernel/livepatch/Makefile
+1
-1
kernel/livepatch/core.c
kernel/livepatch/core.c
+34
-10
kernel/livepatch/core.h
kernel/livepatch/core.h
+3
-2
kernel/livepatch/state.c
kernel/livepatch/state.c
+119
-0
kernel/livepatch/state.h
kernel/livepatch/state.h
+9
-0
kernel/livepatch/transition.c
kernel/livepatch/transition.c
+6
-6
lib/livepatch/Makefile
lib/livepatch/Makefile
+4
-1
lib/livepatch/test_klp_state.c
lib/livepatch/test_klp_state.c
+162
-0
lib/livepatch/test_klp_state2.c
lib/livepatch/test_klp_state2.c
+191
-0
lib/livepatch/test_klp_state3.c
lib/livepatch/test_klp_state3.c
+5
-0
tools/testing/selftests/livepatch/Makefile
tools/testing/selftests/livepatch/Makefile
+2
-1
tools/testing/selftests/livepatch/test-state.sh
tools/testing/selftests/livepatch/test-state.sh
+180
-0
No files found.
Documentation/livepatch/index.rst
View file @
0e672adc
...
...
@@ -12,6 +12,7 @@ Kernel Livepatching
cumulative-patches
module-elf-format
shadow-vars
system-state
.. only:: subproject and html
...
...
Documentation/livepatch/system-state.rst
0 → 100644
View file @
0e672adc
====================
System State Changes
====================
Some users are really reluctant to reboot a system. This brings the need
to provide more livepatches and maintain some compatibility between them.
Maintaining more livepatches is much easier with cumulative livepatches.
Each new livepatch completely replaces any older one. It can keep,
add, and even remove fixes. And it is typically safe to replace any version
of the livepatch with any other one thanks to the atomic replace feature.
The problems might come with shadow variables and callbacks. They might
change the system behavior or state so that it is no longer safe to
go back and use an older livepatch or the original kernel code. Also
any new livepatch must be able to detect what changes have already been
done by the already installed livepatches.
This is where the livepatch system state tracking gets useful. It
allows to:
- store data needed to manipulate and restore the system state
- define compatibility between livepatches using a change id
and version
1. Livepatch system state API
=============================
The state of the system might get modified either by several livepatch callbacks
or by the newly used code. Also it must be possible to find changes done by
already installed livepatches.
Each modified state is described by struct klp_state, see
include/linux/livepatch.h.
Each livepatch defines an array of struct klp_states. They mention
all states that the livepatch modifies.
The livepatch author must define the following two fields for each
struct klp_state:
- *id*
- Non-zero number used to identify the affected system state.
- *version*
- Number describing the variant of the system state change that
is supported by the given livepatch.
The state can be manipulated using two functions:
- *klp_get_state(patch, id)*
- Get struct klp_state associated with the given livepatch
and state id.
- *klp_get_prev_state(id)*
- Get struct klp_state associated with the given feature id and
already installed livepatches.
2. Livepatch compatibility
==========================
The system state version is used to prevent loading incompatible livepatches.
The check is done when the livepatch is enabled. The rules are:
- Any completely new system state modification is allowed.
- System state modifications with the same or higher version are allowed
for already modified system states.
- Cumulative livepatches must handle all system state modifications from
already installed livepatches.
- Non-cumulative livepatches are allowed to touch already modified
system states.
3. Supported scenarios
======================
Livepatches have their life-cycle and the same is true for the system
state changes. Every compatible livepatch has to support the following
scenarios:
- Modify the system state when the livepatch gets enabled and the state
has not been already modified by a livepatches that are being
replaced.
- Take over or update the system state modification when is has already
been done by a livepatch that is being replaced.
- Restore the original state when the livepatch is disabled.
- Restore the previous state when the transition is reverted.
It might be the original system state or the state modification
done by livepatches that were being replaced.
- Remove any already made changes when error occurs and the livepatch
cannot get enabled.
4. Expected usage
=================
System states are usually modified by livepatch callbacks. The expected
role of each callback is as follows:
*pre_patch()*
- Allocate *state->data* when necessary. The allocation might fail
and *pre_patch()* is the only callback that could stop loading
of the livepatch. The allocation is not needed when the data
are already provided by previously installed livepatches.
- Do any other preparatory action that is needed by
the new code even before the transition gets finished.
For example, initialize *state->data*.
The system state itself is typically modified in *post_patch()*
when the entire system is able to handle it.
- Clean up its own mess in case of error. It might be done by a custom
code or by calling *post_unpatch()* explicitly.
*post_patch()*
- Copy *state->data* from the previous livepatch when they are
compatible.
- Do the actual system state modification. Eventually allow
the new code to use it.
- Make sure that *state->data* has all necessary information.
- Free *state->data* from replaces livepatches when they are
not longer needed.
*pre_unpatch()*
- Prevent the code, added by the livepatch, relying on the system
state change.
- Revert the system state modification..
*post_unpatch()*
- Distinguish transition reverse and livepatch disabling by
checking *klp_get_prev_state()*.
- In case of transition reverse, restore the previous system
state. It might mean doing nothing.
- Remove any not longer needed setting or data.
.. note::
*pre_unpatch()* typically does symmetric operations to *post_patch()*.
Except that it is called only when the livepatch is being disabled.
Therefore it does not need to care about any previously installed
livepatch.
*post_unpatch()* typically does symmetric operations to *pre_patch()*.
It might be called also during the transition reverse. Therefore it
has to handle the state of the previously installed livepatches.
include/linux/livepatch.h
View file @
0e672adc
...
...
@@ -130,10 +130,23 @@ struct klp_object {
bool
patched
;
};
/**
* struct klp_state - state of the system modified by the livepatch
* @id: system state identifier (non-zero)
* @version: version of the change
* @data: custom data
*/
struct
klp_state
{
unsigned
long
id
;
unsigned
int
version
;
void
*
data
;
};
/**
* struct klp_patch - patch structure for live patching
* @mod: reference to the live patch module
* @objs: object entries for kernel objects to be patched
* @states: system states that can get modified
* @replace: replace all actively used patches
* @list: list node for global list of actively used patches
* @kobj: kobject for sysfs resources
...
...
@@ -147,6 +160,7 @@ struct klp_patch {
/* external */
struct
module
*
mod
;
struct
klp_object
*
objs
;
struct
klp_state
*
states
;
bool
replace
;
/* internal */
...
...
@@ -217,6 +231,9 @@ void *klp_shadow_get_or_alloc(void *obj, unsigned long id,
void
klp_shadow_free
(
void
*
obj
,
unsigned
long
id
,
klp_shadow_dtor_t
dtor
);
void
klp_shadow_free_all
(
unsigned
long
id
,
klp_shadow_dtor_t
dtor
);
struct
klp_state
*
klp_get_state
(
struct
klp_patch
*
patch
,
unsigned
long
id
);
struct
klp_state
*
klp_get_prev_state
(
unsigned
long
id
);
#else
/* !CONFIG_LIVEPATCH */
static
inline
int
klp_module_coming
(
struct
module
*
mod
)
{
return
0
;
}
...
...
kernel/livepatch/Makefile
View file @
0e672adc
# 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
kernel/livepatch/core.c
View file @
0e672adc
...
...
@@ -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 @@ int klp_enable_patch(struct klp_patch *patch)
EXPORT_SYMBOL_GPL
(
klp_enable_patch
);
/*
* This function
removes replaced
patches.
* This function
unpatches objects from the replaced live
patches.
*
* 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
);
}
}
...
...
kernel/livepatch/core.h
View file @
0e672adc
...
...
@@ -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
)
...
...
kernel/livepatch/state.c
0 → 100644
View file @
0e672adc
// 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
;
}
kernel/livepatch/state.h
0 → 100644
View file @
0e672adc
/* 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 */
kernel/livepatch/transition.c
View file @
0e672adc
...
...
@@ -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
);
}
/*
...
...
lib/livepatch/Makefile
View file @
0e672adc
...
...
@@ -8,7 +8,10 @@ obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
test_klp_callbacks_busy.o
\
test_klp_callbacks_mod.o
\
test_klp_livepatch.o
\
test_klp_shadow_vars.o
test_klp_shadow_vars.o
\
test_klp_state.o
\
test_klp_state2.o
\
test_klp_state3.o
# Target modules to be livepatched require CC_FLAGS_FTRACE
CFLAGS_test_klp_callbacks_busy.o
+=
$(CC_FLAGS_FTRACE)
...
...
lib/livepatch/test_klp_state.c
0 → 100644
View file @
0e672adc
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2019 SUSE
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/livepatch.h>
#define CONSOLE_LOGLEVEL_STATE 1
/* Version 1 does not support migration. */
#define CONSOLE_LOGLEVEL_STATE_VERSION 1
static
const
char
*
const
module_state
[]
=
{
[
MODULE_STATE_LIVE
]
=
"[MODULE_STATE_LIVE] Normal state"
,
[
MODULE_STATE_COMING
]
=
"[MODULE_STATE_COMING] Full formed, running module_init"
,
[
MODULE_STATE_GOING
]
=
"[MODULE_STATE_GOING] Going away"
,
[
MODULE_STATE_UNFORMED
]
=
"[MODULE_STATE_UNFORMED] Still setting it up"
,
};
static
void
callback_info
(
const
char
*
callback
,
struct
klp_object
*
obj
)
{
if
(
obj
->
mod
)
pr_info
(
"%s: %s -> %s
\n
"
,
callback
,
obj
->
mod
->
name
,
module_state
[
obj
->
mod
->
state
]);
else
pr_info
(
"%s: vmlinux
\n
"
,
callback
);
}
static
struct
klp_patch
patch
;
static
int
allocate_loglevel_state
(
void
)
{
struct
klp_state
*
loglevel_state
;
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
-
EINVAL
;
loglevel_state
->
data
=
kzalloc
(
sizeof
(
console_loglevel
),
GFP_KERNEL
);
if
(
!
loglevel_state
->
data
)
return
-
ENOMEM
;
pr_info
(
"%s: allocating space to store console_loglevel
\n
"
,
__func__
);
return
0
;
}
static
void
fix_console_loglevel
(
void
)
{
struct
klp_state
*
loglevel_state
;
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
;
pr_info
(
"%s: fixing console_loglevel
\n
"
,
__func__
);
*
(
int
*
)
loglevel_state
->
data
=
console_loglevel
;
console_loglevel
=
CONSOLE_LOGLEVEL_MOTORMOUTH
;
}
static
void
restore_console_loglevel
(
void
)
{
struct
klp_state
*
loglevel_state
;
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
;
pr_info
(
"%s: restoring console_loglevel
\n
"
,
__func__
);
console_loglevel
=
*
(
int
*
)
loglevel_state
->
data
;
}
static
void
free_loglevel_state
(
void
)
{
struct
klp_state
*
loglevel_state
;
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
;
pr_info
(
"%s: freeing space for the stored console_loglevel
\n
"
,
__func__
);
kfree
(
loglevel_state
->
data
);
}
/* Executed on object patching (ie, patch enablement) */
static
int
pre_patch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
return
allocate_loglevel_state
();
}
/* Executed on object unpatching (ie, patch disablement) */
static
void
post_patch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
fix_console_loglevel
();
}
/* Executed on object unpatching (ie, patch disablement) */
static
void
pre_unpatch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
restore_console_loglevel
();
}
/* Executed on object unpatching (ie, patch disablement) */
static
void
post_unpatch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
free_loglevel_state
();
}
static
struct
klp_func
no_funcs
[]
=
{
{}
};
static
struct
klp_object
objs
[]
=
{
{
.
name
=
NULL
,
/* vmlinux */
.
funcs
=
no_funcs
,
.
callbacks
=
{
.
pre_patch
=
pre_patch_callback
,
.
post_patch
=
post_patch_callback
,
.
pre_unpatch
=
pre_unpatch_callback
,
.
post_unpatch
=
post_unpatch_callback
,
},
},
{
}
};
static
struct
klp_state
states
[]
=
{
{
.
id
=
CONSOLE_LOGLEVEL_STATE
,
.
version
=
CONSOLE_LOGLEVEL_STATE_VERSION
,
},
{
}
};
static
struct
klp_patch
patch
=
{
.
mod
=
THIS_MODULE
,
.
objs
=
objs
,
.
states
=
states
,
.
replace
=
true
,
};
static
int
test_klp_callbacks_demo_init
(
void
)
{
return
klp_enable_patch
(
&
patch
);
}
static
void
test_klp_callbacks_demo_exit
(
void
)
{
}
module_init
(
test_klp_callbacks_demo_init
);
module_exit
(
test_klp_callbacks_demo_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_INFO
(
livepatch
,
"Y"
);
MODULE_AUTHOR
(
"Petr Mladek <pmladek@suse.com>"
);
MODULE_DESCRIPTION
(
"Livepatch test: system state modification"
);
lib/livepatch/test_klp_state2.c
0 → 100644
View file @
0e672adc
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2019 SUSE
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/printk.h>
#include <linux/livepatch.h>
#define CONSOLE_LOGLEVEL_STATE 1
/* Version 2 supports migration. */
#define CONSOLE_LOGLEVEL_STATE_VERSION 2
static
const
char
*
const
module_state
[]
=
{
[
MODULE_STATE_LIVE
]
=
"[MODULE_STATE_LIVE] Normal state"
,
[
MODULE_STATE_COMING
]
=
"[MODULE_STATE_COMING] Full formed, running module_init"
,
[
MODULE_STATE_GOING
]
=
"[MODULE_STATE_GOING] Going away"
,
[
MODULE_STATE_UNFORMED
]
=
"[MODULE_STATE_UNFORMED] Still setting it up"
,
};
static
void
callback_info
(
const
char
*
callback
,
struct
klp_object
*
obj
)
{
if
(
obj
->
mod
)
pr_info
(
"%s: %s -> %s
\n
"
,
callback
,
obj
->
mod
->
name
,
module_state
[
obj
->
mod
->
state
]);
else
pr_info
(
"%s: vmlinux
\n
"
,
callback
);
}
static
struct
klp_patch
patch
;
static
int
allocate_loglevel_state
(
void
)
{
struct
klp_state
*
loglevel_state
,
*
prev_loglevel_state
;
prev_loglevel_state
=
klp_get_prev_state
(
CONSOLE_LOGLEVEL_STATE
);
if
(
prev_loglevel_state
)
{
pr_info
(
"%s: space to store console_loglevel already allocated
\n
"
,
__func__
);
return
0
;
}
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
-
EINVAL
;
loglevel_state
->
data
=
kzalloc
(
sizeof
(
console_loglevel
),
GFP_KERNEL
);
if
(
!
loglevel_state
->
data
)
return
-
ENOMEM
;
pr_info
(
"%s: allocating space to store console_loglevel
\n
"
,
__func__
);
return
0
;
}
static
void
fix_console_loglevel
(
void
)
{
struct
klp_state
*
loglevel_state
,
*
prev_loglevel_state
;
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
;
prev_loglevel_state
=
klp_get_prev_state
(
CONSOLE_LOGLEVEL_STATE
);
if
(
prev_loglevel_state
)
{
pr_info
(
"%s: taking over the console_loglevel change
\n
"
,
__func__
);
loglevel_state
->
data
=
prev_loglevel_state
->
data
;
return
;
}
pr_info
(
"%s: fixing console_loglevel
\n
"
,
__func__
);
*
(
int
*
)
loglevel_state
->
data
=
console_loglevel
;
console_loglevel
=
CONSOLE_LOGLEVEL_MOTORMOUTH
;
}
static
void
restore_console_loglevel
(
void
)
{
struct
klp_state
*
loglevel_state
,
*
prev_loglevel_state
;
prev_loglevel_state
=
klp_get_prev_state
(
CONSOLE_LOGLEVEL_STATE
);
if
(
prev_loglevel_state
)
{
pr_info
(
"%s: passing the console_loglevel change back to the old livepatch
\n
"
,
__func__
);
return
;
}
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
;
pr_info
(
"%s: restoring console_loglevel
\n
"
,
__func__
);
console_loglevel
=
*
(
int
*
)
loglevel_state
->
data
;
}
static
void
free_loglevel_state
(
void
)
{
struct
klp_state
*
loglevel_state
,
*
prev_loglevel_state
;
prev_loglevel_state
=
klp_get_prev_state
(
CONSOLE_LOGLEVEL_STATE
);
if
(
prev_loglevel_state
)
{
pr_info
(
"%s: keeping space to store console_loglevel
\n
"
,
__func__
);
return
;
}
loglevel_state
=
klp_get_state
(
&
patch
,
CONSOLE_LOGLEVEL_STATE
);
if
(
!
loglevel_state
)
return
;
pr_info
(
"%s: freeing space for the stored console_loglevel
\n
"
,
__func__
);
kfree
(
loglevel_state
->
data
);
}
/* Executed on object patching (ie, patch enablement) */
static
int
pre_patch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
return
allocate_loglevel_state
();
}
/* Executed on object unpatching (ie, patch disablement) */
static
void
post_patch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
fix_console_loglevel
();
}
/* Executed on object unpatching (ie, patch disablement) */
static
void
pre_unpatch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
restore_console_loglevel
();
}
/* Executed on object unpatching (ie, patch disablement) */
static
void
post_unpatch_callback
(
struct
klp_object
*
obj
)
{
callback_info
(
__func__
,
obj
);
free_loglevel_state
();
}
static
struct
klp_func
no_funcs
[]
=
{
{}
};
static
struct
klp_object
objs
[]
=
{
{
.
name
=
NULL
,
/* vmlinux */
.
funcs
=
no_funcs
,
.
callbacks
=
{
.
pre_patch
=
pre_patch_callback
,
.
post_patch
=
post_patch_callback
,
.
pre_unpatch
=
pre_unpatch_callback
,
.
post_unpatch
=
post_unpatch_callback
,
},
},
{
}
};
static
struct
klp_state
states
[]
=
{
{
.
id
=
CONSOLE_LOGLEVEL_STATE
,
.
version
=
CONSOLE_LOGLEVEL_STATE_VERSION
,
},
{
}
};
static
struct
klp_patch
patch
=
{
.
mod
=
THIS_MODULE
,
.
objs
=
objs
,
.
states
=
states
,
.
replace
=
true
,
};
static
int
test_klp_callbacks_demo_init
(
void
)
{
return
klp_enable_patch
(
&
patch
);
}
static
void
test_klp_callbacks_demo_exit
(
void
)
{
}
module_init
(
test_klp_callbacks_demo_init
);
module_exit
(
test_klp_callbacks_demo_exit
);
MODULE_LICENSE
(
"GPL"
);
MODULE_INFO
(
livepatch
,
"Y"
);
MODULE_AUTHOR
(
"Petr Mladek <pmladek@suse.com>"
);
MODULE_DESCRIPTION
(
"Livepatch test: system state modification"
);
lib/livepatch/test_klp_state3.c
0 → 100644
View file @
0e672adc
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2019 SUSE
/* The console loglevel fix is the same in the next cumulative patch. */
#include "test_klp_state2.c"
tools/testing/selftests/livepatch/Makefile
View file @
0e672adc
...
...
@@ -4,6 +4,7 @@ TEST_PROGS_EXTENDED := functions.sh
TEST_PROGS
:=
\
test-livepatch.sh
\
test-callbacks.sh
\
test-shadow-vars.sh
test-shadow-vars.sh
\
test-state.sh
include
../lib.mk
tools/testing/selftests/livepatch/test-state.sh
0 → 100755
View file @
0e672adc
#!/bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (C) 2019 SUSE
.
$(
dirname
$0
)
/functions.sh
MOD_LIVEPATCH
=
test_klp_state
MOD_LIVEPATCH2
=
test_klp_state2
MOD_LIVEPATCH3
=
test_klp_state3
set_dynamic_debug
# TEST: Loading and removing a module that modifies the system state
echo
-n
"TEST: system state modification ... "
dmesg
-C
load_lp
$MOD_LIVEPATCH
disable_lp
$MOD_LIVEPATCH
unload_lp
$MOD_LIVEPATCH
check_result
"% modprobe
$MOD_LIVEPATCH
livepatch: enabling patch '
$MOD_LIVEPATCH
'
livepatch: '
$MOD_LIVEPATCH
': initializing patching transition
$MOD_LIVEPATCH
: pre_patch_callback: vmlinux
$MOD_LIVEPATCH
: allocate_loglevel_state: allocating space to store console_loglevel
livepatch: '
$MOD_LIVEPATCH
': starting patching transition
livepatch: '
$MOD_LIVEPATCH
': completing patching transition
$MOD_LIVEPATCH
: post_patch_callback: vmlinux
$MOD_LIVEPATCH
: fix_console_loglevel: fixing console_loglevel
livepatch: '
$MOD_LIVEPATCH
': patching complete
% echo 0 > /sys/kernel/livepatch/
$MOD_LIVEPATCH
/enabled
livepatch: '
$MOD_LIVEPATCH
': initializing unpatching transition
$MOD_LIVEPATCH
: pre_unpatch_callback: vmlinux
$MOD_LIVEPATCH
: restore_console_loglevel: restoring console_loglevel
livepatch: '
$MOD_LIVEPATCH
': starting unpatching transition
livepatch: '
$MOD_LIVEPATCH
': completing unpatching transition
$MOD_LIVEPATCH
: post_unpatch_callback: vmlinux
$MOD_LIVEPATCH
: free_loglevel_state: freeing space for the stored console_loglevel
livepatch: '
$MOD_LIVEPATCH
': unpatching complete
% rmmod
$MOD_LIVEPATCH
"
# TEST: Take over system state change by a cumulative patch
echo
-n
"TEST: taking over system state modification ... "
dmesg
-C
load_lp
$MOD_LIVEPATCH
load_lp
$MOD_LIVEPATCH2
unload_lp
$MOD_LIVEPATCH
disable_lp
$MOD_LIVEPATCH2
unload_lp
$MOD_LIVEPATCH2
check_result
"% modprobe
$MOD_LIVEPATCH
livepatch: enabling patch '
$MOD_LIVEPATCH
'
livepatch: '
$MOD_LIVEPATCH
': initializing patching transition
$MOD_LIVEPATCH
: pre_patch_callback: vmlinux
$MOD_LIVEPATCH
: allocate_loglevel_state: allocating space to store console_loglevel
livepatch: '
$MOD_LIVEPATCH
': starting patching transition
livepatch: '
$MOD_LIVEPATCH
': completing patching transition
$MOD_LIVEPATCH
: post_patch_callback: vmlinux
$MOD_LIVEPATCH
: fix_console_loglevel: fixing console_loglevel
livepatch: '
$MOD_LIVEPATCH
': patching complete
% modprobe
$MOD_LIVEPATCH2
livepatch: enabling patch '
$MOD_LIVEPATCH2
'
livepatch: '
$MOD_LIVEPATCH2
': initializing patching transition
$MOD_LIVEPATCH2
: pre_patch_callback: vmlinux
$MOD_LIVEPATCH2
: allocate_loglevel_state: space to store console_loglevel already allocated
livepatch: '
$MOD_LIVEPATCH2
': starting patching transition
livepatch: '
$MOD_LIVEPATCH2
': completing patching transition
$MOD_LIVEPATCH2
: post_patch_callback: vmlinux
$MOD_LIVEPATCH2
: fix_console_loglevel: taking over the console_loglevel change
livepatch: '
$MOD_LIVEPATCH2
': patching complete
% rmmod
$MOD_LIVEPATCH
% echo 0 > /sys/kernel/livepatch/
$MOD_LIVEPATCH2
/enabled
livepatch: '
$MOD_LIVEPATCH2
': initializing unpatching transition
$MOD_LIVEPATCH2
: pre_unpatch_callback: vmlinux
$MOD_LIVEPATCH2
: restore_console_loglevel: restoring console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': starting unpatching transition
livepatch: '
$MOD_LIVEPATCH2
': completing unpatching transition
$MOD_LIVEPATCH2
: post_unpatch_callback: vmlinux
$MOD_LIVEPATCH2
: free_loglevel_state: freeing space for the stored console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': unpatching complete
% rmmod
$MOD_LIVEPATCH2
"
# TEST: Take over system state change by a cumulative patch
echo
-n
"TEST: compatible cumulative livepatches ... "
dmesg
-C
load_lp
$MOD_LIVEPATCH2
load_lp
$MOD_LIVEPATCH3
unload_lp
$MOD_LIVEPATCH2
load_lp
$MOD_LIVEPATCH2
disable_lp
$MOD_LIVEPATCH2
unload_lp
$MOD_LIVEPATCH2
unload_lp
$MOD_LIVEPATCH3
check_result
"% modprobe
$MOD_LIVEPATCH2
livepatch: enabling patch '
$MOD_LIVEPATCH2
'
livepatch: '
$MOD_LIVEPATCH2
': initializing patching transition
$MOD_LIVEPATCH2
: pre_patch_callback: vmlinux
$MOD_LIVEPATCH2
: allocate_loglevel_state: allocating space to store console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': starting patching transition
livepatch: '
$MOD_LIVEPATCH2
': completing patching transition
$MOD_LIVEPATCH2
: post_patch_callback: vmlinux
$MOD_LIVEPATCH2
: fix_console_loglevel: fixing console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': patching complete
% modprobe
$MOD_LIVEPATCH3
livepatch: enabling patch '
$MOD_LIVEPATCH3
'
livepatch: '
$MOD_LIVEPATCH3
': initializing patching transition
$MOD_LIVEPATCH3
: pre_patch_callback: vmlinux
$MOD_LIVEPATCH3
: allocate_loglevel_state: space to store console_loglevel already allocated
livepatch: '
$MOD_LIVEPATCH3
': starting patching transition
livepatch: '
$MOD_LIVEPATCH3
': completing patching transition
$MOD_LIVEPATCH3
: post_patch_callback: vmlinux
$MOD_LIVEPATCH3
: fix_console_loglevel: taking over the console_loglevel change
livepatch: '
$MOD_LIVEPATCH3
': patching complete
% rmmod
$MOD_LIVEPATCH2
% modprobe
$MOD_LIVEPATCH2
livepatch: enabling patch '
$MOD_LIVEPATCH2
'
livepatch: '
$MOD_LIVEPATCH2
': initializing patching transition
$MOD_LIVEPATCH2
: pre_patch_callback: vmlinux
$MOD_LIVEPATCH2
: allocate_loglevel_state: space to store console_loglevel already allocated
livepatch: '
$MOD_LIVEPATCH2
': starting patching transition
livepatch: '
$MOD_LIVEPATCH2
': completing patching transition
$MOD_LIVEPATCH2
: post_patch_callback: vmlinux
$MOD_LIVEPATCH2
: fix_console_loglevel: taking over the console_loglevel change
livepatch: '
$MOD_LIVEPATCH2
': patching complete
% echo 0 > /sys/kernel/livepatch/
$MOD_LIVEPATCH2
/enabled
livepatch: '
$MOD_LIVEPATCH2
': initializing unpatching transition
$MOD_LIVEPATCH2
: pre_unpatch_callback: vmlinux
$MOD_LIVEPATCH2
: restore_console_loglevel: restoring console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': starting unpatching transition
livepatch: '
$MOD_LIVEPATCH2
': completing unpatching transition
$MOD_LIVEPATCH2
: post_unpatch_callback: vmlinux
$MOD_LIVEPATCH2
: free_loglevel_state: freeing space for the stored console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': unpatching complete
% rmmod
$MOD_LIVEPATCH2
% rmmod
$MOD_LIVEPATCH3
"
# TEST: Failure caused by incompatible cumulative livepatches
echo
-n
"TEST: incompatible cumulative livepatches ... "
dmesg
-C
load_lp
$MOD_LIVEPATCH2
load_failing_mod
$MOD_LIVEPATCH
disable_lp
$MOD_LIVEPATCH2
unload_lp
$MOD_LIVEPATCH2
check_result
"% modprobe
$MOD_LIVEPATCH2
livepatch: enabling patch '
$MOD_LIVEPATCH2
'
livepatch: '
$MOD_LIVEPATCH2
': initializing patching transition
$MOD_LIVEPATCH2
: pre_patch_callback: vmlinux
$MOD_LIVEPATCH2
: allocate_loglevel_state: allocating space to store console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': starting patching transition
livepatch: '
$MOD_LIVEPATCH2
': completing patching transition
$MOD_LIVEPATCH2
: post_patch_callback: vmlinux
$MOD_LIVEPATCH2
: fix_console_loglevel: fixing console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': patching complete
% modprobe
$MOD_LIVEPATCH
livepatch: Livepatch patch (
$MOD_LIVEPATCH
) is not compatible with the already installed livepatches.
modprobe: ERROR: could not insert '
$MOD_LIVEPATCH
': Invalid argument
% echo 0 > /sys/kernel/livepatch/
$MOD_LIVEPATCH2
/enabled
livepatch: '
$MOD_LIVEPATCH2
': initializing unpatching transition
$MOD_LIVEPATCH2
: pre_unpatch_callback: vmlinux
$MOD_LIVEPATCH2
: restore_console_loglevel: restoring console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': starting unpatching transition
livepatch: '
$MOD_LIVEPATCH2
': completing unpatching transition
$MOD_LIVEPATCH2
: post_unpatch_callback: vmlinux
$MOD_LIVEPATCH2
: free_loglevel_state: freeing space for the stored console_loglevel
livepatch: '
$MOD_LIVEPATCH2
': unpatching complete
% rmmod
$MOD_LIVEPATCH2
"
exit
0
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment