diff --git a/doc/patch-author-guide.md b/doc/patch-author-guide.md index e2c38fa23..fa9cb6887 100644 --- a/doc/patch-author-guide.md +++ b/doc/patch-author-guide.md @@ -105,53 +105,99 @@ Not only is this an easy solution, it's also safer than touching data since kpatch creates a barrier between the calling of old functions and new functions. -### Use a kpatch load hook +### Use a kpatch callback macro -If you need to change the contents of an existing variable in-place, you can -use the `KPATCH_LOAD_HOOK` macro to specify a function to be called when the -patch module is loaded. +Kpatch supports livepatch style callbacks, as described by the kernel's +[Documentation/livepatch/callbacks.txt](https://github.com/torvalds/linux/blob/master/Documentation/livepatch/callbacks.txt). -`kpatch-macros.h` provides `KPATCH_LOAD_HOOK` and `KPATCH_UNLOAD_HOOK` macros -to define such functions. The signature of both hook functions is `void -foo(void)`. Their execution context is as follows: +`kpatch-macros.h` defines the following macros that can be used to +register such callbacks: -* For patches to vmlinux or already loaded kernel modules, hook functions +* `KPATCH_PRE_PATCH_CALLBACK` - executed before patching +* `KPATCH_POST_PATCH_CALLBACK` - executed after patching +* `KPATCH_PRE_UNPATCH_CALLBACK` - executed before unpatching, complements the + post-patch callback. +* `KPATCH_POST_UNPATCH_CALLBACK` - executed after unpatching, complements the + pre-patch callback. + +A pre-patch callback routine has the following signature: + +``` +static int callback(patch_object *obj) { } +``` + +and any non-zero return status indicates failure to the kpatch core. For more +information on pre-patch callback failure, see the **Pre-patch return status** +section below. + +Post-patch, pre-unpatch, and post-unpatch callback routines all share the +following signature: + +``` +static void callback(patch_object *obj) { } +``` + +Generally pre-patch callbacks are paired with post-unpatch callbacks, meaning +that anything the former allocates or sets up should be torn down by the former +callback. Likewise for post-patch and pre-unpatch callbacks. + +#### Pre-patch return status + +If kpatch is currently patching already-loaded objects (vmlinux always by +definition as well as any currently loaded kernel modules), a non-zero pre-patch +callback status results in the kpatch core reverting the current +patch-in-progress. The kpatch-module is rejected, completely reverted, and +unloaded. + +If kpatch is patching a newly loaded kernel module, then a failing pre-patch +callback will only result in a WARN message. This is non-intuitive and a +deviation from livepatch callback behavior, but the result of a limitation of +kpatch and linux module notifiers. + +In both cases, if a pre-patch callback fails, none of its other callbacks will +be executed. + +#### Callback context + +* For patches to vmlinux or already loaded kernel modules, callback functions will be run by `stop_machine` as part of applying or removing a patch. -(Therefore the hooks must not block or sleep.) +(Therefore the callbacks must not block or sleep.) * For patches to kernel modules which haven't been loaded yet, a -module-notifier will execute load hooks when the associated module is loaded -into the `MODULE_STATE_COMING` state. The load hook is called before any -module_init code. +module-notifier will execute callbacks when the associated module is loaded +into the `MODULE_STATE_COMING` state. The pre and post-patch callbacks +are called before any module_init code. -Example: a kpatch fix for CVE-2016-5389 utilized the `KPATCH_LOAD_HOOK` and -`KPATCH_UNLOAD_HOOK` macros to modify variable `sysctl_tcp_challenge_ack_limit` -in-place: +Example: a kpatch fix for CVE-2016-5389 could utilize the +`KPATCH_PRE_PATCH_CALLBACK` and `KPATCH_POST_UNPATCH_CALLBACK` macros to modify +variable `sysctl_tcp_challenge_ack_limit` in-place: ``` ++#include "kpatch-macros.h" ++ +static bool kpatch_write = false; -+void kpatch_load_tcp_send_challenge_ack(void) ++static int kpatch_pre_patch_tcp_send_challenge_ack(patch_object *obj) +{ + if (sysctl_tcp_challenge_ack_limit == 100) { + sysctl_tcp_challenge_ack_limit = 1000; + kpatch_write = true; + } ++ return 0; +} -+void kpatch_unload_tcp_send_challenge_ack(void) +static void kpatch_post_unpatch_tcp_send_challenge_ack(patch_object *obj) +{ + if (kpatch_write && sysctl_tcp_challenge_ack_limit == 1000) + sysctl_tcp_challenge_ack_limit = 100; +} -+#include "kpatch-macros.h" -+KPATCH_LOAD_HOOK(kpatch_load_tcp_send_challenge_ack); -+KPATCH_UNLOAD_HOOK(kpatch_unload_tcp_send_challenge_ack); ++KPATCH_PRE_PATCH_CALLBACK(kpatch_pre_patch_tcp_send_challenge_ack); ++KPATCH_POST_UNPATCH_CALLBACK(kpatch_post_unpatch_tcp_send_challenge_ack); ``` Don't forget to protect access to the data as needed. -Also be careful when upgrading. If patch A has a load hook which writes to X, -and then you load patch B which is a superset of A, in some cases you may want -to prevent patch B from writing to X, if A is already loaded. +Also be careful when upgrading. If patch A has a pre/post-patch callback which +writes to X, and then you load patch B which is a superset of A, in some cases +you may want to prevent patch B from writing to X, if A is already loaded. ### Use a shadow variable @@ -339,7 +385,7 @@ Init code changes Any code which runs in an `__init` function or during module or device initialization is problematic, as it may have already run before the patch was -applied. The patch may require a load hook function which detects whether such +applied. The patch may require a pre-patch callback which detects whether such init code has run, and which rewrites or changes the original initialization to force it into the desired state. Some changes involving hardware init are inherently incompatible with live patching. diff --git a/kmod/core/core.c b/kmod/core/core.c index 6cdddc903..8d5da10bb 100644 --- a/kmod/core/core.c +++ b/kmod/core/core.c @@ -305,12 +305,52 @@ static int kpatch_verify_activeness_safety(struct kpatch_module *kpmod) return ret; } +static inline int pre_patch_callback(struct kpatch_object *object) +{ + int ret; + + if (kpatch_object_linked(object) && + object->pre_patch_callback) { + ret = (*object->pre_patch_callback)(object); + if (ret) { + object->callbacks_enabled = false; + return ret; + } + } + object->callbacks_enabled = true; + + return 0; +} + +static inline void post_patch_callback(struct kpatch_object *object) +{ + if (kpatch_object_linked(object) && + object->post_patch_callback && + object->callbacks_enabled) + (*object->post_patch_callback)(object); +} + +static inline void pre_unpatch_callbacks(struct kpatch_object *object) +{ + if (kpatch_object_linked(object) && + object->pre_unpatch_callback && + object->callbacks_enabled) + (*object->pre_unpatch_callback)(object); +} + +static inline void post_unpatch_callback(struct kpatch_object *object) +{ + if (kpatch_object_linked(object) && + object->post_unpatch_callback && + object->callbacks_enabled) + (*object->post_unpatch_callback)(object); +} + /* Called from stop_machine */ static int kpatch_apply_patch(void *data) { struct kpatch_module *kpmod = data; struct kpatch_func *func; - struct kpatch_hook *hook; struct kpatch_object *object; int ret; @@ -320,6 +360,16 @@ static int kpatch_apply_patch(void *data) return ret; } + /* run any user-defined pre-patch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) { + ret = pre_patch_callback(object); + if (ret) { + pr_err("pre-patch callback failed!\n"); + kpatch_state_finish(KPATCH_STATE_FAILURE); + goto err; + } + } + /* tentatively add the new funcs to the global func hash */ do_for_each_linked_func(kpmod, func) { hash_add_rcu(kpatch_func_hash, &func->node, func->old_addr); @@ -341,19 +391,21 @@ static int kpatch_apply_patch(void *data) hash_del_rcu(&func->node); } while_for_each_linked_func(); - return -EBUSY; - } - - /* run any user-defined load hooks */ - list_for_each_entry(object, &kpmod->objects, list) { - if (!kpatch_object_linked(object)) - continue; - list_for_each_entry(hook, &object->hooks_load, list) - (*hook->hook)(); + ret = -EBUSY; + goto err; } + /* run any user-defined post-patch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) + post_patch_callback(object); return 0; +err: + /* undo pre-patch callbacks by calling post-unpatch counterparts */ + list_for_each_entry(object, &kpmod->objects, list) + post_unpatch_callback(object); + + return ret; } /* Called from stop_machine */ @@ -361,7 +413,6 @@ static int kpatch_remove_patch(void *data) { struct kpatch_module *kpmod = data; struct kpatch_func *func; - struct kpatch_hook *hook; struct kpatch_object *object; int ret; @@ -371,25 +422,34 @@ static int kpatch_remove_patch(void *data) return ret; } + /* run any user-defined pre-unpatch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) + pre_unpatch_callbacks(object); + /* Check if any inconsistent NMI has happened while updating */ ret = kpatch_state_finish(KPATCH_STATE_SUCCESS); - if (ret == KPATCH_STATE_FAILURE) - return -EBUSY; + if (ret == KPATCH_STATE_FAILURE) { + ret = -EBUSY; + goto err; + } /* Succeeded, remove all updating funcs from hash table */ do_for_each_linked_func(kpmod, func) { hash_del_rcu(&func->node); } while_for_each_linked_func(); - /* run any user-defined unload hooks */ - list_for_each_entry(object, &kpmod->objects, list) { - if (!kpatch_object_linked(object)) - continue; - list_for_each_entry(hook, &object->hooks_unload, list) - (*hook->hook)(); - } + /* run any user-defined post-unpatch callbacks */ + list_for_each_entry(object, &kpmod->objects, list) + post_unpatch_callback(object); return 0; + +err: + /* undo pre-unpatch callbacks by calling post-patch counterparts */ + list_for_each_entry(object, &kpmod->objects, list) + post_patch_callback(object); + + return ret; } /* @@ -808,7 +868,6 @@ static int kpatch_module_notify(struct notifier_block *nb, unsigned long action, struct kpatch_module *kpmod; struct kpatch_object *object; struct kpatch_func *func; - struct kpatch_hook *hook; int ret = 0; bool found = false; @@ -839,14 +898,19 @@ static int kpatch_module_notify(struct notifier_block *nb, unsigned long action, pr_notice("patching newly loaded module '%s'\n", object->name); - /* run any user-defined load hooks */ - list_for_each_entry(hook, &object->hooks_load, list) - (*hook->hook)(); + /* run user-defined pre-patch callback */ + ret = pre_patch_callback(object); + if (ret) { + pr_err("pre-patch callback failed!\n"); + goto out; /* and WARN */ + } /* add to the global func hash */ list_for_each_entry(func, &object->funcs, list) hash_add_rcu(kpatch_func_hash, &func->node, func->old_addr); + /* run user-defined post-patch callback */ + post_patch_callback(object); out: up(&kpatch_mutex); diff --git a/kmod/core/kpatch.h b/kmod/core/kpatch.h index fb1567ddc..36f021fa0 100644 --- a/kmod/core/kpatch.h +++ b/kmod/core/kpatch.h @@ -60,18 +60,17 @@ struct kpatch_dynrela { struct list_head list; }; -struct kpatch_hook { - struct list_head list; - void (*hook)(void); -}; - struct kpatch_object { struct list_head list; const char *name; struct list_head funcs; struct list_head dynrelas; - struct list_head hooks_load; - struct list_head hooks_unload; + + int (*pre_patch_callback)(struct kpatch_object *); + void (*post_patch_callback)(struct kpatch_object *); + void (*pre_unpatch_callback)(struct kpatch_object *); + void (*post_unpatch_callback)(struct kpatch_object *); + bool callbacks_enabled; /* private */ struct module *mod; diff --git a/kmod/patch/kpatch-macros.h b/kmod/patch/kpatch-macros.h index b6d6e9d39..a60a267f8 100644 --- a/kmod/patch/kpatch-macros.h +++ b/kmod/patch/kpatch-macros.h @@ -3,19 +3,7 @@ #include #include - -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 */ -}; +#include /* * KPATCH_IGNORE_SECTION macro @@ -39,37 +27,73 @@ struct kpatch_unload { #define KPATCH_IGNORE_FUNCTION(_fn) \ void *__kpatch_ignore_func_##_fn __section(.kpatch.ignore.functions) = _fn; -/* - * 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) = { \ + +/* Support for livepatch callbacks */ +#if IS_ENABLED(CONFIG_LIVEPATCH) +# ifdef RHEL_RELEASE_CODE +# if RHEL_RELEASE_CODE >= RHEL_RELEASE_VERSION(7, 5) +# define HAS_LIVEPATCH_CALLBACKS +# endif +# elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) +# define HAS_LIVEPATCH_CALLBACKS +# endif +#endif + +#ifdef HAS_LIVEPATCH_CALLBACKS +# include +typedef struct klp_object patch_object; +#else +# include "kpatch.h" +typedef struct kpatch_object patch_object; +#endif /* HAS_LIVEPATCH_CALLBACKS */ + +typedef int (*kpatch_pre_patch_call_t)(patch_object *obj); +typedef void (*kpatch_post_patch_call_t)(patch_object *obj); +typedef void (*kpatch_pre_unpatch_call_t)(patch_object *obj); +typedef void (*kpatch_post_unpatch_call_t)(patch_object *obj); + +struct kpatch_pre_patch_callback { + kpatch_pre_patch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +struct kpatch_post_patch_callback { + kpatch_post_patch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +struct kpatch_pre_unpatch_callback { + kpatch_pre_unpatch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + +struct kpatch_post_unpatch_callback { + kpatch_post_unpatch_call_t fn; + char *objname; /* filled in by create-diff-object */ +}; + + +#define KPATCH_PRE_PATCH_CALLBACK(_fn) \ + static inline kpatch_pre_patch_call_t __pre_patchtest(void) { return _fn; } \ + static struct kpatch_pre_patch_callback kpatch_pre_patch_data __section(.kpatch.callbacks.pre_patch) __used = { \ .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) = { \ +#define KPATCH_POST_PATCH_CALLBACK(_fn) \ + static inline kpatch_post_patch_call_t __post_patchtest(void) { return _fn; } \ + static struct kpatch_post_patch_callback kpatch_post_patch_data __section(.kpatch.callbacks.post_patch) __used = { \ + .fn = _fn, \ + .objname = NULL \ + }; +#define KPATCH_PRE_UNPATCH_CALLBACK(_fn) \ + static inline kpatch_pre_unpatch_call_t __pre_unpatchtest(void) { return _fn; } \ + static struct kpatch_pre_unpatch_callback kpatch_pre_unpatch_data __section(.kpatch.callbacks.pre_unpatch) __used = { \ + .fn = _fn, \ + .objname = NULL \ + }; +#define KPATCH_POST_UNPATCH_CALLBACK(_fn) \ + static inline kpatch_post_unpatch_call_t __post_unpatchtest(void) { return _fn; } \ + static struct kpatch_post_unpatch_callback kpatch_post_unpatch_data __section(.kpatch.callbacks.post_unpatch) __used = { \ .fn = _fn, \ .objname = NULL \ }; diff --git a/kmod/patch/kpatch-patch-hook.c b/kmod/patch/kpatch-patch-hook.c index df532b755..92f101def 100644 --- a/kmod/patch/kpatch-patch-hook.c +++ b/kmod/patch/kpatch-patch-hook.c @@ -32,8 +32,10 @@ MODULE_PARM_DESC(replace, "replace all previously loaded patch modules"); extern struct kpatch_patch_func __kpatch_funcs[], __kpatch_funcs_end[]; extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[]; -extern struct kpatch_patch_hook __kpatch_hooks_load[], __kpatch_hooks_load_end[]; -extern struct kpatch_patch_hook __kpatch_hooks_unload[], __kpatch_hooks_unload_end[]; +extern struct kpatch_pre_patch_callback __kpatch_callbacks_pre_patch[], __kpatch_callbacks_pre_patch_end[]; +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[]; extern unsigned long __kpatch_force_funcs[], __kpatch_force_funcs_end[]; extern char __kpatch_checksum[]; @@ -182,8 +184,6 @@ static struct kpatch_object *patch_find_or_add_object(struct list_head *head, object->name = name; INIT_LIST_HEAD(&object->funcs); INIT_LIST_HEAD(&object->dynrelas); - INIT_LIST_HEAD(&object->hooks_load); - INIT_LIST_HEAD(&object->hooks_unload); list_add_tail(&object->list, head); @@ -203,7 +203,6 @@ static void patch_free_objects(void) struct kpatch_object *object, *object_safe; struct kpatch_func *func, *func_safe; struct kpatch_dynrela *dynrela, *dynrela_safe; - struct kpatch_hook *hook, *hook_safe; list_for_each_entry_safe(object, object_safe, &kpmod.objects, list) { list_for_each_entry_safe(func, func_safe, &object->funcs, @@ -216,16 +215,6 @@ static void patch_free_objects(void) list_del(&dynrela->list); kfree(dynrela); } - list_for_each_entry_safe(hook, hook_safe, - &object->hooks_load, list) { - list_del(&hook->list); - kfree(hook); - } - list_for_each_entry_safe(hook, hook_safe, - &object->hooks_unload, list) { - list_del(&hook->list); - kfree(hook); - } list_del(&object->list); kobject_put(&object->kobj); } @@ -303,38 +292,84 @@ static int patch_make_dynrelas_list(struct list_head *objects) return 0; } -static int patch_make_hook_lists(struct list_head *objects) +static int patch_set_callbacks(struct list_head *objects) { + struct kpatch_pre_patch_callback *p_pre_patch_callback; + struct kpatch_post_patch_callback *p_post_patch_callback; + struct kpatch_pre_unpatch_callback *p_pre_unpatch_callback; + struct kpatch_post_unpatch_callback *p_post_unpatch_callback; struct kpatch_object *object; - struct kpatch_patch_hook *p_hook; - struct kpatch_hook *hook; - for (p_hook = __kpatch_hooks_load; p_hook < __kpatch_hooks_load_end; - p_hook++) { - object = patch_find_or_add_object(objects, p_hook->objname); + for (p_pre_patch_callback = __kpatch_callbacks_pre_patch; + p_pre_patch_callback < __kpatch_callbacks_pre_patch_end; + p_pre_patch_callback++) { + + object = patch_find_or_add_object(objects, p_pre_patch_callback->objname); if (!object) return -ENOMEM; - hook = kzalloc(sizeof(*hook), GFP_KERNEL); - if (!hook) + if (object->pre_patch_callback) { + pr_err("extra pre-patch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->pre_patch_callback = + (int (*)(struct kpatch_object *)) p_pre_patch_callback->callback; + } + + for (p_post_patch_callback = __kpatch_callbacks_post_patch; + p_post_patch_callback < __kpatch_callbacks_post_patch_end; + p_post_patch_callback++) { + + object = patch_find_or_add_object(objects, p_post_patch_callback->objname); + if (!object) return -ENOMEM; - hook->hook = p_hook->hook; - list_add_tail(&hook->list, &object->hooks_load); + if (object->post_patch_callback) { + pr_err("extra post-patch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->post_patch_callback = + (void (*)(struct kpatch_object *)) p_post_patch_callback->callback; } - for (p_hook = __kpatch_hooks_unload; p_hook < __kpatch_hooks_unload_end; - p_hook++) { - object = patch_find_or_add_object(objects, p_hook->objname); + for (p_pre_unpatch_callback = __kpatch_callbacks_pre_unpatch; + p_pre_unpatch_callback < __kpatch_callbacks_pre_unpatch_end; + p_pre_unpatch_callback++) { + + object = patch_find_or_add_object(objects, p_pre_unpatch_callback->objname); if (!object) return -ENOMEM; - hook = kzalloc(sizeof(*hook), GFP_KERNEL); - if (!hook) + if (object->pre_unpatch_callback) { + pr_err("extra pre-unpatch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->pre_unpatch_callback = + (void (*)(struct kpatch_object *)) p_pre_unpatch_callback->callback; + } + + for (p_post_unpatch_callback = __kpatch_callbacks_post_unpatch; + p_post_unpatch_callback < __kpatch_callbacks_post_unpatch_end; + p_post_unpatch_callback++) { + + object = patch_find_or_add_object(objects, p_post_unpatch_callback->objname); + if (!object) return -ENOMEM; - hook->hook = p_hook->hook; - list_add_tail(&hook->list, &object->hooks_unload); + if (object->post_unpatch_callback) { + pr_err("extra post-unpatch callback for object: %s\n", + object->name); + return -EINVAL; + } + + object->post_unpatch_callback = + (void (*)(struct kpatch_object *)) p_post_unpatch_callback->callback; } return 0; } @@ -360,7 +395,7 @@ static int __init patch_init(void) if (ret) goto err_objects; - ret = patch_make_hook_lists(&kpmod.objects); + ret = patch_set_callbacks(&kpmod.objects); if (ret) goto err_objects; diff --git a/kmod/patch/kpatch-patch.h b/kmod/patch/kpatch-patch.h index d0d6aebbf..917ea32b7 100644 --- a/kmod/patch/kpatch-patch.h +++ b/kmod/patch/kpatch-patch.h @@ -43,8 +43,20 @@ struct kpatch_patch_dynrela { int addend; }; -struct kpatch_patch_hook { - void (*hook)(void); +struct kpatch_pre_patch_callback { + int (*callback)(void *obj); + char *objname; +}; +struct kpatch_post_patch_callback { + void (*callback)(void *obj); + char *objname; +}; +struct kpatch_pre_unpatch_callback { + void (*callback)(void *obj); + char *objname; +}; +struct kpatch_post_unpatch_callback { + void (*callback)(void *obj); char *objname; }; diff --git a/kmod/patch/kpatch.lds.S b/kmod/patch/kpatch.lds.S index b62b1db6e..bc5de826f 100644 --- a/kmod/patch/kpatch.lds.S +++ b/kmod/patch/kpatch.lds.S @@ -9,23 +9,36 @@ __kpatch_checksum = ADDR(.kpatch.checksum); SECTIONS { - .kpatch.hooks.load : { - __kpatch_hooks_load = . ; - *(.kpatch.hooks.load) - __kpatch_hooks_load_end = . ; + .kpatch.callbacks.pre_patch : { + __kpatch_callbacks_pre_patch = . ; + *(.kpatch.callbacks.pre_patch) + __kpatch_callbacks_pre_patch_end = . ; /* * Pad the end of the section with zeros in case the section is empty. * This prevents the kernel from discarding the section at module - * load time. __kpatch_hooks_load_end will still point to the end of - * the section before the padding. If the .kpatch.hooks.load section - * is empty, __kpatch_hooks_load equals __kpatch_hooks_load_end. + * load time. __kpatch_callbacks_pre_patch_end will still point to the + * end of the section before the padding. If the + * .kpatch.callbacks.pre_patch section is empty, + * __kpatch_callbacks_pre_patch equals __kpatch_callbacks_pre_patch_end. */ QUAD(0); } - .kpatch.hooks.unload : { - __kpatch_hooks_unload = . ; - *(.kpatch.hooks.unload) - __kpatch_hooks_unload_end = . ; + .kpatch.callbacks.post_patch : { + __kpatch_callbacks_post_patch = . ; + *(.kpatch.callbacks.post_patch) + __kpatch_callbacks_post_patch_end = . ; + QUAD(0); + } + .kpatch.callbacks.pre_unpatch : { + __kpatch_callbacks_pre_unpatch = . ; + *(.kpatch.callbacks.pre_unpatch) + __kpatch_callbacks_pre_unpatch_end = . ; + QUAD(0); + } + .kpatch.callbacks.post_unpatch : { + __kpatch_callbacks_post_unpatch = . ; + *(.kpatch.callbacks.post_unpatch) + __kpatch_callbacks_post_unpatch_end = . ; QUAD(0); } .kpatch.force : { diff --git a/kmod/patch/livepatch-patch-hook.c b/kmod/patch/livepatch-patch-hook.c index d27dc1a45..0a6139112 100644 --- a/kmod/patch/livepatch-patch-hook.c +++ b/kmod/patch/livepatch-patch-hook.c @@ -79,6 +79,7 @@ struct patch_object { struct list_head list; struct list_head funcs; struct list_head relocs; + struct klp_callbacks callbacks; const char *name; int funcs_nr, relocs_nr; }; @@ -210,6 +211,82 @@ static void patch_free_livepatch(struct klp_patch *patch) } } +extern struct kpatch_pre_patch_callback __kpatch_callbacks_pre_patch[], __kpatch_callbacks_pre_patch_end[]; +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[]; + +static int add_callbacks_to_patch_objects(void) +{ + struct kpatch_pre_patch_callback *p_pre_patch_callback; + struct kpatch_post_patch_callback *p_post_patch_callback; + struct kpatch_pre_unpatch_callback *p_pre_unpatch_callback; + struct kpatch_post_unpatch_callback *p_post_unpatch_callback; + struct patch_object *object; + + for (p_pre_patch_callback = __kpatch_callbacks_pre_patch; + p_pre_patch_callback < __kpatch_callbacks_pre_patch_end; + p_pre_patch_callback++) { + object = patch_find_object_by_name(p_pre_patch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.pre_patch) { + pr_err("extra pre-patch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.pre_patch = (int (*)(struct klp_object *)) + p_pre_patch_callback->callback; + } + + for (p_post_patch_callback = __kpatch_callbacks_post_patch; + p_post_patch_callback < __kpatch_callbacks_post_patch_end; + p_post_patch_callback++) { + object = patch_find_object_by_name(p_post_patch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.post_patch) { + pr_err("extra post-patch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.post_patch = (void (*)(struct klp_object *)) + p_post_patch_callback->callback; + } + + for (p_pre_unpatch_callback = __kpatch_callbacks_pre_unpatch; + p_pre_unpatch_callback < __kpatch_callbacks_pre_unpatch_end; + p_pre_unpatch_callback++) { + object = patch_find_object_by_name(p_pre_unpatch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.pre_unpatch) { + pr_err("extra pre-unpatch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.pre_unpatch = (void (*)(struct klp_object *)) + p_pre_unpatch_callback->callback; + } + + for (p_post_unpatch_callback = __kpatch_callbacks_post_unpatch; + p_post_unpatch_callback < __kpatch_callbacks_post_unpatch_end; + p_post_unpatch_callback++) { + object = patch_find_object_by_name(p_post_unpatch_callback->objname); + if (!object) + return -ENOMEM; + if (object->callbacks.post_unpatch) { + pr_err("extra post-unpatch callback for object: %s\n", + object->name ? object->name : "vmlinux"); + return -EINVAL; + } + object->callbacks.post_unpatch = (void (*)(struct klp_object *)) + p_post_unpatch_callback->callback; + } + + return 0; +} + extern struct kpatch_patch_func __kpatch_funcs[], __kpatch_funcs_end[]; #ifndef HAVE_ELF_RELOCS extern struct kpatch_patch_dynrela __kpatch_dynrelas[], __kpatch_dynrelas_end[]; @@ -248,6 +325,10 @@ static int __init patch_init(void) } #endif + ret = add_callbacks_to_patch_objects(); + if (ret) + goto out; + /* past this point, only possible return code is -ENOMEM */ ret = -ENOMEM; @@ -311,6 +392,8 @@ static int __init patch_init(void) } #endif /* HAVE_ELF_RELOCS */ + lobject->callbacks = object->callbacks; + i++; } diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index bb2f65162..1dc5c6b57 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -1399,29 +1399,44 @@ static void kpatch_include_standard_elements(struct kpatch_elf *kelf) list_entry(kelf->symbols.next, struct symbol, list)->include = 1; } -static int kpatch_include_hook_elements(struct kpatch_elf *kelf) +static int kpatch_include_callback_elements(struct kpatch_elf *kelf) { struct section *sec; struct symbol *sym; struct rela *rela; int found = 0; + static char *callback_sections[] = { + ".kpatch.callbacks.pre_patch", + ".kpatch.callbacks.post_patch", + ".kpatch.callbacks.pre_unpatch", + ".kpatch.callbacks.post_unpatch", + ".rela.kpatch.callbacks.pre_patch", + ".rela.kpatch.callbacks.post_patch", + ".rela.kpatch.callbacks.pre_unpatch", + ".rela.kpatch.callbacks.post_unpatch", + NULL, + }; + char **callback_section; + /* include load/unload sections */ list_for_each_entry(sec, &kelf->sections, list) { - if (!strcmp(sec->name, ".kpatch.hooks.load") || - !strcmp(sec->name, ".kpatch.hooks.unload") || - !strcmp(sec->name, ".rela.kpatch.hooks.load") || - !strcmp(sec->name, ".rela.kpatch.hooks.unload")) { + + for (callback_section = callback_sections; *callback_section; callback_section++) { + + if (strcmp(*callback_section, sec->name)) + continue; + sec->include = 1; found = 1; if (is_rela_section(sec)) { - /* include hook dependencies */ + /* include callback dependencies */ rela = list_entry(sec->relas.next, struct rela, list); sym = rela->sym; - log_normal("found hook: %s\n",sym->name); + log_normal("found callback: %s\n",sym->name); kpatch_include_symbol(sym); - /* strip the hook symbol */ + /* strip the callback symbol */ sym->include = 0; sym->sec->sym = NULL; /* use section symbol instead */ @@ -1432,14 +1447,17 @@ static int kpatch_include_hook_elements(struct kpatch_elf *kelf) } } - /* - * Strip temporary global load/unload function pointer objects - * used by the kpatch_[load|unload]() macros. - */ - list_for_each_entry(sym, &kelf->symbols, list) - if (!strcmp(sym->name, "kpatch_load_data") || - !strcmp(sym->name, "kpatch_unload_data")) - sym->include = 0; + /* Strip temporary global structures used by the callback macros. */ + list_for_each_entry(sym, &kelf->symbols, list) { + if (!sym->sec) + continue; + for (callback_section = callback_sections; *callback_section; callback_section++) { + if (!strcmp(*callback_section, sym->sec->name)) { + sym->include = 0; + break; + } + } + } return found; } @@ -2284,7 +2302,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf, /* * Convert global symbols to local so other objects in - * the patch module (like the patch hook object's init + * the patch module (like the patch callback object's init * code) won't link to this function and call it before * its relocations have been applied. */ @@ -2611,13 +2629,27 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, krela_sec->sh.sh_size = krela_sec->data->d_size; } -static void kpatch_create_hooks_objname_rela(struct kpatch_elf *kelf, char *objname) +static void kpatch_create_callbacks_objname_rela(struct kpatch_elf *kelf, char *objname) { struct section *sec; struct rela *rela; struct symbol *strsym; int objname_offset; + struct callback { char *name; int offset; }; + static struct callback callbacks[] = { + { .name = ".rela.kpatch.callbacks.pre_patch", + .offset = offsetof(struct kpatch_pre_patch_callback, objname) }, + { .name = ".rela.kpatch.callbacks.post_patch", + .offset = offsetof(struct kpatch_post_patch_callback, objname) }, + { .name = ".rela.kpatch.callbacks.pre_unpatch", + .offset = offsetof(struct kpatch_pre_unpatch_callback, objname) }, + { .name = ".rela.kpatch.callbacks.post_unpatch", + .offset = offsetof(struct kpatch_post_patch_callback, objname) }, + { .name = NULL, .offset = 0 }, + }; + struct callback *callbackp; + /* lookup strings symbol */ strsym = find_symbol_by_name(&kelf->symbols, ".kpatch.strings"); if (!strsym) @@ -2627,15 +2659,16 @@ static void kpatch_create_hooks_objname_rela(struct kpatch_elf *kelf, char *objn objname_offset = offset_of_string(&kelf->strings, objname); list_for_each_entry(sec, &kelf->sections, list) { - if (strcmp(sec->name, ".rela.kpatch.hooks.load") && - strcmp(sec->name, ".rela.kpatch.hooks.unload")) - continue; - - ALLOC_LINK(rela, &sec->relas); - rela->sym = strsym; - rela->type = ABSOLUTE_RELA_TYPE; - rela->addend = objname_offset; - rela->offset = offsetof(struct kpatch_patch_hook, objname); + for (callbackp = callbacks; callbackp->name; callbackp++) { + if (!strcmp(callbackp->name, sec->name)) { + ALLOC_LINK(rela, &sec->relas); + rela->sym = strsym; + rela->type = ABSOLUTE_RELA_TYPE; + rela->addend = objname_offset; + rela->offset = callbackp->offset; + break; + } + } } } @@ -2845,7 +2878,7 @@ int main(int argc, char *argv[]) { struct kpatch_elf *kelf_base, *kelf_patched, *kelf_out; struct arguments arguments; - int num_changed, hooks_exist, new_globals_exist; + int num_changed, callbacks_exist, new_globals_exist; struct lookup_table *lookup; struct section *sec, *symtab; struct symbol *sym; @@ -2914,7 +2947,7 @@ int main(int argc, char *argv[]) kpatch_include_standard_elements(kelf_patched); num_changed = kpatch_include_changed_functions(kelf_patched); kpatch_include_debug_sections(kelf_patched); - hooks_exist = kpatch_include_hook_elements(kelf_patched); + callbacks_exist = kpatch_include_callback_elements(kelf_patched); kpatch_include_force_elements(kelf_patched); new_globals_exist = kpatch_include_new_globals(kelf_patched); @@ -2925,8 +2958,8 @@ int main(int argc, char *argv[]) kpatch_verify_patchability(kelf_patched); if (!num_changed && !new_globals_exist) { - if (hooks_exist) - log_debug("no changed functions were found, but hooks exist\n"); + if (callbacks_exist) + log_debug("no changed functions were found, but callbacks exist\n"); else { log_debug("no changed functions were found\n"); return 3; /* 1 is ERROR, 2 is DIFF_FATAL */ @@ -2964,7 +2997,7 @@ int main(int argc, char *argv[]) kpatch_create_patches_sections(kelf_out, lookup, objname); kpatch_create_intermediate_sections(kelf_out, lookup, objname, pmod_name); kpatch_create_kpatch_arch_section(kelf_out, objname); - kpatch_create_hooks_objname_rela(kelf_out, objname); + kpatch_create_callbacks_objname_rela(kelf_out, objname); kpatch_build_strings_section_data(kelf_out); kpatch_create_mcount_sections(kelf_out); diff --git a/test/integration/centos-7/macro-callbacks.patch b/test/integration/centos-7/macro-callbacks.patch new file mode 100644 index 000000000..0d6831b66 --- /dev/null +++ b/test/integration/centos-7/macro-callbacks.patch @@ -0,0 +1,160 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +--- src.old/fs/aio.c 2018-02-26 11:07:51.522610407 -0500 ++++ src/fs/aio.c 2018-03-05 11:17:21.560015449 -0500 +@@ -42,6 +42,50 @@ + #include + #include + ++#include ++#include "kpatch-macros.h" ++ ++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, patch_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 int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 +--- src.old/drivers/input/joydev.c 2018-02-26 11:07:49.470610407 -0500 ++++ src/drivers/input/joydev.c 2018-03-05 11:18:13.998015449 -0500 +@@ -954,3 +954,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++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, patch_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 int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +--- src.old/drivers/input/misc/pcspkr.c 2018-02-26 11:07:49.477610407 -0500 ++++ src/drivers/input/misc/pcspkr.c 2018-03-05 11:18:23.411015449 -0500 +@@ -136,3 +136,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++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, patch_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 int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); diff --git a/test/integration/centos-7/macro-hooks-LOADED.test b/test/integration/centos-7/macro-hooks-LOADED.test deleted file mode 100755 index 135d4b1ba..000000000 --- a/test/integration/centos-7/macro-hooks-LOADED.test +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -[[ $(cat /proc/sys/fs/aio-max-nr) = 262144 ]] diff --git a/test/integration/centos-7/macro-hooks.patch b/test/integration/centos-7/macro-hooks.patch deleted file mode 100644 index 5580d7d77..000000000 --- a/test/integration/centos-7/macro-hooks.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -Nupr src.orig/fs/aio.c src/fs/aio.c ---- src.orig/fs/aio.c 2017-09-22 15:27:21.702056192 -0400 -+++ src/fs/aio.c 2017-09-22 15:27:36.703118311 -0400 -@@ -1683,6 +1683,20 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t - return ret; - } - -+static int aio_max_nr_orig; -+void kpatch_load_aio_max_nr(void) -+{ -+ aio_max_nr_orig = aio_max_nr; -+ aio_max_nr = 0x40000; -+} -+void kpatch_unload_aio_max_nr(void) -+{ -+ aio_max_nr = aio_max_nr_orig; -+} -+#include "kpatch-macros.h" -+KPATCH_LOAD_HOOK(kpatch_load_aio_max_nr); -+KPATCH_UNLOAD_HOOK(kpatch_unload_aio_max_nr); -+ - /* io_getevents: - * Attempts to read at least min_nr events and up to nr events from - * the completion queue for the aio_context specified by ctx_id. If diff --git a/test/integration/fedora-27/macro-callbacks.patch b/test/integration/fedora-27/macro-callbacks.patch new file mode 100644 index 000000000..569cc7c80 --- /dev/null +++ b/test/integration/fedora-27/macro-callbacks.patch @@ -0,0 +1,163 @@ +kpatch/livepatch callback test patch: + + vmlinux + pcspkr (mod) + joydev (mod) + +Note: update joydev's pre-patch callback to return -ENODEV to test failure path + +diff -Nupr src.old/drivers/input/joydev.c src/drivers/input/joydev.c +--- src.old/drivers/input/joydev.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/drivers/input/joydev.c 2018-03-22 16:32:40.963082354 -0400 +@@ -1010,3 +1010,47 @@ static void __exit joydev_exit(void) + + module_init(joydev_init); + module_exit(joydev_exit); ++ ++#include ++#include "kpatch-macros.h" ++ ++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, patch_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 int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; /* return -ENODEV; */ ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/drivers/input/misc/pcspkr.c src/drivers/input/misc/pcspkr.c +--- src.old/drivers/input/misc/pcspkr.c 2018-03-22 16:29:27.716082354 -0400 ++++ src/drivers/input/misc/pcspkr.c 2018-03-22 16:32:40.963082354 -0400 +@@ -132,3 +132,46 @@ static struct platform_driver pcspkr_pla + }; + module_platform_driver(pcspkr_platform_driver); + ++#include ++#include "kpatch-macros.h" ++ ++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, patch_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 int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); +diff -Nupr src.old/fs/aio.c src/fs/aio.c +--- src.old/fs/aio.c 2017-09-03 16:56:17.000000000 -0400 ++++ src/fs/aio.c 2018-03-22 16:32:40.962082354 -0400 +@@ -46,6 +46,50 @@ + + #include "internal.h" + ++#include ++#include "kpatch-macros.h" ++ ++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, patch_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 int pre_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++ return 0; ++} ++KPATCH_PRE_PATCH_CALLBACK(pre_patch_callback); ++ ++static void post_patch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_PATCH_CALLBACK(post_patch_callback); ++ ++static void pre_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_PRE_UNPATCH_CALLBACK(pre_unpatch_callback); ++ ++static void post_unpatch_callback(patch_object *obj) ++{ ++ callback_info(__func__, obj); ++} ++KPATCH_POST_UNPATCH_CALLBACK(post_unpatch_callback); ++ + #define AIO_RING_MAGIC 0xa10a10a1 + #define AIO_RING_COMPAT_FEATURES 1 + #define AIO_RING_INCOMPAT_FEATURES 0 diff --git a/test/integration/fedora-27/macro-hooks-LOADED.test b/test/integration/fedora-27/macro-hooks-LOADED.test deleted file mode 100755 index 135d4b1ba..000000000 --- a/test/integration/fedora-27/macro-hooks-LOADED.test +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -[[ $(cat /proc/sys/fs/aio-max-nr) = 262144 ]] diff --git a/test/integration/fedora-27/macro-hooks.patch b/test/integration/fedora-27/macro-hooks.patch deleted file mode 100644 index ebdbf823e..000000000 --- a/test/integration/fedora-27/macro-hooks.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff -Nupr src.orig/fs/aio.c src/fs/aio.c ---- src.orig/fs/aio.c 2017-11-17 15:58:38.111211972 -0500 -+++ src/fs/aio.c 2017-11-17 15:59:17.699211972 -0500 -@@ -1819,6 +1819,20 @@ SYSCALL_DEFINE3(io_cancel, aio_context_t - return ret; - } - -+static int aio_max_nr_orig; -+void kpatch_load_aio_max_nr(void) -+{ -+ aio_max_nr_orig = aio_max_nr; -+ aio_max_nr = 0x40000; -+} -+void kpatch_unload_aio_max_nr(void) -+{ -+ aio_max_nr = aio_max_nr_orig; -+} -+#include "kpatch-macros.h" -+KPATCH_LOAD_HOOK(kpatch_load_aio_max_nr); -+KPATCH_UNLOAD_HOOK(kpatch_unload_aio_max_nr); -+ - /* io_getevents: - * Attempts to read at least min_nr events and up to nr events from - * the completion queue for the aio_context specified by ctx_id. If