diff options
| author | Joe Lawrence <joe.lawrence@redhat.com> | 2017-10-02 11:56:48 -0400 |
|---|---|---|
| committer | Jiri Kosina <jkosina@suse.cz> | 2017-10-11 09:38:46 -0400 |
| commit | ef8daf8eeb5b8ab6bc356656163d19f20fb827ed (patch) | |
| tree | 2ae9e01115e5aa8adfe4aa3870e0cf0c156d419f /kernel | |
| parent | dcba71086e0d1abf4f00cd381530b11d0db7fa1d (diff) | |
livepatch: unpatch all klp_objects if klp_module_coming fails
When an incoming module is considered for livepatching by
klp_module_coming(), it iterates over multiple patches and multiple
kernel objects in this order:
list_for_each_entry(patch, &klp_patches, list) {
klp_for_each_object(patch, obj) {
which means that if one of the kernel objects fails to patch,
klp_module_coming()'s error path needs to unpatch and cleanup any kernel
objects that were already patched by a previous patch.
Reported-by: Miroslav Benes <mbenes@suse.cz>
Suggested-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
Reviewed-by: Petr Mladek <pmladek@suse.com>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/livepatch/core.c | 60 |
1 files changed, 37 insertions, 23 deletions
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c index b9628e43c78f..bf8c8fd72589 100644 --- a/kernel/livepatch/core.c +++ b/kernel/livepatch/core.c | |||
| @@ -830,6 +830,41 @@ int klp_register_patch(struct klp_patch *patch) | |||
| 830 | } | 830 | } |
| 831 | EXPORT_SYMBOL_GPL(klp_register_patch); | 831 | EXPORT_SYMBOL_GPL(klp_register_patch); |
| 832 | 832 | ||
| 833 | /* | ||
| 834 | * Remove parts of patches that touch a given kernel module. The list of | ||
| 835 | * patches processed might be limited. When limit is NULL, all patches | ||
| 836 | * will be handled. | ||
| 837 | */ | ||
| 838 | static void klp_cleanup_module_patches_limited(struct module *mod, | ||
| 839 | struct klp_patch *limit) | ||
| 840 | { | ||
| 841 | struct klp_patch *patch; | ||
| 842 | struct klp_object *obj; | ||
| 843 | |||
| 844 | list_for_each_entry(patch, &klp_patches, list) { | ||
| 845 | if (patch == limit) | ||
| 846 | break; | ||
| 847 | |||
| 848 | klp_for_each_object(patch, obj) { | ||
| 849 | if (!klp_is_module(obj) || strcmp(obj->name, mod->name)) | ||
| 850 | continue; | ||
| 851 | |||
| 852 | /* | ||
| 853 | * Only unpatch the module if the patch is enabled or | ||
| 854 | * is in transition. | ||
| 855 | */ | ||
| 856 | if (patch->enabled || patch == klp_transition_patch) { | ||
| 857 | pr_notice("reverting patch '%s' on unloading module '%s'\n", | ||
| 858 | patch->mod->name, obj->mod->name); | ||
| 859 | klp_unpatch_object(obj); | ||
| 860 | } | ||
| 861 | |||
| 862 | klp_free_object_loaded(obj); | ||
| 863 | break; | ||
| 864 | } | ||
| 865 | } | ||
| 866 | } | ||
| 867 | |||
| 833 | int klp_module_coming(struct module *mod) | 868 | int klp_module_coming(struct module *mod) |
| 834 | { | 869 | { |
| 835 | int ret; | 870 | int ret; |
| @@ -894,7 +929,7 @@ err: | |||
| 894 | pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n", | 929 | pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n", |
| 895 | patch->mod->name, obj->mod->name, obj->mod->name); | 930 | patch->mod->name, obj->mod->name, obj->mod->name); |
| 896 | mod->klp_alive = false; | 931 | mod->klp_alive = false; |
| 897 | klp_free_object_loaded(obj); | 932 | klp_cleanup_module_patches_limited(mod, patch); |
| 898 | mutex_unlock(&klp_mutex); | 933 | mutex_unlock(&klp_mutex); |
| 899 | 934 | ||
| 900 | return ret; | 935 | return ret; |
| @@ -902,9 +937,6 @@ err: | |||
| 902 | 937 | ||
| 903 | void klp_module_going(struct module *mod) | 938 | void klp_module_going(struct module *mod) |
| 904 | { | 939 | { |
| 905 | struct klp_patch *patch; | ||
| 906 | struct klp_object *obj; | ||
| 907 | |||
| 908 | if (WARN_ON(mod->state != MODULE_STATE_GOING && | 940 | if (WARN_ON(mod->state != MODULE_STATE_GOING && |
| 909 | mod->state != MODULE_STATE_COMING)) | 941 | mod->state != MODULE_STATE_COMING)) |
| 910 | return; | 942 | return; |
| @@ -917,25 +949,7 @@ void klp_module_going(struct module *mod) | |||
| 917 | */ | 949 | */ |
| 918 | mod->klp_alive = false; | 950 | mod->klp_alive = false; |
| 919 | 951 | ||
| 920 | list_for_each_entry(patch, &klp_patches, list) { | 952 | klp_cleanup_module_patches_limited(mod, NULL); |
| 921 | klp_for_each_object(patch, obj) { | ||
| 922 | if (!klp_is_module(obj) || strcmp(obj->name, mod->name)) | ||
| 923 | continue; | ||
| 924 | |||
| 925 | /* | ||
| 926 | * Only unpatch the module if the patch is enabled or | ||
| 927 | * is in transition. | ||
| 928 | */ | ||
| 929 | if (patch->enabled || patch == klp_transition_patch) { | ||
| 930 | pr_notice("reverting patch '%s' on unloading module '%s'\n", | ||
| 931 | patch->mod->name, obj->mod->name); | ||
| 932 | klp_unpatch_object(obj); | ||
| 933 | } | ||
| 934 | |||
| 935 | klp_free_object_loaded(obj); | ||
| 936 | break; | ||
| 937 | } | ||
| 938 | } | ||
| 939 | 953 | ||
| 940 | mutex_unlock(&klp_mutex); | 954 | mutex_unlock(&klp_mutex); |
| 941 | } | 955 | } |
