From e91d2d2c6778275b16249f7e9d7439018fed70fe Mon Sep 17 00:00:00 2001 From: Zhipeng Xie Date: Fri, 2 Nov 2018 17:25:08 +0000 Subject: [PATCH 1009/1015] kmod/kpatch-build: support build patch for old kernels Forward compatible for old kernels Signed-off-by: Zhipeng Xie --- kmod/patch/kpatch-macros.h | 48 ++++++++++++++ kmod/patch/kpatch-patch.h | 4 + kmod/patch/kpatch.lds.S | 12 ++++ kmod/patch/livepatch-patch-hook.c | 124 +++++++++++++++++++++++++++++++++++++ kpatch-build/create-diff-object.c | 8 +++ 5 files changed, 196 insertions(+), 0 deletions(-) diff --git a/kmod/patch/kpatch-macros.h b/kmod/patch/kpatch-macros.h index a60a267..569a18d 100644 --- a/kmod/patch/kpatch-macros.h +++ b/kmod/patch/kpatch-macros.h @@ -133,4 +133,52 @@ struct kpatch_post_unpatch_callback { printk(_fmt, ## __VA_ARGS__); \ }) +typedef void (*kpatch_loadcall_t)(void); +typedef void (*kpatch_unloadcall_t)(void); + +struct kpatch_load { + kpatch_loadcall_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +struct kpatch_unload { + kpatch_unloadcall_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +/* + * KPATCH_LOAD_HOOK macro + * + * The first line only ensures that the hook being registered has the required + * function signature. If not, there is compile error on this line. + * + * The section line declares a struct kpatch_load to be allocated in a new + * .kpatch.hook.load section. This kpatch_load_data symbol is later stripped + * by create-diff-object so that it can be declared in multiple objects that + * are later linked together, avoiding global symbol collision. Since multiple + * hooks can be registered, the .kpatch.hook.load section is a table of struct + * kpatch_load elements that will be executed in series by the kpatch core + * module at load time, assuming the kernel object (module) is currently + * loaded; otherwise, the hook is called when module to be patched is loaded + * via the module load notifier. + */ +#define KPATCH_LOAD_HOOK(_fn) \ + static inline kpatch_loadcall_t __loadtest(void) { return _fn; } \ + struct kpatch_load kpatch_load_data __section(.kpatch.hooks.load) = { \ + .fn = _fn, \ + .objname = NULL \ + }; + +/* + * KPATCH_UNLOAD_HOOK macro + * + * Same as LOAD hook with s/load/unload/ + */ +#define KPATCH_UNLOAD_HOOK(_fn) \ + static inline kpatch_unloadcall_t __unloadtest(void) { return _fn; } \ + struct kpatch_unload kpatch_unload_data __section(.kpatch.hooks.unload) = { \ + .fn = _fn, \ + .objname = NULL \ + }; + #endif /* __KPATCH_MACROS_H_ */ diff --git a/kmod/patch/kpatch-patch.h b/kmod/patch/kpatch-patch.h index 917ea32..4d47d30 100644 --- a/kmod/patch/kpatch-patch.h +++ b/kmod/patch/kpatch-patch.h @@ -59,5 +59,9 @@ struct kpatch_post_unpatch_callback { void (*callback)(void *obj); char *objname; }; +struct kpatch_patch_hook { + void (*hook)(void); + char *objname; +}; #endif /* _KPATCH_PATCH_H_ */ diff --git a/kmod/patch/kpatch.lds.S b/kmod/patch/kpatch.lds.S index bc5de82..e3c4e97 100644 --- a/kmod/patch/kpatch.lds.S +++ b/kmod/patch/kpatch.lds.S @@ -47,4 +47,16 @@ SECTIONS __kpatch_force_funcs_end = . ; QUAD(0); } + .kpatch.hooks.load : { + __kpatch_hooks_load = . ; + *(.kpatch.hooks.load) + __kpatch_hooks_load_end = . ; + QUAD(0); + } + .kpatch.hooks.unload : { + __kpatch_hooks_unload = . ; + *(.kpatch.hooks.unload) + __kpatch_hooks_unload_end = . ; + QUAD(0); + } } diff --git a/kmod/patch/livepatch-patch-hook.c b/kmod/patch/livepatch-patch-hook.c index 625ffbd..8f7d044 100644 --- a/kmod/patch/livepatch-patch-hook.c +++ b/kmod/patch/livepatch-patch-hook.c @@ -64,6 +64,15 @@ # define HAVE_CALLBACKS #endif +#ifdef EULER_RELEASE_CODE +# if EULER_RELEASE_CODE <= EULER_RELEASE_VERSION(2, 3) +# undef HAVE_SYMPOS +# undef HAVE_IMMEDIATE +# undef HAVE_ELF_RELOCS +# define HAVE_LOADHOOKS +# endif +#endif + /* * There are quite a few similar structures at play in this file: * - livepatch.h structs prefixed with klp_* @@ -95,6 +104,11 @@ struct patch_object { #endif const char *name; int funcs_nr, relocs_nr; +#ifdef HAVE_LOADHOOKS + struct list_head hooks_load; + struct list_head hooks_unload; + int hooks_load_nr, hooks_unload_nr; +#endif }; struct patch_func { @@ -107,6 +121,13 @@ struct patch_reloc { struct kpatch_patch_dynrela *kdynrela; }; +#ifdef HAVE_LOADHOOKS +struct patch_hook { + struct list_head list; + struct kpatch_patch_hook *khook; +}; +#endif + static struct patch_object *patch_alloc_new_object(const char *name) { struct patch_object *object; @@ -118,6 +139,10 @@ static struct patch_object *patch_alloc_new_object(const char *name) #ifndef HAVE_ELF_RELOCS INIT_LIST_HEAD(&object->relocs); #endif +#ifdef HAVE_LOADHOOKS + INIT_LIST_HEAD(&object->hooks_load); + INIT_LIST_HEAD(&object->hooks_unload); +#endif if (strcmp(name, "vmlinux")) object->name = name; list_add(&object->list, &patch_objects); @@ -180,6 +205,37 @@ static int patch_add_reloc_to_object(struct kpatch_patch_dynrela *kdynrela) } #endif +#ifdef HAVE_LOADHOOKS +static int patch_add_hook_to_object(struct kpatch_patch_hook *khook, bool load) +{ + struct patch_hook *hook; + struct patch_object *object; + + hook = kzalloc(sizeof(*hook), GFP_KERNEL); + if (!hook) + return -ENOMEM; + INIT_LIST_HEAD(&hook->list); + hook->khook = khook; + + object = patch_find_object_by_name(khook->objname); + if (!object) { + kfree(hook); + return -ENOMEM; + } + + if (load) { + list_add(&hook->list, &object->hooks_load); + object->hooks_load_nr++; + } + else { + list_add(&hook->list, &object->hooks_unload); + object->hooks_unload_nr++; + } + + return 0; +} +#endif + static void patch_free_scaffold(void) { struct patch_func *func, *safefunc; struct patch_object *object, *safeobject; @@ -217,6 +273,12 @@ static void patch_free_livepatch(struct klp_patch *patch) if (object->relocs) kfree(object->relocs); #endif +#ifdef HAVE_LOADHOOKS + if (object->hooks_load) + kfree(object->hooks_load); + if (object->hooks_unload) + kfree(object->hooks_unload); +#endif } if (patch->objs) kfree(patch->objs); @@ -228,6 +290,10 @@ extern struct kpatch_pre_patch_callback __kpatch_callbacks_pre_patch[], __kpatch extern struct kpatch_post_patch_callback __kpatch_callbacks_post_patch[], __kpatch_callbacks_post_patch_end[]; extern struct kpatch_pre_unpatch_callback __kpatch_callbacks_pre_unpatch[], __kpatch_callbacks_pre_unpatch_end[]; extern struct kpatch_post_unpatch_callback __kpatch_callbacks_post_unpatch[], __kpatch_callbacks_post_unpatch_end[]; +#ifdef HAVE_LOADHOOKS +extern struct kpatch_patch_hook __kpatch_hooks_load[], __kpatch_hooks_load_end[]; +extern struct kpatch_patch_hook __kpatch_hooks_unload[], __kpatch_hooks_unload_end[]; +#endif extern unsigned long __kpatch_force_funcs[], __kpatch_force_funcs_end[]; static int patch_is_func_forced(unsigned long addr) @@ -347,6 +413,11 @@ static int __init patch_init(void) struct patch_reloc *reloc; struct klp_reloc *lrelocs, *lreloc; #endif +#ifdef HAVE_LOADHOOKS + struct patch_hook *hook; + struct kpatch_patch_hook *khook_load, *khook_unload; + struct klp_hook *lhooks_load, *lhooks_unload, *lhook; +#endif /* organize functions and relocs by object in scaffold */ for (kfunc = __kpatch_funcs; @@ -371,6 +442,24 @@ static int __init patch_init(void) if (ret) goto out; +#ifdef HAVE_LOADHOOKS + for (khook_load = __kpatch_hooks_load; + khook_load != __kpatch_hooks_load_end; + khook_load++) { + ret = patch_add_hook_to_object(khook_load, true); + if (ret) + goto out; + } + + for (khook_unload = __kpatch_hooks_unload; + khook_unload != __kpatch_hooks_unload_end; + khook_unload++) { + ret = patch_add_hook_to_object(khook_unload, false); + if (ret) + goto out; + } +#endif + /* past this point, only possible return code is -ENOMEM */ ret = -ENOMEM; @@ -403,12 +492,21 @@ static int __init patch_init(void) lfunc = &lfuncs[j]; lfunc->old_name = func->kfunc->name; lfunc->new_func = (void *)func->kfunc->new_addr; +#if defined(HAVE_IMMEDIATE) lfunc->immediate = patch_is_func_forced(lfunc->new_func); +#endif #ifdef HAVE_SYMPOS lfunc->old_sympos = func->kfunc->sympos; #else lfunc->old_addr = func->kfunc->old_addr; #endif +#ifdef EULER_RELEASE_CODE +# if EULER_RELEASE_CODE <= EULER_RELEASE_VERSION(2, 3) + lfunc->old_size = func->kfunc->old_size; + lfunc->new_size = func->kfunc->new_size; + lfunc->force = patch_is_func_forced(func->kfunc->new_addr); +# endif +#endif j++; } @@ -439,6 +537,32 @@ static int __init patch_init(void) lobject->callbacks = object->callbacks; #endif +#ifdef HAVE_LOADHOOKS + lhooks_load = kzalloc(sizeof(struct klp_hook) * + (object->hooks_load_nr+1), GFP_KERNEL); + if (!lhooks_load) + goto out; + lobject->hooks_load = lhooks_load; + j = 0; + list_for_each_entry(hook, &object->hooks_load, list) { + lhook = &lhooks_load[j]; + lhook->hook = hook->khook->hook; + j++; + } + + lhooks_unload = kzalloc(sizeof(struct klp_hook) * + (object->hooks_unload_nr+1), GFP_KERNEL); + if (!lhooks_unload) + goto out; + lobject->hooks_unload = lhooks_unload; + j = 0; + list_for_each_entry(hook, &object->hooks_unload, list) { + lhook = &lhooks_unload[j]; + lhook->hook = hook->khook->hook; + j++; + } +#endif + i++; } diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 349e483..ed96758 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1463,6 +1463,10 @@ static int kpatch_include_callback_elements(struct kpatch_elf *kelf) ".rela.kpatch.callbacks.post_patch", ".rela.kpatch.callbacks.pre_unpatch", ".rela.kpatch.callbacks.post_unpatch", + ".kpatch.hooks.load", + ".kpatch.hooks.unload", + ".rela.kpatch.hooks.load", + ".rela.kpatch.hooks.unload", NULL, }; char **callback_section; @@ -2921,6 +2925,10 @@ static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char * .offset = offsetof(struct kpatch_pre_unpatch_callback, objname) }, { .name = ".rela.kpatch.callbacks.post_unpatch", .offset = offsetof(struct kpatch_post_patch_callback, objname) }, + { .name = ".rela.kpatch.hooks.load", + .offset = offsetof(struct kpatch_patch_hook, objname) }, + { .name = ".rela.kpatch.hooks.unload", + .offset = offsetof(struct kpatch_patch_hook, objname) }, { .name = NULL, .offset = 0 }, }; struct callback *callbackp; -- 1.7.5.4