Merge pull request #780 from joe-lawrence/livepatch-hooks

kmod: add support for in-kernel livepatch hooks
This commit is contained in:
Joe Lawrence 2018-04-02 14:49:07 -04:00 committed by GitHub
commit 55650e16af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 810 additions and 232 deletions

View File

@ -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.

View File

@ -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);

View File

@ -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;

View File

@ -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 \
};

View File

@ -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;

View File

@ -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;
};

View File

@ -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 : {

View File

@ -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++;
}

View File

@ -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);

View 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);

View File

@ -1,3 +0,0 @@
#!/bin/bash
[[ $(cat /proc/sys/fs/aio-max-nr) = 262144 ]]

View File

@ -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

View 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

View File

@ -1,3 +0,0 @@
#!/bin/bash
[[ $(cat /proc/sys/fs/aio-max-nr) = 262144 ]]

View File

@ -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