When the livepatch core executes klp_(un)patch_object, call out to a
livepatch-module specified array of callback hooks.  These hooks provide
a notification mechanism for livepatch modules when klp_objects are
(un)patching. This may be most interesting when another kernel module
is a klp_object target and the livepatch module needs to execute code
after the target is loaded, but before its module_init code is run.

The patch-hook executes right before patching objects and the
unpatch-hook executes right after unpatching objects.

Signed-off-by: Joe Lawrence <[email protected]>
---
 Documentation/livepatch/hooks.txt        |  98 +++++++++++++++++++++++++
 include/linux/livepatch.h                |  32 ++++++++
 kernel/livepatch/core.c                  |   5 --
 kernel/livepatch/patch.c                 |  35 +++++++++
 samples/livepatch/Makefile               |   2 +
 samples/livepatch/livepatch-hooks-demo.c | 122 +++++++++++++++++++++++++++++++
 samples/livepatch/livepatch-hooks-mod.c  |  38 ++++++++++
 7 files changed, 327 insertions(+), 5 deletions(-)
 create mode 100644 Documentation/livepatch/hooks.txt
 create mode 100644 samples/livepatch/livepatch-hooks-demo.c
 create mode 100644 samples/livepatch/livepatch-hooks-mod.c

diff --git a/Documentation/livepatch/hooks.txt 
b/Documentation/livepatch/hooks.txt
new file mode 100644
index 000000000000..ef18101a3b90
--- /dev/null
+++ b/Documentation/livepatch/hooks.txt
@@ -0,0 +1,98 @@
+(Un)patching Hooks
+==================
+
+Livepatching (un)patch-hooks provide a mechanism to register and execute
+a set of callback functions when the kernel's livepatching core performs
+an (un)patching operation on a given kernel object.
+
+The hooks are provided and registered by a livepatch module as part of
+klp_objects that make up its klp_patch structure.  Both patch and
+unpatch-hook function signatures accept a pointer to a klp_object
+argument and return an integer status, ie:
+
+  static int patch_hook(struct klp_object *obj)
+  {
+       /* ... */
+  }
+  static int unpatch_hook(struct klp_object *obj)
+  {
+       /* ... */
+  }
+
+  static struct klp_hook patch_hooks[] = {
+       {
+               .hook = patch_hook,
+       }, { }
+  };
+  static struct klp_hook unpatch_hooks[] = {
+       {
+               .hook = unpatch_hook,
+       }, { }
+  };
+
+  static struct klp_object objs[] = {
+       {
+               /* ... */
+               .patch_hooks = patch_hooks,
+               .unpatch_hooks = unpatch_hooks,
+       }, { }
+  };
+
+  static struct klp_patch patch = {
+       .mod = THIS_MODULE,
+       .objs = objs,
+  };
+
+If a hook returns non-zero status, the livepatching core will log a
+hook failure warning message.
+
+Multiple (un)patch-hooks may be registered per klp_object.  Each hook
+will execute regardless of any previously executed hook's non-zero
+return status.
+
+Hooks are optional.  The livepatching core will not execute any
+callbacks for an empty klp_hook.hook array or a NULL klp_hook.hook
+value.
+
+
+For module targets
+------------------
+
+In the case of kernel module objects, patch-hooks provide a livepatch
+module opportunity to defer execution until a target module is loaded.
+Similarly, unpatch-hooks only call back into a livepatch module after a
+target module has itself cleaned up.  In these cases, the order of
+execution looks like:
+
+  load kernel module
+  execute all patch_hooks[] for this kernel object
+  livepatch kernel object
+  execute module_init function
+
+  ...
+
+  unload kernel module
+  execute module_exit function
+  livepatch restore kernel object
+  execute all unpatch_hooks[] for this kernel object
+
+On the other hand, if a target kernel module is already present when a
+livepatch is loading, then the corresponding patch hook(s) will execute
+as soon as the livepatching kernel core enables the livepatch.
+
+It may be useful for hooks to inspect the module state of the klp_object
+it is passed (i.e. obj->mod->state).  Patch hooks can expect to see
+modules in MODULE_STATE_LIVE and MODULE_STATE_COMING states.  Unpatch
+hooks can expect modules in MODULE_STATE_LIVE and MODULE_STATE_GOING
+states.
+
+
+For vmlinux target
+------------------
+
+As the kernel is always loaded, patch-hooks for vmlinux will execute as
+soon as the livepatch core enables the livepatch.  Patch-hooks will also
+run if the livepatch is disabled and then re-enabled.
+
+Unpatch-hooks for vmlinux will only execute when the livepatch is
+disabled.
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 194991ef9347..d95050386ac7 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -87,10 +87,23 @@ struct klp_func {
        bool transition;
 };
 
+struct klp_object;
+
+/**
+ * struct klp_hook - hook structure for live patching
+ * @hook:      function to be executed on hook
+ *
+ */
+struct klp_hook {
+       int (*hook)(struct klp_object *obj);
+};
+
 /**
  * struct klp_object - kernel object structure for live patching
  * @name:      module name (or NULL for vmlinux)
  * @funcs:     function entries for functions to be patched in the object
+ * @patch_hooks:       functions to be executed on patching
+ * @unpatch_hooks:     functions to be executed on unpatching
  * @kobj:      kobject for sysfs resources
  * @mod:       kernel module associated with the patched object
  *             (NULL for vmlinux)
@@ -100,6 +113,8 @@ struct klp_object {
        /* external */
        const char *name;
        struct klp_func *funcs;
+       struct klp_hook *patch_hooks;
+       struct klp_hook *unpatch_hooks;
 
        /* internal */
        struct kobject kobj;
@@ -108,6 +123,17 @@ struct klp_object {
 };
 
 /**
+ * klp_is_module() - is klp_object a module?
+ * @obj:       klp_object pointer
+ *
+ * Return: true if klp_object is a loadable module
+ */
+static inline bool klp_is_module(struct klp_object *obj)
+{
+       return obj->name;
+}
+
+/**
  * struct klp_patch - patch structure for live patching
  * @mod:       reference to the live patch module
  * @objs:      object entries for kernel objects to be patched
@@ -138,6 +164,12 @@ struct klp_patch {
             func->old_name || func->new_func || func->old_sympos; \
             func++)
 
+#define klp_for_each_patch_hook(obj, hook) \
+       for (hook = obj->patch_hooks; hook && hook->hook; hook++)
+
+#define klp_for_each_unpatch_hook(obj, hook) \
+       for (hook = obj->unpatch_hooks; hook && hook->hook; hook++)
+
 int klp_register_patch(struct klp_patch *);
 int klp_unregister_patch(struct klp_patch *);
 int klp_enable_patch(struct klp_patch *);
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index b9628e43c78f..ff3685470057 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -49,11 +49,6 @@
 
 static struct kobject *klp_root_kobj;
 
-static bool klp_is_module(struct klp_object *obj)
-{
-       return obj->name;
-}
-
 static bool klp_is_object_loaded(struct klp_object *obj)
 {
        return !obj->name || obj->mod;
diff --git a/kernel/livepatch/patch.c b/kernel/livepatch/patch.c
index 52c4e907c14b..c8084a18ddb7 100644
--- a/kernel/livepatch/patch.c
+++ b/kernel/livepatch/patch.c
@@ -235,25 +235,60 @@ static int klp_patch_func(struct klp_func *func)
        return ret;
 }
 
+/**
+ * klp_run_hook - execute a given klp_hook callback
+ * @hook:      callback hook
+ * @obj:       kernel object that has been hooked
+ *
+ * Return: return value from hook, or 0 if none is currently associated
+ */
+static int klp_run_hook(struct klp_hook *hook, struct klp_object *obj)
+{
+       if (hook && hook->hook)
+               return (*hook->hook)(obj);
+
+       return 0;
+}
+
 void klp_unpatch_object(struct klp_object *obj)
 {
        struct klp_func *func;
+       struct klp_hook *hook;
+       int ret;
 
        klp_for_each_func(obj, func)
                if (func->patched)
                        klp_unpatch_func(func);
 
        obj->patched = false;
+
+       klp_for_each_unpatch_hook(obj, hook) {
+               ret = klp_run_hook(hook, obj);
+               if (ret) {
+                       pr_warn("unpatch hook '%p' failed for object '%s'\n",
+                               hook, klp_is_module(obj) ? obj->name : 
"vmlinux");
+               }
+       }
+
 }
 
 int klp_patch_object(struct klp_object *obj)
 {
        struct klp_func *func;
+       struct klp_hook *hook;
        int ret;
 
        if (WARN_ON(obj->patched))
                return -EINVAL;
 
+       klp_for_each_patch_hook(obj, hook) {
+               ret = klp_run_hook(hook, obj);
+               if (ret) {
+                       pr_warn("patch hook '%p' failed for object '%s'\n",
+                               hook, klp_is_module(obj) ? obj->name : 
"vmlinux");
+               }
+       }
+
        klp_for_each_func(obj, func) {
                ret = klp_patch_func(func);
                if (ret) {
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile
index 10319d7ea0b1..2568a56ba8f3 100644
--- a/samples/livepatch/Makefile
+++ b/samples/livepatch/Makefile
@@ -1 +1,3 @@
 obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-hooks-demo.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-hooks-mod.o
diff --git a/samples/livepatch/livepatch-hooks-mod.c 
b/samples/livepatch/livepatch-hooks-mod.c
new file mode 100644
index 000000000000..f4ec09a5fc53
--- /dev/null
+++ b/samples/livepatch/livepatch-hooks-mod.c
@@ -0,0 +1,38 @@
+/*
+ * livepatch-hooks-mod.c - (un)patching hooks demo support module
+ *
+ * Copyright (C) 2017 Joe Lawrence <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+static int livepatch_hooks_mod_init(void)
+{
+       pr_info("%s\n", __func__);
+       return 0;
+}
+
+static void livepatch_hooks_mod_exit(void)
+{
+       pr_info("%s\n", __func__);
+}
+
+module_init(livepatch_hooks_mod_init);
+module_exit(livepatch_hooks_mod_exit);
+MODULE_LICENSE("GPL");
diff --git a/samples/livepatch/livepatch-hooks-demo.c 
b/samples/livepatch/livepatch-hooks-demo.c
new file mode 100644
index 000000000000..672a749a0549
--- /dev/null
+++ b/samples/livepatch/livepatch-hooks-demo.c
@@ -0,0 +1,122 @@
+/*
+ * livepatch-hooks-demo.c - (un)patching hooks livepatch demo
+ *
+ * Copyright (C) 2017 Joe Lawrence <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+
+const char *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 hook_info(const char *hook, struct klp_object *obj)
+{
+       if (klp_is_module(obj))
+               pr_info("%s: %s\n", hook, module_state[obj->mod->state]);
+       else
+               pr_info("%s: vmlinux\n", hook);
+}
+
+/* Executed on object patching (ie, patch enablement) */
+static int patch_hook(struct klp_object *obj)
+{
+       hook_info(__func__, obj);
+       return 0;
+}
+
+/* Executed on object unpatching (ie, patch disablement) */
+static int unpatch_hook(struct klp_object *obj)
+{
+       hook_info(__func__, obj);
+       return 0;
+}
+
+static struct klp_func funcs[] = {
+       { }
+};
+
+static struct klp_hook patch_hooks[] = {
+       {
+               .hook = patch_hook,
+       }, { }
+};
+static struct klp_hook unpatch_hooks[] = {
+       {
+               .hook = unpatch_hook,
+       }, { }
+};
+
+static struct klp_object objs[] = {
+       {
+               .name = "livepatch_hooks_mod",
+               .funcs = funcs,
+               .patch_hooks = patch_hooks,
+               .unpatch_hooks = unpatch_hooks,
+       }, { }
+};
+
+static struct klp_patch patch = {
+       .mod = THIS_MODULE,
+       .objs = objs,
+};
+
+static int livepatch_hooks_demo_init(void)
+{
+       int ret;
+
+       if (!klp_have_reliable_stack() && !patch.immediate) {
+               /*
+                * WARNING: Be very careful when using 'patch.immediate' in
+                * your patches.  It's ok to use it for simple patches like
+                * this, but for more complex patches which change function
+                * semantics, locking semantics, or data structures, it may not
+                * be safe.  Use of this option will also prevent removal of
+                * the patch.
+                *
+                * See Documentation/livepatch/livepatch.txt for more details.
+                */
+               patch.immediate = true;
+               pr_notice("The consistency model isn't supported for your 
architecture.  Bypassing safety mechanisms and applying the patch 
immediately.\n");
+       }
+
+       ret = klp_register_patch(&patch);
+       if (ret)
+               return ret;
+       ret = klp_enable_patch(&patch);
+       if (ret) {
+               WARN_ON(klp_unregister_patch(&patch));
+               return ret;
+       }
+       return 0;
+}
+
+static void livepatch_hooks_demo_exit(void)
+{
+       WARN_ON(klp_unregister_patch(&patch));
+}
+
+module_init(livepatch_hooks_demo_init);
+module_exit(livepatch_hooks_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
-- 
1.8.3.1

Reply via email to