Skip to content

Commit 7e35e4e

Browse files
committed
livepatch: Keep replaced patches until post_patch callback is called
Pre/post (un)patch callbacks might manipulate the system state. Cumulative livepatches might need to take over the changes made by the replaced ones. For this they might need to access some data stored or referenced by the old livepatches. Therefore the replaced livepatches have to stay around until post_patch() callback is called. It is achieved by calling the free functions later. It is the same location where disabled livepatches have already been freed. Link: http://lkml.kernel.org/r/20191030154313.13263-2-pmladek@suse.com To: Jiri Kosina <jikos@kernel.org> Cc: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com> Cc: Nicolai Stange <nstange@suse.de> Cc: live-patching@vger.kernel.org Cc: linux-kernel@vger.kernel.org Acked-by: Miroslav Benes <mbenes@suse.cz> Acked-by: Joe Lawrence <joe.lawrence@redhat.com> Acked-by: Josh Poimboeuf <jpoimboe@redhat.com> Signed-off-by: Petr Mladek <pmladek@suse.com>
1 parent 9f7582d commit 7e35e4e

3 files changed

Lines changed: 35 additions & 18 deletions

File tree

kernel/livepatch/core.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ static void klp_free_objects_dynamic(struct klp_patch *patch)
632632
* The operation must be completed by calling klp_free_patch_finish()
633633
* outside klp_mutex.
634634
*/
635-
void klp_free_patch_start(struct klp_patch *patch)
635+
static void klp_free_patch_start(struct klp_patch *patch)
636636
{
637637
if (!list_empty(&patch->list))
638638
list_del(&patch->list);
@@ -677,6 +677,23 @@ static void klp_free_patch_work_fn(struct work_struct *work)
677677
klp_free_patch_finish(patch);
678678
}
679679

680+
void klp_free_patch_async(struct klp_patch *patch)
681+
{
682+
klp_free_patch_start(patch);
683+
schedule_work(&patch->free_work);
684+
}
685+
686+
void klp_free_replaced_patches_async(struct klp_patch *new_patch)
687+
{
688+
struct klp_patch *old_patch, *tmp_patch;
689+
690+
klp_for_each_patch_safe(old_patch, tmp_patch) {
691+
if (old_patch == new_patch)
692+
return;
693+
klp_free_patch_async(old_patch);
694+
}
695+
}
696+
680697
static int klp_init_func(struct klp_object *obj, struct klp_func *func)
681698
{
682699
if (!func->old_name)
@@ -1022,31 +1039,30 @@ int klp_enable_patch(struct klp_patch *patch)
10221039
EXPORT_SYMBOL_GPL(klp_enable_patch);
10231040

10241041
/*
1025-
* This function removes replaced patches.
1042+
* This function unpatches objects from the replaced livepatches.
10261043
*
10271044
* We could be pretty aggressive here. It is called in the situation where
1028-
* these structures are no longer accessible. All functions are redirected
1029-
* by the klp_transition_patch. They use either a new code or they are in
1030-
* the original code because of the special nop function patches.
1045+
* these structures are no longer accessed from the ftrace handler.
1046+
* All functions are redirected by the klp_transition_patch. They
1047+
* use either a new code or they are in the original code because
1048+
* of the special nop function patches.
10311049
*
10321050
* The only exception is when the transition was forced. In this case,
10331051
* klp_ftrace_handler() might still see the replaced patch on the stack.
10341052
* Fortunately, it is carefully designed to work with removed functions
10351053
* thanks to RCU. We only have to keep the patches on the system. Also
10361054
* this is handled transparently by patch->module_put.
10371055
*/
1038-
void klp_discard_replaced_patches(struct klp_patch *new_patch)
1056+
void klp_unpatch_replaced_patches(struct klp_patch *new_patch)
10391057
{
1040-
struct klp_patch *old_patch, *tmp_patch;
1058+
struct klp_patch *old_patch;
10411059

1042-
klp_for_each_patch_safe(old_patch, tmp_patch) {
1060+
klp_for_each_patch(old_patch) {
10431061
if (old_patch == new_patch)
10441062
return;
10451063

10461064
old_patch->enabled = false;
10471065
klp_unpatch_objects(old_patch);
1048-
klp_free_patch_start(old_patch);
1049-
schedule_work(&old_patch->free_work);
10501066
}
10511067
}
10521068

kernel/livepatch/core.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ extern struct list_head klp_patches;
1313
#define klp_for_each_patch(patch) \
1414
list_for_each_entry(patch, &klp_patches, list)
1515

16-
void klp_free_patch_start(struct klp_patch *patch);
17-
void klp_discard_replaced_patches(struct klp_patch *new_patch);
16+
void klp_free_patch_async(struct klp_patch *patch);
17+
void klp_free_replaced_patches_async(struct klp_patch *new_patch);
18+
void klp_unpatch_replaced_patches(struct klp_patch *new_patch);
1819
void klp_discard_nops(struct klp_patch *new_patch);
1920

2021
static inline bool klp_is_object_loaded(struct klp_object *obj)

kernel/livepatch/transition.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ static void klp_complete_transition(void)
7878
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
7979

8080
if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
81-
klp_discard_replaced_patches(klp_transition_patch);
81+
klp_unpatch_replaced_patches(klp_transition_patch);
8282
klp_discard_nops(klp_transition_patch);
8383
}
8484

@@ -446,14 +446,14 @@ void klp_try_complete_transition(void)
446446
klp_complete_transition();
447447

448448
/*
449-
* It would make more sense to free the patch in
449+
* It would make more sense to free the unused patches in
450450
* klp_complete_transition() but it is called also
451451
* from klp_cancel_transition().
452452
*/
453-
if (!patch->enabled) {
454-
klp_free_patch_start(patch);
455-
schedule_work(&patch->free_work);
456-
}
453+
if (!patch->enabled)
454+
klp_free_patch_async(patch);
455+
else if (patch->replace)
456+
klp_free_replaced_patches_async(patch);
457457
}
458458

459459
/*

0 commit comments

Comments
 (0)