Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93862e38 authored by Joe Lawrence's avatar Joe Lawrence Committed by Jiri Kosina
Browse files

livepatch: add (un)patch callbacks



Provide livepatch modules a klp_object (un)patching notification
mechanism.  Pre and post-(un)patch callbacks allow livepatch modules to
setup or synchronize changes that would be difficult to support in only
patched-or-unpatched code contexts.

Callbacks can be registered for target module or vmlinux klp_objects,
but each implementation is klp_object specific.

  - Pre-(un)patch callbacks run before any (un)patching transition
    starts.

  - Post-(un)patch callbacks run once an object has been (un)patched and
    the klp_patch fully transitioned to its target state.

Example use cases include modification of global data and registration
of newly available services/handlers.

See Documentation/livepatch/callbacks.txt for details and
samples/livepatch/ for examples.

Signed-off-by: default avatarJoe Lawrence <joe.lawrence@redhat.com>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@redhat.com>
Acked-by: default avatarMiroslav Benes <mbenes@suse.cz>
Signed-off-by: default avatarJiri Kosina <jkosina@suse.cz>
parent 19205da6
Loading
Loading
Loading
Loading
+605 −0

File added.

Preview size limit exceeded, changes collapsed.

+26 −0
Original line number Diff line number Diff line
@@ -87,10 +87,35 @@ struct klp_func {
	bool transition;
};

struct klp_object;

/**
 * struct klp_callbacks - pre/post live-(un)patch callback structure
 * @pre_patch:		executed before code patching
 * @post_patch:		executed after code patching
 * @pre_unpatch:	executed before code unpatching
 * @post_unpatch:	executed after code unpatching
 * @post_unpatch_enabled:	flag indicating if post-unpatch callback
 * 				should run
 *
 * All callbacks are optional.  Only the pre-patch callback, if provided,
 * will be unconditionally executed.  If the parent klp_object fails to
 * patch for any reason, including a non-zero error status returned from
 * the pre-patch callback, no further callbacks will be executed.
 */
struct klp_callbacks {
	int (*pre_patch)(struct klp_object *obj);
	void (*post_patch)(struct klp_object *obj);
	void (*pre_unpatch)(struct klp_object *obj);
	void (*post_unpatch)(struct klp_object *obj);
	bool post_unpatch_enabled;
};

/**
 * 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
 * @callbacks:	functions to be executed pre/post (un)patching
 * @kobj:	kobject for sysfs resources
 * @mod:	kernel module associated with the patched object
 *		(NULL for vmlinux)
@@ -100,6 +125,7 @@ struct klp_object {
	/* external */
	const char *name;
	struct klp_func *funcs;
	struct klp_callbacks callbacks;

	/* internal */
	struct kobject kobj;
+41 −10
Original line number Diff line number Diff line
@@ -54,11 +54,6 @@ 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;
}

/* sets obj->mod if object is not vmlinux and module is found */
static void klp_find_object_module(struct klp_object *obj)
{
@@ -285,6 +280,8 @@ static int klp_write_object_relocations(struct module *pmod,

static int __klp_disable_patch(struct klp_patch *patch)
{
	struct klp_object *obj;

	if (klp_transition_patch)
		return -EBUSY;

@@ -295,6 +292,10 @@ static int __klp_disable_patch(struct klp_patch *patch)

	klp_init_transition(patch, KLP_UNPATCHED);

	klp_for_each_object(patch, obj)
		if (patch->enabled && obj->patched)
			klp_pre_unpatch_callback(obj);

	/*
	 * Enforce the order of the func->transition writes in
	 * klp_init_transition() and the TIF_PATCH_PENDING writes in
@@ -388,13 +389,18 @@ static int __klp_enable_patch(struct klp_patch *patch)
		if (!klp_is_object_loaded(obj))
			continue;

		ret = klp_patch_object(obj);
		ret = klp_pre_patch_callback(obj);
		if (ret) {
			pr_warn("failed to enable patch '%s'\n",
				patch->mod->name);
			pr_warn("pre-patch callback failed for object '%s'\n",
				klp_is_module(obj) ? obj->name : "vmlinux");
			goto err;
		}

			klp_cancel_transition();
			return ret;
		ret = klp_patch_object(obj);
		if (ret) {
			pr_warn("failed to patch object '%s'\n",
				klp_is_module(obj) ? obj->name : "vmlinux");
			goto err;
		}
	}

@@ -403,6 +409,11 @@ static int __klp_enable_patch(struct klp_patch *patch)
	patch->enabled = true;

	return 0;
err:
	pr_warn("failed to enable patch '%s'\n", patch->mod->name);

	klp_cancel_transition();
	return ret;
}

/**
@@ -871,13 +882,27 @@ int klp_module_coming(struct module *mod)
			pr_notice("applying patch '%s' to loading module '%s'\n",
				  patch->mod->name, obj->mod->name);

			ret = klp_pre_patch_callback(obj);
			if (ret) {
				pr_warn("pre-patch callback failed for object '%s'\n",
					obj->name);
				goto err;
			}

			ret = klp_patch_object(obj);
			if (ret) {
				pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
					patch->mod->name, obj->mod->name, ret);

				if (patch != klp_transition_patch)
					klp_post_unpatch_callback(obj);

				goto err;
			}

			if (patch != klp_transition_patch)
				klp_post_patch_callback(obj);

			break;
		}
	}
@@ -927,9 +952,15 @@ void klp_module_going(struct module *mod)
			 * is in transition.
			 */
			if (patch->enabled || patch == klp_transition_patch) {

				if (patch != klp_transition_patch)
					klp_pre_unpatch_callback(obj);

				pr_notice("reverting patch '%s' on unloading module '%s'\n",
					  patch->mod->name, obj->mod->name);
				klp_unpatch_object(obj);

				klp_post_unpatch_callback(obj);
			}

			klp_free_object_loaded(obj);
+38 −0
Original line number Diff line number Diff line
#ifndef _LIVEPATCH_CORE_H
#define _LIVEPATCH_CORE_H

#include <linux/livepatch.h>

extern struct mutex klp_mutex;

static inline bool klp_is_object_loaded(struct klp_object *obj)
{
	return !obj->name || obj->mod;
}

static inline int klp_pre_patch_callback(struct klp_object *obj)
{
	int ret;

	ret = (obj->callbacks.pre_patch) ?
		(*obj->callbacks.pre_patch)(obj) : 0;

	obj->callbacks.post_unpatch_enabled = !ret;

	return ret;
}

static inline void klp_post_patch_callback(struct klp_object *obj)
{
	if (obj->callbacks.post_patch)
		(*obj->callbacks.post_patch)(obj);
}

static inline void klp_pre_unpatch_callback(struct klp_object *obj)
{
	if (obj->callbacks.pre_unpatch)
		(*obj->callbacks.pre_unpatch)(obj);
}

static inline void klp_post_unpatch_callback(struct klp_object *obj)
{
	if (obj->callbacks.post_unpatch_enabled &&
	    obj->callbacks.post_unpatch)
		(*obj->callbacks.post_unpatch)(obj);
}

#endif /* _LIVEPATCH_CORE_H */
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/bug.h>
#include <linux/printk.h>
#include "core.h"
#include "patch.h"
#include "transition.h"

Loading