mirror of
https://github.com/dynup/kpatch
synced 2024-12-22 21:22:07 +00:00
Merge pull request #780 from joe-lawrence/livepatch-hooks
kmod: add support for in-kernel livepatch hooks
This commit is contained in:
commit
55650e16af
@ -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
|
||||
will be run by `stop_machine` as part of applying or removing a patch.
|
||||
(Therefore the hooks must not block or sleep.)
|
||||
* `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.
|
||||
|
||||
* 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.
|
||||
|
||||
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:
|
||||
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 callbacks must not block or sleep.)
|
||||
|
||||
* For patches to kernel modules which haven't been loaded yet, a
|
||||
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 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.
|
||||
|
112
kmod/core/core.c
112
kmod/core/core.c
@ -305,12 +305,52 @@ out:
|
||||
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 @@ done:
|
||||
|
||||
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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -3,19 +3,7 @@
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/jiffies.h>
|
||||
|
||||
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 <linux/version.h>
|
||||
|
||||
/*
|
||||
* 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 <linux/livepatch.h>
|
||||
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 \
|
||||
};
|
||||
|
@ -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)
|
||||
return -ENOMEM;
|
||||
if (object->pre_patch_callback) {
|
||||
pr_err("extra pre-patch callback for object: %s\n",
|
||||
object->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
hook->hook = p_hook->hook;
|
||||
list_add_tail(&hook->list, &object->hooks_load);
|
||||
object->pre_patch_callback =
|
||||
(int (*)(struct kpatch_object *)) p_pre_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_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 = kzalloc(sizeof(*hook), GFP_KERNEL);
|
||||
if (!hook)
|
||||
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_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->hook = p_hook->hook;
|
||||
list_add_tail(&hook->list, &object->hooks_unload);
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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 : {
|
||||
|
@ -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++;
|
||||
}
|
||||
|
||||
|
@ -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.
|
||||
*/
|
||||
@ -2709,13 +2727,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)
|
||||
@ -2725,15 +2757,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2943,7 +2976,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;
|
||||
@ -3012,7 +3045,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);
|
||||
|
||||
@ -3023,8 +3056,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 */
|
||||
@ -3062,7 +3095,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);
|
||||
|
160
test/integration/centos-7/macro-callbacks.patch
Normal file
160
test/integration/centos-7/macro-callbacks.patch
Normal file
@ -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 <asm/kmap_types.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
+#include <linux/module.h>
|
||||
+#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 <linux/module.h>
|
||||
+#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 <linux/module.h>
|
||||
+#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);
|
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
[[ $(cat /proc/sys/fs/aio-max-nr) = 262144 ]]
|
@ -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
|
163
test/integration/fedora-27/macro-callbacks.patch
Normal file
163
test/integration/fedora-27/macro-callbacks.patch
Normal file
@ -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 <linux/module.h>
|
||||
+#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 <linux/module.h>
|
||||
+#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 <linux/module.h>
|
||||
+#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
|
@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
[[ $(cat /proc/sys/fs/aio-max-nr) = 262144 ]]
|
@ -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
|
Loading…
Reference in New Issue
Block a user