2020-01-17 15:05:05

by Petr Mladek

[permalink] [raw]
Subject: [POC 00/23] livepatch: Split livepatch module per-object

Hi,

first, do not get scared by the size of the patchset. There are only
few patches that are really complicated and need attention at this
stage. I just wanted to split it as much as possible to review
and discuss each change separately.

Now to the problem. There are long term complains about maintainability
of the arch-specific code that is needed to livepatch modules that
are loaded after the livepatch itself.

There was always an idea about splitting the livepatch module
per-livepatched object. One interesting approach was drafted
on the last Livepatch microconference at Linux Plubmers 2019.

I played with the idea and came up with this POC. Of course,
there are pros and cons.

On the positive note:

+ The approach seems to work.

+ The same scenarios are supported. It is even newly possible to use
the livepatch-specific relocations and reload the livepatched module.

+ The livepatch-specific relocations are still needed but
they are handled together with other relocations. As
a result, the other code modifications work out of box,
e.g. alternatives, parainstructions.

+ Some problematic code could get removed (last 4 patches):

+ module_disable_ro()
+ arch_klp_init_object_loaded()
+ copy_module_elf()

+ The amount if livepatch-specific hooks in the module loader
is about the same. They are _not_ longer arch-specific. But
they are a bit tricky, see negatives below.


On the negative side:

+ It adds dependency on userspace tool "modprobe" called
via usermodhelper. It brings several new problems:

+ How to distinguish modprobe called by user or by kernel
when resolving races and errors.

+ How to pass the real error code to the usermodhelper caller.

+ Automatic dependencies are generated and handled in
the userspace. Might create unwanted cyclic load.
It requires crazy workarounds from the kernel side,
see the patch 19.

+ There is a new bunch of races that sometimes need a tricky
solution. For example, see the patches 8, 9, 15.

+ It might be slightly more complicated to prepare and use
the livepatches. There are more modules that need to built
and are visibly to the administrators. Also it complicates
sharing some common helper functionality.


From my point of view. The new code is much less arch-dependent
and more self-contained. Therefore it should be easier to maintain
in the long term.

On the other hand, it is more tricky regarding possible races and
infinite loops. They are not always easy to solve because of
"modprobe" called via userspace and because of more switches
between klp_mutex and module_mutex guarded code. Anyway, once
this is solved, it should just work for a long time as is.

All in all, I think that this approach is worth exploring.
I am curious about your opinion.

Best Regards,
Petr

PS: The patchset applies against Linus' master (v5.5-rc6).

Petr Mladek (23):
module: Allow to delete module also from inside kernel
livepatch: Split livepatch modules per livepatched object
livepatch: Better checks of struct klp_object definition
livepatch: Prevent loading livepatch sub-module unintentionally.
livepatch: Initialize and free livepatch submodule
livepatch: Enable the livepatch submodule
livepatch: Remove obsolete functionality from klp_module_coming()
livepatch: Automatically load livepatch module when the patch module
is loaded
livepatch: Handle race when livepatches are reloaded during a module
load
livepatch: Handle modprobe exit code
livepatch: Safely detect forced transition when removing split
livepatch modules
livepatch: Automatically remove livepatch module when the object is
freed
livepatch: Remove livepatch module when the livepatched module is
unloaded
livepatch: Never block livepatch modules when the related module is
being removed
livepatch: Prevent infinite loop when loading livepatch module
livepatch: Add patch into the global list early
livepatch: Load livepatches for modules when loading the main
livepatch
module: Refactor add_unformed_module()
module/livepatch: Allow to use exported symbols from livepatch module
for "vmlinux"
module/livepatch: Relocate local variables in the module loaded when
the livepatch is being loaded
livepatch: Remove obsolete arch_klp_init_object_loaded()
livepatch/module: Remove obsolete copy_module_elf()
module: Remove obsolete module_disable_ro()

Documentation/livepatch/module-elf-format.rst | 15 +-
arch/x86/kernel/Makefile | 1 -
arch/x86/kernel/livepatch.c | 53 --
include/linux/livepatch.h | 36 +-
include/linux/module.h | 10 +-
kernel/livepatch/core.c | 743 ++++++++++++++-------
kernel/livepatch/core.h | 5 -
kernel/livepatch/transition.c | 22 +-
kernel/module.c | 279 ++++----
lib/livepatch/Makefile | 2 +
lib/livepatch/test_klp_atomic_replace.c | 18 +-
lib/livepatch/test_klp_callbacks_demo.c | 90 ++-
lib/livepatch/test_klp_callbacks_demo.h | 11 +
lib/livepatch/test_klp_callbacks_demo2.c | 62 +-
lib/livepatch/test_klp_callbacks_demo2.h | 11 +
...t_klp_callbacks_demo__test_klp_callbacks_busy.c | 50 ++
...st_klp_callbacks_demo__test_klp_callbacks_mod.c | 42 ++
lib/livepatch/test_klp_livepatch.c | 18 +-
lib/livepatch/test_klp_state.c | 53 +-
lib/livepatch/test_klp_state2.c | 53 +-
samples/livepatch/Makefile | 4 +
samples/livepatch/livepatch-callbacks-demo.c | 90 ++-
samples/livepatch/livepatch-callbacks-demo.h | 11 +
...h-callbacks-demo__livepatch-callbacks-busymod.c | 54 ++
...patch-callbacks-demo__livepatch-callbacks-mod.c | 46 ++
samples/livepatch/livepatch-sample.c | 18 +-
samples/livepatch/livepatch-shadow-fix1.c | 120 +---
.../livepatch-shadow-fix1__livepatch-shadow-mod.c | 155 +++++
samples/livepatch/livepatch-shadow-fix2.c | 92 +--
.../livepatch-shadow-fix2__livepatch-shadow-mod.c | 127 ++++
.../testing/selftests/livepatch/test-callbacks.sh | 19 +-
31 files changed, 1424 insertions(+), 886 deletions(-)
delete mode 100644 arch/x86/kernel/livepatch.c
create mode 100644 lib/livepatch/test_klp_callbacks_demo.h
create mode 100644 lib/livepatch/test_klp_callbacks_demo2.h
create mode 100644 lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_busy.c
create mode 100644 lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_mod.c
create mode 100644 samples/livepatch/livepatch-callbacks-demo.h
create mode 100644 samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-busymod.c
create mode 100644 samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-mod.c
create mode 100644 samples/livepatch/livepatch-shadow-fix1__livepatch-shadow-mod.c
create mode 100644 samples/livepatch/livepatch-shadow-fix2__livepatch-shadow-mod.c

--
2.16.4


2020-01-17 15:05:13

by Petr Mladek

[permalink] [raw]
Subject: [POC 01/23] module: Allow to delete module also from inside kernel

Livepatch subsystems will need to automatically load and delete
livepatch module when the livepatched one is being removed
or when the entire livepatch is being removed.

The code stopping the kernel module is moved to separate function
so that it can be reused.

The function always have rights to do the action. Also it does not
need to search for struct module because it is already passed as
a parameter.

On the other hand, it has to make sure that the given struct module
can't be released in parallel. It is achieved by combining module_put()
and module_delete() functionality in a single function.

This patch does not change the existing behavior of delete_module
syscall.

Signed-off-by: Petr Mladek <[email protected]>

module: Add module_put_and_delete()
---
include/linux/module.h | 5 +++
kernel/module.c | 119 +++++++++++++++++++++++++++++++------------------
2 files changed, 80 insertions(+), 44 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index bd165ba68617..f69f3fd72dd5 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -623,6 +623,7 @@ extern void __module_get(struct module *module);
extern bool try_module_get(struct module *module);

extern void module_put(struct module *module);
+extern int module_put_and_delete(struct module *mod);

#else /*!CONFIG_MODULE_UNLOAD*/
static inline bool try_module_get(struct module *module)
@@ -632,6 +633,10 @@ static inline bool try_module_get(struct module *module)
static inline void module_put(struct module *module)
{
}
+static inline int module_put_and_delete(struct module *mod)
+{
+ return 0;
+}
static inline void __module_get(struct module *module)
{
}
diff --git a/kernel/module.c b/kernel/module.c
index b56f3224b161..b3f11524f8f9 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -964,62 +964,36 @@ EXPORT_SYMBOL(module_refcount);
/* This exists whether we can unload or not */
static void free_module(struct module *mod);

-SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
- unsigned int, flags)
+int stop_module(struct module *mod, int flags)
{
- struct module *mod;
- char name[MODULE_NAME_LEN];
- int ret, forced = 0;
-
- if (!capable(CAP_SYS_MODULE) || modules_disabled)
- return -EPERM;
-
- if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
- return -EFAULT;
- name[MODULE_NAME_LEN-1] = '\0';
+ int forced = 0;

- audit_log_kern_module(name);
-
- if (mutex_lock_interruptible(&module_mutex) != 0)
- return -EINTR;
-
- mod = find_module(name);
- if (!mod) {
- ret = -ENOENT;
- goto out;
- }
-
- if (!list_empty(&mod->source_list)) {
- /* Other modules depend on us: get rid of them first. */
- ret = -EWOULDBLOCK;
- goto out;
- }
+ /* Other modules depend on us: get rid of them first. */
+ if (!list_empty(&mod->source_list))
+ return -EWOULDBLOCK;

/* Doing init or already dying? */
if (mod->state != MODULE_STATE_LIVE) {
/* FIXME: if (force), slam module count damn the torpedoes */
pr_debug("%s already dying\n", mod->name);
- ret = -EBUSY;
- goto out;
+ return -EBUSY;
}

/* If it has an init func, it must have an exit func to unload */
if (mod->init && !mod->exit) {
forced = try_force_unload(flags);
- if (!forced) {
- /* This module can't be removed */
- ret = -EBUSY;
- goto out;
- }
+ /* This module can't be removed */
+ if (!forced)
+ return -EBUSY;
}

/* Stop the machine so refcounts can't move and disable module. */
- ret = try_stop_module(mod, flags, &forced);
- if (ret != 0)
- goto out;
+ return try_stop_module(mod, flags, &forced);
+}

- mutex_unlock(&module_mutex);
- /* Final destruction now no one is using it. */
+/* Final destruction now no one is using it. */
+static void destruct_module(struct module *mod)
+{
if (mod->exit != NULL)
mod->exit();
blocking_notifier_call_chain(&module_notify_list,
@@ -1033,8 +1007,44 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));

free_module(mod);
+
/* someone could wait for the module in add_unformed_module() */
wake_up_all(&module_wq);
+}
+
+SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
+ unsigned int, flags)
+{
+ struct module *mod;
+ char name[MODULE_NAME_LEN];
+ int ret;
+
+ if (!capable(CAP_SYS_MODULE) || modules_disabled)
+ return -EPERM;
+
+ if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
+ return -EFAULT;
+ name[MODULE_NAME_LEN-1] = '\0';
+
+ audit_log_kern_module(name);
+
+ if (mutex_lock_interruptible(&module_mutex) != 0)
+ return -EINTR;
+
+ mod = find_module(name);
+ if (!mod) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ ret = stop_module(mod, flags);
+ if (ret)
+ goto out;
+
+ mutex_unlock(&module_mutex);
+
+/* Final destruction now no one is using it. */
+ destruct_module(mod);
return 0;
out:
mutex_unlock(&module_mutex);
@@ -1138,20 +1148,41 @@ bool try_module_get(struct module *module)
}
EXPORT_SYMBOL(try_module_get);

-void module_put(struct module *module)
+/* Must be called under module_mutex or with preemtion disabled */
+static void __module_put(struct module* module)
{
int ret;

+ ret = atomic_dec_if_positive(&module->refcnt);
+ WARN_ON(ret < 0); /* Failed to put refcount */
+ trace_module_put(module, _RET_IP_);
+}
+
+void module_put(struct module *module)
+{
if (module) {
preempt_disable();
- ret = atomic_dec_if_positive(&module->refcnt);
- WARN_ON(ret < 0); /* Failed to put refcount */
- trace_module_put(module, _RET_IP_);
+ __module_put(module);
preempt_enable();
}
}
EXPORT_SYMBOL(module_put);

+int module_put_and_delete(struct module *mod)
+{
+ int ret;
+ mutex_lock(&module_mutex);
+ __module_put(mod);
+ ret = stop_module(mod, 0);
+ mutex_unlock(&module_mutex);
+
+ if (ret)
+ return ret;
+
+ destruct_module(mod);
+ return 0;
+}
+
#else /* !CONFIG_MODULE_UNLOAD */
static inline void print_unload_info(struct seq_file *m, struct module *mod)
{
--
2.16.4

2020-01-17 15:05:16

by Petr Mladek

[permalink] [raw]
Subject: [POC 02/23] livepatch: Split livepatch modules per livepatched object

One livepatch module allows to fix vmlinux and any number of modules
while providing some guarantees defined by the consistency model.

The patched modules can be loaded at any time: before, during,
or after the livepatch module gets loaded. They can even get
removed and loaded again. This variety of scenarios bring some
troubles. For example, some livepatch symbols could be relocated
only after the related module gets loaded. These changes need
to get cleared when the module gets unloaded so that it can
get eventually loaded again.

As a result some functionality needs to be duplicated by
the livepatching code. Some elf sections need to be preserved
even when they normally can be removed during the module load.
Architecture specific code is involved which makes harder
adding support for new architectures and the maintainace.

The solution is to split the livepatch module per livepatched
object (vmlinux or module). Then both livepatch module and
the livepatched modules could get loaded and removed at the
same time.

This require many changes in the livepatch subsystem, module
loader, sample livepatches and livepatches needed for selftests.

The bad news is that bisection will not work by definition.
The good news is that it allows to do the changes in smaller
steps.

The first step allows to split the existing sample and testing
modules so that they can be later user. It is done by
the following changes:

1. struct klp_patch:

+ Add "patch_name" and "obj_names" to match all the related
livepatch modules.

+ Replace "objs" array with a pointer to a single struct object.

+ move "mod" to struct object.

2. struct klp_object:

+ Add "patch_name" to match all the related livepatch modules.

+ "mod" points to the livepatch module instead of the livepatched
one. The pointer to the livepatched module was used only to
detect whether it was loaded. It will be always loaded
with related livepatch module now.

3. klp_find_object_module() and klp_is_object_loaded() are no longer
needed. Livepatch module is loaded only when the related livepatched
module is loaded.

4. Add klp_add_object() function that will need to initialize
struct object, link it into the related struct klp_patch,
and patch the functions. It will get implemented later.

The livepatches for modules are put into separate source files
that define only struct klp_object() and call the new klp_add_object()
in the init() callback. The name of the module follows the pattern:

<patch_name>__<object_name>

Signed-off-by: Petr Mladek <[email protected]>
---
arch/x86/kernel/livepatch.c | 5 +-
include/linux/livepatch.h | 20 +--
kernel/livepatch/core.c | 139 +++++++-----------
kernel/livepatch/core.h | 5 -
kernel/livepatch/transition.c | 14 +-
lib/livepatch/Makefile | 2 +
lib/livepatch/test_klp_atomic_replace.c | 18 ++-
lib/livepatch/test_klp_callbacks_demo.c | 90 ++++++------
lib/livepatch/test_klp_callbacks_demo.h | 11 ++
lib/livepatch/test_klp_callbacks_demo2.c | 62 ++++++---
lib/livepatch/test_klp_callbacks_demo2.h | 11 ++
...t_klp_callbacks_demo__test_klp_callbacks_busy.c | 50 +++++++
...st_klp_callbacks_demo__test_klp_callbacks_mod.c | 42 ++++++
lib/livepatch/test_klp_livepatch.c | 18 ++-
lib/livepatch/test_klp_state.c | 53 ++++---
lib/livepatch/test_klp_state2.c | 53 ++++---
samples/livepatch/Makefile | 4 +
samples/livepatch/livepatch-callbacks-demo.c | 90 ++++++------
samples/livepatch/livepatch-callbacks-demo.h | 11 ++
...h-callbacks-demo__livepatch-callbacks-busymod.c | 54 +++++++
...patch-callbacks-demo__livepatch-callbacks-mod.c | 46 ++++++
samples/livepatch/livepatch-sample.c | 18 ++-
samples/livepatch/livepatch-shadow-fix1.c | 120 ++--------------
.../livepatch-shadow-fix1__livepatch-shadow-mod.c | 155 +++++++++++++++++++++
samples/livepatch/livepatch-shadow-fix2.c | 92 ++----------
.../livepatch-shadow-fix2__livepatch-shadow-mod.c | 127 +++++++++++++++++
.../testing/selftests/livepatch/test-callbacks.sh | 16 +--
27 files changed, 841 insertions(+), 485 deletions(-)
create mode 100644 lib/livepatch/test_klp_callbacks_demo.h
create mode 100644 lib/livepatch/test_klp_callbacks_demo2.h
create mode 100644 lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_busy.c
create mode 100644 lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_mod.c
create mode 100644 samples/livepatch/livepatch-callbacks-demo.h
create mode 100644 samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-busymod.c
create mode 100644 samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-mod.c
create mode 100644 samples/livepatch/livepatch-shadow-fix1__livepatch-shadow-mod.c
create mode 100644 samples/livepatch/livepatch-shadow-fix2__livepatch-shadow-mod.c

diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c
index 6a68e41206e7..728b44eaa168 100644
--- a/arch/x86/kernel/livepatch.c
+++ b/arch/x86/kernel/livepatch.c
@@ -9,8 +9,7 @@
#include <asm/text-patching.h>

/* Apply per-object alternatives. Based on x86 module_finalize() */
-void arch_klp_init_object_loaded(struct klp_patch *patch,
- struct klp_object *obj)
+void arch_klp_init_object_loaded(struct klp_object *obj)
{
int cnt;
struct klp_modinfo *info;
@@ -20,7 +19,7 @@ void arch_klp_init_object_loaded(struct klp_patch *patch,
char sec_objname[MODULE_NAME_LEN];
char secname[KSYM_NAME_LEN];

- info = patch->mod->klp_info;
+ info = obj->mod->klp_info;
objname = obj->name ? obj->name : "vmlinux";

/* See livepatch core code for BUILD_BUG_ON() explanation */
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index e894e74905f3..a4567c17a9f2 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -105,19 +105,21 @@ struct klp_callbacks {
/**
* struct klp_object - kernel object structure for live patching
* @name: module name (or NULL for vmlinux)
+ * @patch_name: module name for vmlinux
+ * @mod: reference to the live patch module for this object
* @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
* @func_list: dynamic list of the function entries
* @node: list node for klp_patch obj_list
- * @mod: kernel module associated with the patched object
- * (NULL for vmlinux)
* @dynamic: temporary object for nop functions; dynamically allocated
* @patched: the object's funcs have been added to the klp_ops list
*/
struct klp_object {
/* external */
const char *name;
+ const char *patch_name;
+ struct module *mod;
struct klp_func *funcs;
struct klp_callbacks callbacks;

@@ -125,7 +127,6 @@ struct klp_object {
struct kobject kobj;
struct list_head func_list;
struct list_head node;
- struct module *mod;
bool dynamic;
bool patched;
};
@@ -144,8 +145,9 @@ struct klp_state {

/**
* struct klp_patch - patch structure for live patching
- * @mod: reference to the live patch module
- * @objs: object entries for kernel objects to be patched
+ * @patch_name: livepatch name; same for related livepatch against other objects
+ * @objs: object entry for vmlinux object
+ * @obj_names: names of modules synchronously livepatched with this patch
* @states: system states that can get modified
* @replace: replace all actively used patches
* @list: list node for global list of actively used patches
@@ -158,9 +160,9 @@ struct klp_state {
*/
struct klp_patch {
/* external */
- struct module *mod;
- struct klp_object *objs;
struct klp_state *states;
+ struct klp_object *obj;
+ char **obj_names;
bool replace;

/* internal */
@@ -194,9 +196,9 @@ struct klp_patch {
list_for_each_entry(func, &obj->func_list, node)

int klp_enable_patch(struct klp_patch *);
+int klp_add_object(struct klp_object *);

-void arch_klp_init_object_loaded(struct klp_patch *patch,
- struct klp_object *obj);
+void arch_klp_init_object_loaded(struct klp_object *obj);

/* Called from the module loader during module coming/going states */
int klp_module_coming(struct module *mod);
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index c3512e7e0801..bb62c5407b75 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -49,34 +49,6 @@ static bool klp_is_module(struct klp_object *obj)
return obj->name;
}

-/* sets obj->mod if object is not vmlinux and module is found */
-static void klp_find_object_module(struct klp_object *obj)
-{
- struct module *mod;
-
- if (!klp_is_module(obj))
- return;
-
- mutex_lock(&module_mutex);
- /*
- * We do not want to block removal of patched modules and therefore
- * we do not take a reference here. The patches are removed by
- * klp_module_going() instead.
- */
- mod = find_module(obj->name);
- /*
- * Do not mess work of klp_module_coming() and klp_module_going().
- * Note that the patch might still be needed before klp_module_going()
- * is called. Module functions can be called even in the GOING state
- * until mod->exit() finishes. This is especially important for
- * patches that modify semantic of the functions.
- */
- if (mod && mod->klp_alive)
- obj->mod = mod;
-
- mutex_unlock(&module_mutex);
-}
-
static bool klp_initialized(void)
{
return !!klp_root_kobj;
@@ -246,18 +218,16 @@ static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod)
return 0;
}

-static int klp_write_object_relocations(struct module *pmod,
- struct klp_object *obj)
+static int klp_write_object_relocations(struct klp_object *obj)
{
int i, cnt, ret = 0;
const char *objname, *secname;
char sec_objname[MODULE_NAME_LEN];
+ struct module *pmod;
Elf_Shdr *sec;

- if (WARN_ON(!klp_is_object_loaded(obj)))
- return -EINVAL;
-
objname = klp_is_module(obj) ? obj->name : "vmlinux";
+ pmod = obj->mod;

/* For each klp relocation section */
for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) {
@@ -419,8 +389,8 @@ static void klp_free_object_dynamic(struct klp_object *obj)

static void klp_init_func_early(struct klp_object *obj,
struct klp_func *func);
-static void klp_init_object_early(struct klp_patch *patch,
- struct klp_object *obj);
+static int klp_init_object_early(struct klp_patch *patch,
+ struct klp_object *obj);

static struct klp_object *klp_alloc_object_dynamic(const char *name,
struct klp_patch *patch)
@@ -662,7 +632,7 @@ static void klp_free_patch_finish(struct klp_patch *patch)

/* Put the module after the last access to struct klp_patch. */
if (!patch->forced)
- module_put(patch->mod);
+ module_put(patch->obj->mod);
}

/*
@@ -725,30 +695,28 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
}

/* Arches may override this to finish any remaining arch-specific tasks */
-void __weak arch_klp_init_object_loaded(struct klp_patch *patch,
- struct klp_object *obj)
+void __weak arch_klp_init_object_loaded(struct klp_object *obj)
{
}

/* parts of the initialization that is done only when the object is loaded */
-static int klp_init_object_loaded(struct klp_patch *patch,
- struct klp_object *obj)
+static int klp_init_object_loaded(struct klp_object *obj)
{
struct klp_func *func;
int ret;

mutex_lock(&text_mutex);

- module_disable_ro(patch->mod);
- ret = klp_write_object_relocations(patch->mod, obj);
+ module_disable_ro(obj->mod);
+ ret = klp_write_object_relocations(obj);
if (ret) {
- module_enable_ro(patch->mod, true);
+ module_enable_ro(obj->mod, true);
mutex_unlock(&text_mutex);
return ret;
}

- arch_klp_init_object_loaded(patch, obj);
- module_enable_ro(patch->mod, true);
+ arch_klp_init_object_loaded(obj);
+ module_enable_ro(obj->mod, true);

mutex_unlock(&text_mutex);

@@ -792,11 +760,8 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
return -EINVAL;

obj->patched = false;
- obj->mod = NULL;

- klp_find_object_module(obj);
-
- name = klp_is_module(obj) ? obj->name : "vmlinux";
+ name = obj->name ? obj->name : "vmlinux";
ret = kobject_add(&obj->kobj, &patch->kobj, "%s", name);
if (ret)
return ret;
@@ -807,8 +772,7 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
return ret;
}

- if (klp_is_object_loaded(obj))
- ret = klp_init_object_loaded(patch, obj);
+ ret = klp_init_object_loaded(obj);

return ret;
}
@@ -820,20 +784,34 @@ static void klp_init_func_early(struct klp_object *obj,
list_add_tail(&func->node, &obj->func_list);
}

-static void klp_init_object_early(struct klp_patch *patch,
+static int klp_init_object_early(struct klp_patch *patch,
struct klp_object *obj)
{
+ struct klp_func *func;
+
+ if (!obj->funcs)
+ return -EINVAL;
+
INIT_LIST_HEAD(&obj->func_list);
kobject_init(&obj->kobj, &klp_ktype_object);
list_add_tail(&obj->node, &patch->obj_list);
+
+ klp_for_each_func_static(obj, func) {
+ klp_init_func_early(obj, func);
+ }
+
+ if (obj->dynamic || try_module_get(obj->mod))
+ return 0;
+
+ return -ENODEV;
}

static int klp_init_patch_early(struct klp_patch *patch)
{
- struct klp_object *obj;
- struct klp_func *func;
+ struct klp_object *obj = patch->obj;

- if (!patch->objs)
+ /* Main patch module is always for vmlinux */
+ if (obj->name)
return -EINVAL;

INIT_LIST_HEAD(&patch->list);
@@ -844,21 +822,7 @@ static int klp_init_patch_early(struct klp_patch *patch)
INIT_WORK(&patch->free_work, klp_free_patch_work_fn);
init_completion(&patch->finish);

- klp_for_each_object_static(patch, obj) {
- if (!obj->funcs)
- return -EINVAL;
-
- klp_init_object_early(patch, obj);
-
- klp_for_each_func_static(obj, func) {
- klp_init_func_early(obj, func);
- }
- }
-
- if (!try_module_get(patch->mod))
- return -ENODEV;
-
- return 0;
+ return klp_init_object_early(patch, obj);
}

static int klp_init_patch(struct klp_patch *patch)
@@ -866,7 +830,7 @@ static int klp_init_patch(struct klp_patch *patch)
struct klp_object *obj;
int ret;

- ret = kobject_add(&patch->kobj, klp_root_kobj, "%s", patch->mod->name);
+ ret = kobject_add(&patch->kobj, klp_root_kobj, "%s", patch->obj->mod->name);
if (ret)
return ret;

@@ -887,6 +851,12 @@ static int klp_init_patch(struct klp_patch *patch)
return 0;
}

+int klp_add_object(struct klp_object *obj)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(klp_add_object);
+
static int __klp_disable_patch(struct klp_patch *patch)
{
struct klp_object *obj;
@@ -930,7 +900,7 @@ static int __klp_enable_patch(struct klp_patch *patch)
if (WARN_ON(patch->enabled))
return -EINVAL;

- pr_notice("enabling patch '%s'\n", patch->mod->name);
+ pr_notice("enabling patch '%s'\n", patch->obj->patch_name);

klp_init_transition(patch, KLP_PATCHED);

@@ -944,9 +914,6 @@ static int __klp_enable_patch(struct klp_patch *patch)
smp_wmb();

klp_for_each_object(patch, obj) {
- if (!klp_is_object_loaded(obj))
- continue;
-
ret = klp_pre_patch_callback(obj);
if (ret) {
pr_warn("pre-patch callback failed for object '%s'\n",
@@ -968,7 +935,7 @@ static int __klp_enable_patch(struct klp_patch *patch)

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

klp_cancel_transition();
return ret;
@@ -991,12 +958,12 @@ int klp_enable_patch(struct klp_patch *patch)
{
int ret;

- if (!patch || !patch->mod)
+ if (!patch || !patch->obj || !patch->obj->mod)
return -EINVAL;

- if (!is_livepatch_module(patch->mod)) {
+ if (!is_livepatch_module(patch->obj->mod)) {
pr_err("module %s is not marked as a livepatch module\n",
- patch->mod->name);
+ patch->obj->patch_name);
return -EINVAL;
}

@@ -1012,7 +979,7 @@ int klp_enable_patch(struct klp_patch *patch)

if (!klp_is_patch_compatible(patch)) {
pr_err("Livepatch patch (%s) is not compatible with the already installed livepatches.\n",
- patch->mod->name);
+ patch->obj->mod->name);
mutex_unlock(&klp_mutex);
return -EINVAL;
}
@@ -1119,7 +1086,7 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
klp_pre_unpatch_callback(obj);

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

klp_post_unpatch_callback(obj);
@@ -1154,15 +1121,15 @@ int klp_module_coming(struct module *mod)

obj->mod = mod;

- ret = klp_init_object_loaded(patch, obj);
+ ret = klp_init_object_loaded(obj);
if (ret) {
pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
- patch->mod->name, obj->mod->name, ret);
+ patch->obj->patch_name, obj->name, ret);
goto err;
}

pr_notice("applying patch '%s' to loading module '%s'\n",
- patch->mod->name, obj->mod->name);
+ patch->obj->patch_name, obj->name);

ret = klp_pre_patch_callback(obj);
if (ret) {
@@ -1174,7 +1141,7 @@ int klp_module_coming(struct module *mod)
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);
+ patch->obj->patch_name, obj->name, ret);

klp_post_unpatch_callback(obj);
goto err;
@@ -1197,7 +1164,7 @@ int klp_module_coming(struct module *mod)
* error to the module loader.
*/
pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n",
- patch->mod->name, obj->mod->name, obj->mod->name);
+ patch->obj->patch_name, obj->name, obj->name);
mod->klp_alive = false;
obj->mod = NULL;
klp_cleanup_module_patches_limited(mod, patch);
diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h
index 38209c7361b6..01980cc0509b 100644
--- a/kernel/livepatch/core.h
+++ b/kernel/livepatch/core.h
@@ -18,11 +18,6 @@ void klp_free_replaced_patches_async(struct klp_patch *new_patch);
void klp_unpatch_replaced_patches(struct klp_patch *new_patch);
void klp_discard_nops(struct klp_patch *new_patch);

-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 = 0;
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index f6310f848f34..78e3280560cd 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -74,7 +74,7 @@ static void klp_complete_transition(void)
unsigned int cpu;

pr_debug("'%s': completing %s transition\n",
- klp_transition_patch->mod->name,
+ klp_transition_patch->obj->patch_name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");

if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
@@ -120,15 +120,13 @@ static void klp_complete_transition(void)
}

klp_for_each_object(klp_transition_patch, obj) {
- if (!klp_is_object_loaded(obj))
- continue;
if (klp_target_state == KLP_PATCHED)
klp_post_patch_callback(obj);
else if (klp_target_state == KLP_UNPATCHED)
klp_post_unpatch_callback(obj);
}

- pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
+ pr_notice("'%s': %s complete\n", klp_transition_patch->obj->patch_name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");

klp_target_state = KLP_UNDEFINED;
@@ -147,7 +145,7 @@ void klp_cancel_transition(void)
return;

pr_debug("'%s': canceling patching transition, going to unpatch\n",
- klp_transition_patch->mod->name);
+ klp_transition_patch->obj->patch_name);

klp_target_state = KLP_UNPATCHED;
klp_complete_transition();
@@ -468,7 +466,7 @@ void klp_start_transition(void)
WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED);

pr_notice("'%s': starting %s transition\n",
- klp_transition_patch->mod->name,
+ klp_transition_patch->obj->patch_name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");

/*
@@ -519,7 +517,7 @@ void klp_init_transition(struct klp_patch *patch, int state)
*/
klp_target_state = state;

- pr_debug("'%s': initializing %s transition\n", patch->mod->name,
+ pr_debug("'%s': initializing %s transition\n", patch->obj->patch_name,
klp_target_state == KLP_PATCHED ? "patching" : "unpatching");

/*
@@ -581,7 +579,7 @@ void klp_reverse_transition(void)
struct task_struct *g, *task;

pr_debug("'%s': reversing transition from %s\n",
- klp_transition_patch->mod->name,
+ klp_transition_patch->obj->patch_name,
klp_target_state == KLP_PATCHED ? "patching to unpatching" :
"unpatching to patching");

diff --git a/lib/livepatch/Makefile b/lib/livepatch/Makefile
index 295b94bff370..812da987da42 100644
--- a/lib/livepatch/Makefile
+++ b/lib/livepatch/Makefile
@@ -4,6 +4,8 @@

obj-$(CONFIG_TEST_LIVEPATCH) += test_klp_atomic_replace.o \
test_klp_callbacks_demo.o \
+ test_klp_callbacks_demo__test_klp_callbacks_busy.o \
+ test_klp_callbacks_demo__test_klp_callbacks_mod.o \
test_klp_callbacks_demo2.o \
test_klp_callbacks_busy.o \
test_klp_callbacks_mod.o \
diff --git a/lib/livepatch/test_klp_atomic_replace.c b/lib/livepatch/test_klp_atomic_replace.c
index 5af7093ca00c..bd42f9af9843 100644
--- a/lib/livepatch/test_klp_atomic_replace.c
+++ b/lib/livepatch/test_klp_atomic_replace.c
@@ -26,16 +26,20 @@ static struct klp_func funcs[] = {
}, {}
};

-static struct klp_object objs[] = {
- {
- /* name being NULL means vmlinux */
- .funcs = funcs,
- }, {}
+static struct klp_object obj = {
+ .patch_name = THIS_MODULE->name,
+ .name = NULL, /* vmlinux */
+ .mod = THIS_MODULE,
+ .funcs = funcs,
+};
+
+static char *obj_names[] = {
+ NULL
};

static struct klp_patch patch = {
- .mod = THIS_MODULE,
- .objs = objs,
+ .obj = &obj,
+ .obj_names = obj_names,
/* set .replace in the init function below for demo purposes */
};

diff --git a/lib/livepatch/test_klp_callbacks_demo.c b/lib/livepatch/test_klp_callbacks_demo.c
index 3fd8fe1cd1cc..0e51cbd4b61c 100644
--- a/lib/livepatch/test_klp_callbacks_demo.c
+++ b/lib/livepatch/test_klp_callbacks_demo.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
+#include "test_klp_callbacks_demo.h"

static int pre_patch_ret;
module_param(pre_patch_ret, int, 0644);
@@ -20,88 +21,79 @@ static const char *const module_state[] = {

static void callback_info(const char *callback, struct klp_object *obj)
{
- if (obj->mod)
- pr_info("%s: %s -> %s\n", callback, obj->mod->name,
- module_state[obj->mod->state]);
- else
+ struct module *mod;
+
+ if (!obj->name) {
pr_info("%s: vmlinux\n", callback);
+ return;
+ }
+
+ mutex_lock(&module_mutex);
+ mod = find_module(obj->name);
+ if (mod) {
+ pr_info("%s: %s -> %s\n", callback, obj->name,
+ module_state[mod->state]);
+ } else {
+ pr_err("%s: Unable to find module: %s", callback, obj->name);
+ }
+ mutex_unlock(&module_mutex);
}

/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
+int pre_patch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
return pre_patch_ret;
}
+EXPORT_SYMBOL(pre_patch_callback);

/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
+void post_patch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
+EXPORT_SYMBOL(post_patch_callback);

/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
+void pre_unpatch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
+EXPORT_SYMBOL(pre_unpatch_callback);

/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
+void post_unpatch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
-
-static void patched_work_func(struct work_struct *work)
-{
- pr_info("%s\n", __func__);
-}
+EXPORT_SYMBOL(post_unpatch_callback);

static struct klp_func no_funcs[] = {
{}
};

-static struct klp_func busymod_funcs[] = {
- {
- .old_name = "busymod_work_func",
- .new_func = patched_work_func,
- }, {}
+static struct klp_object obj = {
+ .patch_name = LIVEPATCH_NAME,
+ .name = NULL, /* vmlinux */
+ .mod = THIS_MODULE,
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
};

-static struct klp_object objs[] = {
- {
- .name = NULL, /* vmlinux */
- .funcs = no_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, {
- .name = "test_klp_callbacks_mod",
- .funcs = no_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, {
- .name = "test_klp_callbacks_busy",
- .funcs = busymod_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, { }
+static char *obj_names[] = {
+ "test_klp_callbacks_mod",
+ "test_klp_callbacks_busy",
+ NULL
};

static struct klp_patch patch = {
- .mod = THIS_MODULE,
- .objs = objs,
+ .obj = &obj,
+ .obj_names = obj_names,
};

static int test_klp_callbacks_demo_init(void)
diff --git a/lib/livepatch/test_klp_callbacks_demo.h b/lib/livepatch/test_klp_callbacks_demo.h
new file mode 100644
index 000000000000..d34a6c4b6c91
--- /dev/null
+++ b/lib/livepatch/test_klp_callbacks_demo.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#include <linux/livepatch.h>
+
+#define LIVEPATCH_NAME "test_klp_callbacks_demo"
+
+int pre_patch_callback(struct klp_object *obj);
+void post_patch_callback(struct klp_object *obj);
+void pre_unpatch_callback(struct klp_object *obj);
+void post_unpatch_callback(struct klp_object *obj);
diff --git a/lib/livepatch/test_klp_callbacks_demo2.c b/lib/livepatch/test_klp_callbacks_demo2.c
index 5417573e80af..d83711ce52e3 100644
--- a/lib/livepatch/test_klp_callbacks_demo2.c
+++ b/lib/livepatch/test_klp_callbacks_demo2.c
@@ -6,6 +6,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
+#include "test_klp_callbacks_demo2.h"

static int replace;
module_param(replace, int, 0644);
@@ -20,58 +21,77 @@ static const char *const module_state[] = {

static void callback_info(const char *callback, struct klp_object *obj)
{
- if (obj->mod)
- pr_info("%s: %s -> %s\n", callback, obj->mod->name,
- module_state[obj->mod->state]);
- else
+ struct module *mod;
+
+ if (!obj->name) {
pr_info("%s: vmlinux\n", callback);
+ return;
+ }
+
+ mutex_lock(&module_mutex);
+ mod = find_module(obj->name);
+ if (mod) {
+ pr_info("%s: %s -> %s\n", callback, obj->name,
+ module_state[mod->state]);
+ } else {
+ pr_err("%s: Unable to find module: %s", callback, obj->name);
+ }
+ mutex_unlock(&module_mutex);
}

/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
+int pre_patch_callback2(struct klp_object *obj)
{
callback_info(__func__, obj);
return 0;
}
+EXPORT_SYMBOL(pre_patch_callback2);

/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
+void post_patch_callback2(struct klp_object *obj)
{
callback_info(__func__, obj);
}
+EXPORT_SYMBOL(post_patch_callback2);

/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
+void pre_unpatch_callback2(struct klp_object *obj)
{
callback_info(__func__, obj);
}
+EXPORT_SYMBOL(pre_unpatch_callback2);

/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
+void post_unpatch_callback2(struct klp_object *obj)
{
callback_info(__func__, obj);
}
+EXPORT_SYMBOL(post_unpatch_callback2);

static struct klp_func no_funcs[] = {
{ }
};

-static struct klp_object objs[] = {
- {
- .name = NULL, /* vmlinux */
- .funcs = no_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, { }
+static struct klp_object obj = {
+ .patch_name = LIVEPATCH_NAME,
+ .name = NULL, /* vmlinux */
+ .mod = THIS_MODULE,
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback2,
+ .post_patch = post_patch_callback2,
+ .pre_unpatch = pre_unpatch_callback2,
+ .post_unpatch = post_unpatch_callback2,
+ },
+};
+
+static char *obj_names[] = {
+ NULL
};

static struct klp_patch patch = {
- .mod = THIS_MODULE,
- .objs = objs,
+ .obj = &obj,
+ .obj_names = obj_names,
/* set .replace in the init function below for demo purposes */
};

diff --git a/lib/livepatch/test_klp_callbacks_demo2.h b/lib/livepatch/test_klp_callbacks_demo2.h
new file mode 100644
index 000000000000..595da3395628
--- /dev/null
+++ b/lib/livepatch/test_klp_callbacks_demo2.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#include <linux/livepatch.h>
+
+#define LIVEPATCH_NAME "test_klp_callbacks_demo2"
+
+int pre_patch_callback2(struct klp_object *obj);
+void post_patch_callback2(struct klp_object *obj);
+void pre_unpatch_callback2(struct klp_object *obj);
+void post_unpatch_callback2(struct klp_object *obj);
diff --git a/lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_busy.c b/lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_busy.c
new file mode 100644
index 000000000000..5255cfb22d23
--- /dev/null
+++ b/lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_busy.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include "test_klp_callbacks_demo.h"
+
+static void patched_work_func(struct work_struct *work)
+{
+ pr_info("%s\n", __func__);
+}
+
+static struct klp_func busymod_funcs[] = {
+ {
+ .old_name = "busymod_work_func",
+ .new_func = patched_work_func,
+ }, {}
+};
+
+static struct klp_object obj = {
+ .patch_name = LIVEPATCH_NAME,
+ .name = "test_klp_callbacks_busy",
+ .mod = THIS_MODULE,
+ .funcs = busymod_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+};
+
+static int test_klp_callbacks_demo_init(void)
+{
+ return klp_add_object(&obj);
+}
+
+static void test_klp_callbacks_demo_exit(void)
+{
+}
+
+module_init(test_klp_callbacks_demo_init);
+module_exit(test_klp_callbacks_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+MODULE_DESCRIPTION("Livepatch test: livepatch demo");
diff --git a/lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_mod.c b/lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_mod.c
new file mode 100644
index 000000000000..f2208deecde1
--- /dev/null
+++ b/lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_mod.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include "test_klp_callbacks_demo.h"
+
+static struct klp_func no_funcs[] = {
+ {}
+};
+
+static struct klp_object obj = {
+ .patch_name = LIVEPATCH_NAME,
+ .name = "test_klp_callbacks_mod",
+ .mod = THIS_MODULE,
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+};
+
+static int test_klp_callbacks_demo_init(void)
+{
+ return klp_add_object(&obj);
+}
+
+static void test_klp_callbacks_demo_exit(void)
+{
+}
+
+module_init(test_klp_callbacks_demo_init);
+module_exit(test_klp_callbacks_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
+MODULE_AUTHOR("Joe Lawrence <[email protected]>");
+MODULE_DESCRIPTION("Livepatch test: livepatch demo");
diff --git a/lib/livepatch/test_klp_livepatch.c b/lib/livepatch/test_klp_livepatch.c
index aff08199de71..d7606d7b8367 100644
--- a/lib/livepatch/test_klp_livepatch.c
+++ b/lib/livepatch/test_klp_livepatch.c
@@ -22,16 +22,20 @@ static struct klp_func funcs[] = {
}, { }
};

-static struct klp_object objs[] = {
- {
- /* name being NULL means vmlinux */
- .funcs = funcs,
- }, { }
+struct klp_object obj = {
+ .patch_name = THIS_MODULE->name,
+ .name = NULL, /* vmlinux */
+ .mod = THIS_MODULE,
+ .funcs = funcs,
+};
+
+static char *obj_names[] = {
+ NULL
};

static struct klp_patch patch = {
- .mod = THIS_MODULE,
- .objs = objs,
+ .obj = &obj,
+ .obj_names = obj_names,
};

static int test_klp_livepatch_init(void)
diff --git a/lib/livepatch/test_klp_state.c b/lib/livepatch/test_klp_state.c
index 57a4253acb01..26e8cb86d59d 100644
--- a/lib/livepatch/test_klp_state.c
+++ b/lib/livepatch/test_klp_state.c
@@ -22,11 +22,22 @@ static const char *const module_state[] = {

static void callback_info(const char *callback, struct klp_object *obj)
{
- if (obj->mod)
- pr_info("%s: %s -> %s\n", callback, obj->mod->name,
- module_state[obj->mod->state]);
- else
+ struct module *mod;
+
+ if (!obj->name) {
pr_info("%s: vmlinux\n", callback);
+ return;
+ }
+
+ mutex_lock(&module_mutex);
+ mod = find_module(obj->name);
+ if (mod) {
+ pr_info("%s: %s -> %s\n", callback, obj->name,
+ module_state[mod->state]);
+ } else {
+ pr_err("%s: Unable to find module: %s", callback, obj->name);
+ }
+ mutex_unlock(&module_mutex);
}

static struct klp_patch patch;
@@ -118,19 +129,6 @@ static struct klp_func no_funcs[] = {
{}
};

-static struct klp_object objs[] = {
- {
- .name = NULL, /* vmlinux */
- .funcs = no_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, { }
-};
-
static struct klp_state states[] = {
{
.id = CONSOLE_LOGLEVEL_STATE,
@@ -138,9 +136,26 @@ static struct klp_state states[] = {
}, { }
};

-static struct klp_patch patch = {
+static struct klp_object obj = {
+ .patch_name = THIS_MODULE->name,
+ .name = NULL, /* vmlinux */
.mod = THIS_MODULE,
- .objs = objs,
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+};
+
+static char *obj_names[] = {
+ NULL
+};
+
+static struct klp_patch patch = {
+ .obj = &obj,
+ .obj_names = obj_names,
.states = states,
.replace = true,
};
diff --git a/lib/livepatch/test_klp_state2.c b/lib/livepatch/test_klp_state2.c
index c978ea4d5e67..4942924d5cb2 100644
--- a/lib/livepatch/test_klp_state2.c
+++ b/lib/livepatch/test_klp_state2.c
@@ -22,11 +22,22 @@ static const char *const module_state[] = {

static void callback_info(const char *callback, struct klp_object *obj)
{
- if (obj->mod)
- pr_info("%s: %s -> %s\n", callback, obj->mod->name,
- module_state[obj->mod->state]);
- else
+ struct module *mod;
+
+ if (!obj->name) {
pr_info("%s: vmlinux\n", callback);
+ return;
+ }
+
+ mutex_lock(&module_mutex);
+ mod = find_module(obj->name);
+ if (mod) {
+ pr_info("%s: %s -> %s\n", callback, obj->name,
+ module_state[mod->state]);
+ } else {
+ pr_err("%s: Unable to find module: %s", callback, obj->name);
+ }
+ mutex_unlock(&module_mutex);
}

static struct klp_patch patch;
@@ -147,19 +158,6 @@ static struct klp_func no_funcs[] = {
{}
};

-static struct klp_object objs[] = {
- {
- .name = NULL, /* vmlinux */
- .funcs = no_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, { }
-};
-
static struct klp_state states[] = {
{
.id = CONSOLE_LOGLEVEL_STATE,
@@ -167,9 +165,26 @@ static struct klp_state states[] = {
}, { }
};

-static struct klp_patch patch = {
+static struct klp_object obj = {
+ .patch_name = THIS_MODULE->name,
+ .name = NULL, /* vmlinux */
.mod = THIS_MODULE,
- .objs = objs,
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = pre_patch_callback,
+ .post_patch = post_patch_callback,
+ .pre_unpatch = pre_unpatch_callback,
+ .post_unpatch = post_unpatch_callback,
+ },
+};
+
+static char *obj_names[] = {
+ NULL
+};
+
+static struct klp_patch patch = {
+ .obj = &obj,
+ .obj_names = obj_names,
.states = states,
.replace = true,
};
diff --git a/samples/livepatch/Makefile b/samples/livepatch/Makefile
index 9f853eeb6140..922bf3fa335c 100644
--- a/samples/livepatch/Makefile
+++ b/samples/livepatch/Makefile
@@ -2,7 +2,11 @@
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-sample.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-mod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix1__livepatch-shadow-mod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-shadow-fix2__livepatch-shadow-mod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo__livepatch-callbacks-mod.o
+obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-demo__livepatch-callbacks-busymod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-mod.o
obj-$(CONFIG_SAMPLE_LIVEPATCH) += livepatch-callbacks-busymod.o
diff --git a/samples/livepatch/livepatch-callbacks-demo.c b/samples/livepatch/livepatch-callbacks-demo.c
index 11c3f4357812..d119f8ba4f3d 100644
--- a/samples/livepatch/livepatch-callbacks-demo.c
+++ b/samples/livepatch/livepatch-callbacks-demo.c
@@ -83,6 +83,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
+#include "livepatch-callbacks-demo.h"

static int pre_patch_ret;
module_param(pre_patch_ret, int, 0644);
@@ -97,88 +98,79 @@ static const char *const module_state[] = {

static void callback_info(const char *callback, struct klp_object *obj)
{
- if (obj->mod)
- pr_info("%s: %s -> %s\n", callback, obj->mod->name,
- module_state[obj->mod->state]);
- else
+ struct module *mod;
+
+ if (!obj->name) {
pr_info("%s: vmlinux\n", callback);
+ return;
+ }
+
+ mutex_lock(&module_mutex);
+ mod = find_module(obj->name);
+ if (mod) {
+ pr_info("%s: %s -> %s\n", callback, obj->name,
+ module_state[mod->state]);
+ } else {
+ pr_err("%s: Unable to find module: %s", callback, obj->name);
+ }
+ mutex_unlock(&module_mutex);
}

/* Executed on object patching (ie, patch enablement) */
-static int pre_patch_callback(struct klp_object *obj)
+int sample_pre_patch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
return pre_patch_ret;
}
+EXPORT_SYMBOL(sample_pre_patch_callback);

/* Executed on object unpatching (ie, patch disablement) */
-static void post_patch_callback(struct klp_object *obj)
+void sample_post_patch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
+EXPORT_SYMBOL(sample_post_patch_callback);

/* Executed on object unpatching (ie, patch disablement) */
-static void pre_unpatch_callback(struct klp_object *obj)
+void sample_pre_unpatch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
+EXPORT_SYMBOL(sample_pre_unpatch_callback);

/* Executed on object unpatching (ie, patch disablement) */
-static void post_unpatch_callback(struct klp_object *obj)
+void sample_post_unpatch_callback(struct klp_object *obj)
{
callback_info(__func__, obj);
}
-
-static void patched_work_func(struct work_struct *work)
-{
- pr_info("%s\n", __func__);
-}
+EXPORT_SYMBOL(sample_post_unpatch_callback);

static struct klp_func no_funcs[] = {
{ }
};

-static struct klp_func busymod_funcs[] = {
- {
- .old_name = "busymod_work_func",
- .new_func = patched_work_func,
- }, { }
+static struct klp_object obj = {
+ .patch_name = LIVEPATCH_NAME,
+ .name = NULL, /* vmlinux */
+ .mod = THIS_MODULE,
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = sample_pre_patch_callback,
+ .post_patch = sample_post_patch_callback,
+ .pre_unpatch = sample_pre_unpatch_callback,
+ .post_unpatch = sample_post_unpatch_callback,
+ },
};

-static struct klp_object objs[] = {
- {
- .name = NULL, /* vmlinux */
- .funcs = no_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, {
- .name = "livepatch_callbacks_mod",
- .funcs = no_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, {
- .name = "livepatch_callbacks_busymod",
- .funcs = busymod_funcs,
- .callbacks = {
- .pre_patch = pre_patch_callback,
- .post_patch = post_patch_callback,
- .pre_unpatch = pre_unpatch_callback,
- .post_unpatch = post_unpatch_callback,
- },
- }, { }
+static char *obj_names[] = {
+ "livepatch_callbacks_mod",
+ "livepatch_callbacks_busymod",
+ NULL
};

static struct klp_patch patch = {
- .mod = THIS_MODULE,
- .objs = objs,
+ .obj = &obj,
+ .obj_names = obj_names,
};

static int livepatch_callbacks_demo_init(void)
diff --git a/samples/livepatch/livepatch-callbacks-demo.h b/samples/livepatch/livepatch-callbacks-demo.h
new file mode 100644
index 000000000000..42b0e92f2dda
--- /dev/null
+++ b/samples/livepatch/livepatch-callbacks-demo.h
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2018 Joe Lawrence <[email protected]>
+
+#include <linux/livepatch.h>
+
+#define LIVEPATCH_NAME "livepatch_callbacks_demo"
+
+int sample_pre_patch_callback(struct klp_object *obj);
+void sample_post_patch_callback(struct klp_object *obj);
+void sample_pre_unpatch_callback(struct klp_object *obj);
+void sample_post_unpatch_callback(struct klp_object *obj);
diff --git a/samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-busymod.c b/samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-busymod.c
new file mode 100644
index 000000000000..8eb714ba59d1
--- /dev/null
+++ b/samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-busymod.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Joe Lawrence <[email protected]>
+ */
+
+/*
+ * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include "livepatch-callbacks-demo.h"
+
+static void patched_work_func(struct work_struct *work)
+{
+ pr_info("%s\n", __func__);
+}
+
+static struct klp_func busymod_funcs[] = {
+ {
+ .old_name = "busymod_work_func",
+ .new_func = patched_work_func,
+ }, { }
+};
+
+static struct klp_object obj = {
+ .patch_name = LIVEPATCH_NAME,
+ .name = "livepatch_callbacks_busymod",
+ .mod = THIS_MODULE,
+ .funcs = busymod_funcs,
+ .callbacks = {
+ .pre_patch = sample_pre_patch_callback,
+ .post_patch = sample_post_patch_callback,
+ .pre_unpatch = sample_pre_unpatch_callback,
+ .post_unpatch = sample_post_unpatch_callback,
+ },
+};
+
+static int livepatch_callbacks_demo_init(void)
+{
+ return klp_add_object(&obj);
+}
+
+static void livepatch_callbacks_demo_exit(void)
+{
+}
+
+module_init(livepatch_callbacks_demo_init);
+module_exit(livepatch_callbacks_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-mod.c b/samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-mod.c
new file mode 100644
index 000000000000..134fcf4bf69d
--- /dev/null
+++ b/samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-mod.c
@@ -0,0 +1,46 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Joe Lawrence <[email protected]>
+ */
+
+/*
+ * livepatch-callbacks-demo.c - (un)patching callbacks livepatch demo
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include "livepatch-callbacks-demo.h"
+
+static struct klp_func no_funcs[] = {
+ { }
+};
+
+static struct klp_object obj = {
+ .patch_name = LIVEPATCH_NAME,
+ .name = "livepatch_callbacks_mod",
+ .mod = THIS_MODULE,
+ .funcs = no_funcs,
+ .callbacks = {
+ .pre_patch = sample_pre_patch_callback,
+ .post_patch = sample_post_patch_callback,
+ .pre_unpatch = sample_pre_unpatch_callback,
+ .post_unpatch = sample_post_unpatch_callback,
+ },
+};
+
+static int livepatch_callbacks_demo_init(void)
+{
+ return klp_add_object(&obj);
+}
+
+static void livepatch_callbacks_demo_exit(void)
+{
+}
+
+module_init(livepatch_callbacks_demo_init);
+module_exit(livepatch_callbacks_demo_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c
index cd76d7ebe598..94b74c05c4be 100644
--- a/samples/livepatch/livepatch-sample.c
+++ b/samples/livepatch/livepatch-sample.c
@@ -43,16 +43,20 @@ static struct klp_func funcs[] = {
}, { }
};

-static struct klp_object objs[] = {
- {
- /* name being NULL means vmlinux */
- .funcs = funcs,
- }, { }
+static char *obj_names[] = {
+ NULL
};

-static struct klp_patch patch = {
+static struct klp_object obj = {
+ .patch_name = THIS_MODULE->name,
+ .name = NULL, /* vmlinux */
.mod = THIS_MODULE,
- .objs = objs,
+ .funcs = funcs,
+};
+
+static struct klp_patch patch = {
+ .obj = &obj,
+ .obj_names = obj_names,
};

static int livepatch_init(void)
diff --git a/samples/livepatch/livepatch-shadow-fix1.c b/samples/livepatch/livepatch-shadow-fix1.c
index e89ca4546114..62c3f89f26d1 100644
--- a/samples/livepatch/livepatch-shadow-fix1.c
+++ b/samples/livepatch/livepatch-shadow-fix1.c
@@ -27,120 +27,26 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
-#include <linux/slab.h>

-/* Shadow variable enums */
-#define SV_LEAK 1
-
-/* Allocate new dummies every second */
-#define ALLOC_PERIOD 1
-/* Check for expired dummies after a few new ones have been allocated */
-#define CLEANUP_PERIOD (3 * ALLOC_PERIOD)
-/* Dummies expire after a few cleanup instances */
-#define EXPIRE_PERIOD (4 * CLEANUP_PERIOD)
-
-struct dummy {
- struct list_head list;
- unsigned long jiffies_expire;
+static struct klp_func no_funcs[] = {
+ {}
};

-/*
- * The constructor makes more sense together with klp_shadow_get_or_alloc().
- * In this example, it would be safe to assign the pointer also to the shadow
- * variable returned by klp_shadow_alloc(). But we wanted to show the more
- * complicated use of the API.
- */
-static int shadow_leak_ctor(void *obj, void *shadow_data, void *ctor_data)
-{
- void **shadow_leak = shadow_data;
- void *leak = ctor_data;
-
- *shadow_leak = leak;
- return 0;
-}
-
-static struct dummy *livepatch_fix1_dummy_alloc(void)
-{
- struct dummy *d;
- void *leak;
-
- d = kzalloc(sizeof(*d), GFP_KERNEL);
- if (!d)
- return NULL;
-
- d->jiffies_expire = jiffies +
- msecs_to_jiffies(1000 * EXPIRE_PERIOD);
-
- /*
- * Patch: save the extra memory location into a SV_LEAK shadow
- * variable. A patched dummy_free routine can later fetch this
- * pointer to handle resource release.
- */
- leak = kzalloc(sizeof(int), GFP_KERNEL);
- if (!leak) {
- kfree(d);
- return NULL;
- }
-
- klp_shadow_alloc(d, SV_LEAK, sizeof(leak), GFP_KERNEL,
- shadow_leak_ctor, leak);
-
- pr_info("%s: dummy @ %p, expires @ %lx\n",
- __func__, d, d->jiffies_expire);
-
- return d;
-}
-
-static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data)
-{
- void *d = obj;
- void **shadow_leak = shadow_data;
-
- kfree(*shadow_leak);
- pr_info("%s: dummy @ %p, prevented leak @ %p\n",
- __func__, d, *shadow_leak);
-}
-
-static void livepatch_fix1_dummy_free(struct dummy *d)
-{
- void **shadow_leak;
-
- /*
- * Patch: fetch the saved SV_LEAK shadow variable, detach and
- * free it. Note: handle cases where this shadow variable does
- * not exist (ie, dummy structures allocated before this livepatch
- * was loaded.)
- */
- shadow_leak = klp_shadow_get(d, SV_LEAK);
- if (shadow_leak)
- klp_shadow_free(d, SV_LEAK, livepatch_fix1_dummy_leak_dtor);
- else
- pr_info("%s: dummy @ %p leaked!\n", __func__, d);
-
- kfree(d);
-}
-
-static struct klp_func funcs[] = {
- {
- .old_name = "dummy_alloc",
- .new_func = livepatch_fix1_dummy_alloc,
- },
- {
- .old_name = "dummy_free",
- .new_func = livepatch_fix1_dummy_free,
- }, { }
+static struct klp_object obj = {
+ .patch_name = THIS_MODULE->name,
+ .name = NULL, /* vmlinux */
+ .mod = THIS_MODULE,
+ .funcs = no_funcs,
};

-static struct klp_object objs[] = {
- {
- .name = "livepatch_shadow_mod",
- .funcs = funcs,
- }, { }
+static char *obj_names[] = {
+ "livepatch_shadow_mod",
+ NULL
};

static struct klp_patch patch = {
- .mod = THIS_MODULE,
- .objs = objs,
+ .obj = &obj,
+ .obj_names = obj_names,
};

static int livepatch_shadow_fix1_init(void)
@@ -150,8 +56,6 @@ static int livepatch_shadow_fix1_init(void)

static void livepatch_shadow_fix1_exit(void)
{
- /* Cleanup any existing SV_LEAK shadow variables */
- klp_shadow_free_all(SV_LEAK, livepatch_fix1_dummy_leak_dtor);
}

module_init(livepatch_shadow_fix1_init);
diff --git a/samples/livepatch/livepatch-shadow-fix1__livepatch-shadow-mod.c b/samples/livepatch/livepatch-shadow-fix1__livepatch-shadow-mod.c
new file mode 100644
index 000000000000..904bbc9ccfda
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-fix1__livepatch-shadow-mod.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Joe Lawrence <[email protected]>
+ */
+
+/*
+ * livepatch-shadow-fix1.c - Shadow variables, livepatch demo
+ *
+ * Purpose
+ * -------
+ *
+ * Fixes the memory leak introduced in livepatch-shadow-mod through the
+ * use of a shadow variable. This fix demonstrates the "extending" of
+ * short-lived data structures by patching its allocation and release
+ * functions.
+ *
+ *
+ * Usage
+ * -----
+ *
+ * This module is not intended to be standalone. See the "Usage"
+ * section of livepatch-shadow-mod.c.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+
+/* Shadow variable enums */
+#define SV_LEAK 1
+
+/* Allocate new dummies every second */
+#define ALLOC_PERIOD 1
+/* Check for expired dummies after a few new ones have been allocated */
+#define CLEANUP_PERIOD (3 * ALLOC_PERIOD)
+/* Dummies expire after a few cleanup instances */
+#define EXPIRE_PERIOD (4 * CLEANUP_PERIOD)
+
+struct dummy {
+ struct list_head list;
+ unsigned long jiffies_expire;
+};
+
+/*
+ * The constructor makes more sense together with klp_shadow_get_or_alloc().
+ * In this example, it would be safe to assign the pointer also to the shadow
+ * variable returned by klp_shadow_alloc(). But we wanted to show the more
+ * complicated use of the API.
+ */
+static int shadow_leak_ctor(void *obj, void *shadow_data, void *ctor_data)
+{
+ void **shadow_leak = shadow_data;
+ void *leak = ctor_data;
+
+ *shadow_leak = leak;
+ return 0;
+}
+
+static struct dummy *livepatch_fix1_dummy_alloc(void)
+{
+ struct dummy *d;
+ void *leak;
+
+ d = kzalloc(sizeof(*d), GFP_KERNEL);
+ if (!d)
+ return NULL;
+
+ d->jiffies_expire = jiffies +
+ msecs_to_jiffies(1000 * EXPIRE_PERIOD);
+
+ /*
+ * Patch: save the extra memory location into a SV_LEAK shadow
+ * variable. A patched dummy_free routine can later fetch this
+ * pointer to handle resource release.
+ */
+ leak = kzalloc(sizeof(int), GFP_KERNEL);
+ if (!leak) {
+ kfree(d);
+ return NULL;
+ }
+
+ klp_shadow_alloc(d, SV_LEAK, sizeof(leak), GFP_KERNEL,
+ shadow_leak_ctor, leak);
+
+ pr_info("%s: dummy @ %p, expires @ %lx\n",
+ __func__, d, d->jiffies_expire);
+
+ return d;
+}
+
+static void livepatch_fix1_dummy_leak_dtor(void *obj, void *shadow_data)
+{
+ void *d = obj;
+ void **shadow_leak = shadow_data;
+
+ kfree(*shadow_leak);
+ pr_info("%s: dummy @ %p, prevented leak @ %p\n",
+ __func__, d, *shadow_leak);
+}
+
+static void livepatch_fix1_dummy_free(struct dummy *d)
+{
+ void **shadow_leak;
+
+ /*
+ * Patch: fetch the saved SV_LEAK shadow variable, detach and
+ * free it. Note: handle cases where this shadow variable does
+ * not exist (ie, dummy structures allocated before this livepatch
+ * was loaded.)
+ */
+ shadow_leak = klp_shadow_get(d, SV_LEAK);
+ if (shadow_leak)
+ klp_shadow_free(d, SV_LEAK, livepatch_fix1_dummy_leak_dtor);
+ else
+ pr_info("%s: dummy @ %p leaked!\n", __func__, d);
+
+ kfree(d);
+}
+
+static struct klp_func funcs[] = {
+ {
+ .old_name = "dummy_alloc",
+ .new_func = livepatch_fix1_dummy_alloc,
+ },
+ {
+ .old_name = "dummy_free",
+ .new_func = livepatch_fix1_dummy_free,
+ }, { }
+};
+
+static struct klp_object obj = {
+ .patch_name = "livepatch_shadow_fix1",
+ .name = "livepatch_shadow_mod",
+ .mod = THIS_MODULE,
+ .funcs = funcs,
+};
+
+static int livepatch_shadow_fix1_init(void)
+{
+ return klp_add_object(&obj);
+}
+
+static void livepatch_shadow_fix1_exit(void)
+{
+ /* Cleanup any existing SV_LEAK shadow variables */
+ klp_shadow_free_all(SV_LEAK, livepatch_fix1_dummy_leak_dtor);
+}
+
+module_init(livepatch_shadow_fix1_init);
+module_exit(livepatch_shadow_fix1_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/samples/livepatch/livepatch-shadow-fix2.c b/samples/livepatch/livepatch-shadow-fix2.c
index 50d223b82e8b..6f1359c002d5 100644
--- a/samples/livepatch/livepatch-shadow-fix2.c
+++ b/samples/livepatch/livepatch-shadow-fix2.c
@@ -27,92 +27,26 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/livepatch.h>
-#include <linux/slab.h>

-/* Shadow variable enums */
-#define SV_LEAK 1
-#define SV_COUNTER 2
-
-struct dummy {
- struct list_head list;
- unsigned long jiffies_expire;
+static struct klp_func no_funcs[] = {
+ {}
};

-static bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies)
-{
- int *shadow_count;
-
- /*
- * Patch: handle in-flight dummy structures, if they do not
- * already have a SV_COUNTER shadow variable, then attach a
- * new one.
- */
- shadow_count = klp_shadow_get_or_alloc(d, SV_COUNTER,
- sizeof(*shadow_count), GFP_NOWAIT,
- NULL, NULL);
- if (shadow_count)
- *shadow_count += 1;
-
- return time_after(jiffies, d->jiffies_expire);
-}
-
-static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data)
-{
- void *d = obj;
- void **shadow_leak = shadow_data;
-
- kfree(*shadow_leak);
- pr_info("%s: dummy @ %p, prevented leak @ %p\n",
- __func__, d, *shadow_leak);
-}
-
-static void livepatch_fix2_dummy_free(struct dummy *d)
-{
- void **shadow_leak;
- int *shadow_count;
-
- /* Patch: copy the memory leak patch from the fix1 module. */
- shadow_leak = klp_shadow_get(d, SV_LEAK);
- if (shadow_leak)
- klp_shadow_free(d, SV_LEAK, livepatch_fix2_dummy_leak_dtor);
- else
- pr_info("%s: dummy @ %p leaked!\n", __func__, d);
-
- /*
- * Patch: fetch the SV_COUNTER shadow variable and display
- * the final count. Detach the shadow variable.
- */
- shadow_count = klp_shadow_get(d, SV_COUNTER);
- if (shadow_count) {
- pr_info("%s: dummy @ %p, check counter = %d\n",
- __func__, d, *shadow_count);
- klp_shadow_free(d, SV_COUNTER, NULL);
- }
-
- kfree(d);
-}
-
-static struct klp_func funcs[] = {
- {
- .old_name = "dummy_check",
- .new_func = livepatch_fix2_dummy_check,
- },
- {
- .old_name = "dummy_free",
- .new_func = livepatch_fix2_dummy_free,
- }, { }
+static struct klp_object obj = {
+ .patch_name = THIS_MODULE->name,
+ .name = NULL, /* vmlinux */
+ .mod = THIS_MODULE,
+ .funcs = no_funcs,
};

-static struct klp_object objs[] = {
- {
- .name = "livepatch_shadow_mod",
- .funcs = funcs,
- }, { }
+static char *obj_names[] = {
+ "livepatch_shadow_mod",
+ NULL
};

static struct klp_patch patch = {
- .mod = THIS_MODULE,
- .objs = objs,
+ .obj = &obj,
+ .obj_names = obj_names,
};

static int livepatch_shadow_fix2_init(void)
@@ -122,8 +56,6 @@ static int livepatch_shadow_fix2_init(void)

static void livepatch_shadow_fix2_exit(void)
{
- /* Cleanup any existing SV_COUNTER shadow variables */
- klp_shadow_free_all(SV_COUNTER, NULL);
}

module_init(livepatch_shadow_fix2_init);
diff --git a/samples/livepatch/livepatch-shadow-fix2__livepatch-shadow-mod.c b/samples/livepatch/livepatch-shadow-fix2__livepatch-shadow-mod.c
new file mode 100644
index 000000000000..f4592b78d667
--- /dev/null
+++ b/samples/livepatch/livepatch-shadow-fix2__livepatch-shadow-mod.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2017 Joe Lawrence <[email protected]>
+ */
+
+/*
+ * livepatch-shadow-fix2.c - Shadow variables, livepatch demo
+ *
+ * Purpose
+ * -------
+ *
+ * Adds functionality to livepatch-shadow-mod's in-flight data
+ * structures through a shadow variable. The livepatch patches a
+ * routine that periodically inspects data structures, incrementing a
+ * per-data-structure counter, creating the counter if needed.
+ *
+ *
+ * Usage
+ * -----
+ *
+ * This module is not intended to be standalone. See the "Usage"
+ * section of livepatch-shadow-mod.c.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/livepatch.h>
+#include <linux/slab.h>
+
+/* Shadow variable enums */
+#define SV_LEAK 1
+#define SV_COUNTER 2
+
+struct dummy {
+ struct list_head list;
+ unsigned long jiffies_expire;
+};
+
+static bool livepatch_fix2_dummy_check(struct dummy *d, unsigned long jiffies)
+{
+ int *shadow_count;
+
+ /*
+ * Patch: handle in-flight dummy structures, if they do not
+ * already have a SV_COUNTER shadow variable, then attach a
+ * new one.
+ */
+ shadow_count = klp_shadow_get_or_alloc(d, SV_COUNTER,
+ sizeof(*shadow_count), GFP_NOWAIT,
+ NULL, NULL);
+ if (shadow_count)
+ *shadow_count += 1;
+
+ return time_after(jiffies, d->jiffies_expire);
+}
+
+static void livepatch_fix2_dummy_leak_dtor(void *obj, void *shadow_data)
+{
+ void *d = obj;
+ void **shadow_leak = shadow_data;
+
+ kfree(*shadow_leak);
+ pr_info("%s: dummy @ %p, prevented leak @ %p\n",
+ __func__, d, *shadow_leak);
+}
+
+static void livepatch_fix2_dummy_free(struct dummy *d)
+{
+ void **shadow_leak;
+ int *shadow_count;
+
+ /* Patch: copy the memory leak patch from the fix1 module. */
+ shadow_leak = klp_shadow_get(d, SV_LEAK);
+ if (shadow_leak)
+ klp_shadow_free(d, SV_LEAK, livepatch_fix2_dummy_leak_dtor);
+ else
+ pr_info("%s: dummy @ %p leaked!\n", __func__, d);
+
+ /*
+ * Patch: fetch the SV_COUNTER shadow variable and display
+ * the final count. Detach the shadow variable.
+ */
+ shadow_count = klp_shadow_get(d, SV_COUNTER);
+ if (shadow_count) {
+ pr_info("%s: dummy @ %p, check counter = %d\n",
+ __func__, d, *shadow_count);
+ klp_shadow_free(d, SV_COUNTER, NULL);
+ }
+
+ kfree(d);
+}
+
+static struct klp_func funcs[] = {
+ {
+ .old_name = "dummy_check",
+ .new_func = livepatch_fix2_dummy_check,
+ },
+ {
+ .old_name = "dummy_free",
+ .new_func = livepatch_fix2_dummy_free,
+ }, { }
+};
+
+static struct klp_object obj = {
+ .patch_name = "livepatch_shadow_fix2",
+ .name = "livepatch_shadow_mod",
+ .mod = THIS_MODULE,
+ .funcs = funcs,
+};
+
+static int livepatch_shadow_fix2_init(void)
+{
+ return klp_add_object(&obj);
+}
+
+static void livepatch_shadow_fix2_exit(void)
+{
+ /* Cleanup any existing SV_COUNTER shadow variables */
+ klp_shadow_free_all(SV_COUNTER, NULL);
+}
+
+module_init(livepatch_shadow_fix2_init);
+module_exit(livepatch_shadow_fix2_exit);
+MODULE_LICENSE("GPL");
+MODULE_INFO(livepatch, "Y");
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
index a35289b13c9c..ccaed35d0901 100755
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh
@@ -509,17 +509,17 @@ livepatch: '$MOD_LIVEPATCH': patching complete
% modprobe $MOD_LIVEPATCH2
livepatch: enabling patch '$MOD_LIVEPATCH2'
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
+$MOD_LIVEPATCH2: pre_patch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': starting patching transition
livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
+$MOD_LIVEPATCH2: post_patch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': patching complete
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: pre_unpatch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: post_unpatch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': unpatching complete
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
@@ -568,17 +568,17 @@ livepatch: '$MOD_LIVEPATCH': patching complete
% modprobe $MOD_LIVEPATCH2 replace=1
livepatch: enabling patch '$MOD_LIVEPATCH2'
livepatch: '$MOD_LIVEPATCH2': initializing patching transition
-$MOD_LIVEPATCH2: pre_patch_callback: vmlinux
+$MOD_LIVEPATCH2: pre_patch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': starting patching transition
livepatch: '$MOD_LIVEPATCH2': completing patching transition
-$MOD_LIVEPATCH2: post_patch_callback: vmlinux
+$MOD_LIVEPATCH2: post_patch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': patching complete
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH2/enabled
livepatch: '$MOD_LIVEPATCH2': initializing unpatching transition
-$MOD_LIVEPATCH2: pre_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: pre_unpatch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': starting unpatching transition
livepatch: '$MOD_LIVEPATCH2': completing unpatching transition
-$MOD_LIVEPATCH2: post_unpatch_callback: vmlinux
+$MOD_LIVEPATCH2: post_unpatch_callback2: vmlinux
livepatch: '$MOD_LIVEPATCH2': unpatching complete
% rmmod $MOD_LIVEPATCH2
% rmmod $MOD_LIVEPATCH"
--
2.16.4

2020-01-17 15:05:32

by Petr Mladek

[permalink] [raw]
Subject: [POC 20/23] module/livepatch: Relocate local variables in the module loaded when the livepatch is being loaded

The special SHF_RELA_LIVEPATCH section is still needed to find static
(non-exported) symbols. But it can be done together with the other
relocations when the livepatch module is being loaded.

There is no longer needed to copy the info section. The related
code in the module loaded will get removed in separate patch.

Signed-off-by: Petr Mladek <[email protected]>
---
include/linux/livepatch.h | 4 +++
kernel/livepatch/core.c | 62 +++--------------------------------------------
kernel/module.c | 16 +++++++-----
3 files changed, 18 insertions(+), 64 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 4776deb7418c..d721e79ac691 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -211,6 +211,10 @@ bool klp_break_recursion(struct module *mod);
int klp_module_coming(struct module *mod);
void klp_module_going(struct module *mod);

+int klp_resolve_symbols(Elf_Shdr *sechdrs,
+ unsigned int relsec,
+ struct module *pmod);
+
void klp_copy_process(struct task_struct *child);
void klp_update_patch_state(struct task_struct *task);

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 9e00871fbc06..4da6bbd687d0 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -196,12 +196,15 @@ static int klp_find_object_symbol(const char *objname, const char *name,
return -EINVAL;
}

-static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod)
+int klp_resolve_symbols(Elf_Shdr *sechdrs,
+ unsigned int relsec,
+ struct module *pmod)
{
int i, cnt, vmlinux, ret;
char objname[MODULE_NAME_LEN];
char symname[KSYM_NAME_LEN];
char *strtab = pmod->core_kallsyms.strtab;
+ Elf_Shdr *relasec = sechdrs + relsec;
Elf_Rela *relas;
Elf_Sym *sym;
unsigned long sympos, addr;
@@ -251,54 +254,6 @@ static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod)
return 0;
}

-static int klp_write_object_relocations(struct klp_object *obj)
-{
- int i, cnt, ret = 0;
- const char *objname, *secname;
- char sec_objname[MODULE_NAME_LEN];
- struct module *pmod;
- Elf_Shdr *sec;
-
- objname = klp_is_module(obj) ? obj->name : "vmlinux";
- pmod = obj->mod;
-
- /* For each klp relocation section */
- for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) {
- sec = pmod->klp_info->sechdrs + i;
- secname = pmod->klp_info->secstrings + sec->sh_name;
- if (!(sec->sh_flags & SHF_RELA_LIVEPATCH))
- continue;
-
- /*
- * Format: .klp.rela.sec_objname.section_name
- * See comment in klp_resolve_symbols() for an explanation
- * of the selected field width value.
- */
- cnt = sscanf(secname, ".klp.rela.%55[^.]", sec_objname);
- if (cnt != 1) {
- pr_err("section %s has an incorrectly formatted name\n",
- secname);
- ret = -EINVAL;
- break;
- }
-
- if (strcmp(objname, sec_objname))
- continue;
-
- ret = klp_resolve_symbols(sec, pmod);
- if (ret)
- break;
-
- ret = apply_relocate_add(pmod->klp_info->sechdrs,
- pmod->core_kallsyms.strtab,
- pmod->klp_info->symndx, i, pmod);
- if (ret)
- break;
- }
-
- return ret;
-}
-
/*
* Sysfs Interface
*
@@ -758,18 +713,9 @@ static int klp_init_object_loaded(struct klp_object *obj)
int ret;

mutex_lock(&text_mutex);
-
module_disable_ro(obj->mod);
- ret = klp_write_object_relocations(obj);
- if (ret) {
- module_enable_ro(obj->mod, true);
- mutex_unlock(&text_mutex);
- return ret;
- }
-
arch_klp_init_object_loaded(obj);
module_enable_ro(obj->mod, true);
-
mutex_unlock(&text_mutex);

klp_for_each_func(obj, func) {
diff --git a/kernel/module.c b/kernel/module.c
index bd92854b42c2..c14b5135db27 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2410,16 +2410,20 @@ static int apply_relocations(struct module *mod, const struct load_info *info)
if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
continue;

- /* Livepatch relocation sections are applied by livepatch */
- if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH)
- continue;
-
- if (info->sechdrs[i].sh_type == SHT_REL)
+ /* Livepatch need to resolve static symbols. */
+ if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) {
+ err = klp_resolve_symbols(info->sechdrs, i, mod);
+ if (err < 0)
+ break;
+ err = apply_relocate_add(info->sechdrs, info->strtab,
+ info->index.sym, i, mod);
+ } else if (info->sechdrs[i].sh_type == SHT_REL) {
err = apply_relocate(info->sechdrs, info->strtab,
info->index.sym, i, mod);
- else if (info->sechdrs[i].sh_type == SHT_RELA)
+ } else if (info->sechdrs[i].sh_type == SHT_RELA) {
err = apply_relocate_add(info->sechdrs, info->strtab,
info->index.sym, i, mod);
+ }
if (err < 0)
break;
}
--
2.16.4

2020-01-17 15:05:42

by Petr Mladek

[permalink] [raw]
Subject: [POC 19/23] module/livepatch: Allow to use exported symbols from livepatch module for "vmlinux"

HINT: Get some coffee before reading this commit message.

Stop reading when it gets too complicated. It is possible that we
will need to resolve symbols from livepatch modules another way.
Livepatches need to access also non-exported symbols anyway.

Or just ask me to explain the problem a better way. I have
ended in many cycles when thinking about it. And it might
be much easier from another point of view.

The split per-object livepatches brings even more types of module
dependencies. Let's split them into few categories:

A. Livepatch module using an exported symbol from "vmlinux".

It is quite common and works by definition. Livepatch module is just
a module from this point of view.

B. Livepatch module using an exported symbol from the patched module.

It should be avoided even with the non-split livepatch module. The module
loader automatically takes reference to make sure the modules are
unloaded in the right order. This would basically prevent the livepatched
module from unloading.

Note that it would be perfectly safe to remove this automatic
dependency. The livepatch framework makes sure that the livepatch
module is loaded only when the patched one is loaded. But it cannot
be implemented easily, see below.

C. Livepatch module using an exported symbol from the main livepatch module
for "vmlinux".

It is the 2nd most realistic variant. It even exists in current
selftests. Namely, test_klp_callback_demo* modules share
the implementation of callbacks. It avoids code duplication.
And it is actually needed to make module parameters working.

Note that the current implementation allows to pass module parameters
only to the main livepatch module for "vmlinux". It should not be a real
life problem. The parameters are used in selftests. But they are
not used in practice.

D. Livepatch modules might depend on each other. Note that dependency on
the main livepatch module for "vmlinux" has got a separate category 'C'.

The dependencies between modules are quite rare. But they exist.
One can assume that this might be useful also on the livepatching
level.

To keep it sane, the livepatch modules should just follow
the dependencies of the related patched modules. By other words,
the livepatch modules might or should have the same dependencies
as the patched counter parts but nothing more.

Do these dependencies need some special handling?

Yes, the livepatch module load might get blocked in the following
situations:

1. Recursion caused by dependency of type B:

If a livepatch module uses a symbol from the patched module and
the livepatch module is loaded by klp_module_coming().
The module loader will then automatically try to load the patched module
once again while it is blocked in the klp_module_coming() callback
and STATE_COMING.

2. Recursion caused by dependency of type C:

Livepatch for "vmlinux" loads other livepatch modules from
klp_enabled_patch() in mod->init(). If any of these other
modules use symbol from the main module for "vmlinux" the module
loaded will automatically try to load the main module once
again. The first instance is waiting in klp_enable_patch()
and STATE_COMING.

3. Deadlock caused by dependency of type D:

Random dependencies between livepatch and patched modules might
create a cycle. It need not be obvious and detected by the existing
code because some dependencies are created by livepatching code.
It loads the livepatches automatically for patched modules.

What can be done?

First, put aside the problems with random dependencies of type D:

They are not much realistic. Livepatches follow upstream changes.
As a result livepatch modules should have the same of less
dependencies than the related patched modules.

Note that the existing code is able to handle non-cyclic dependencies
between livepatch modules. The module loader is able to load more
modules in parallel. Also klp_add_object() can be called more times
in parallel.

Second, let's look more at the recursion problems. They might cause
deadlock on two locations in the module loader:

i. The recursive instance waits in add_unformed_module() until the first
instance reaches STATE_LIVE. But this never happens because the first
instance waits until the recursive instance finishes.

ii. resolve_symbol() wants to get reference of the module where
the symbol comes from. But strong_try_module_get() refuses
to get reference when the module is still in STATE_COMING.
It does not want to block removing the module when the load
eventually fails later.

Is it possible to avoid the recursion?

1. It might be possible to avoid calling exported symbols directly
from the livepatch. For example, using kallsyms lookup. Or some
special relocation. This would require some extra action when
preparing the livepatchi.

2. Hide these dependencies to depmod and the module loader. This
would require changes in the user space tools.

And what about breaking the cycle?

It seems acceptable to return immediately from the nested load
if we are able to guarantee:

+ The recursion is clearly caused by one the of above described
livepatch dependency type.

+ The code from affected modules will be used only after the modules
are successfully loaded in the end.

+ The modules will get unloaded when there are other errors
on the way.

+ The original caller will get the right error code. By other
words, only the automatically triggered loads will be finished
prematurely.

OK, this patch provides the solution for the dependency of type C. It is when
other livepatch modules use symbols from the livepatch module for vmlinux.

The recursive load returns -EEXIST immediately when the recursive module
tries to load the same livepatch that is already being loaded. It is
safe because:

+ Only one livepatch can be enabled at a time. Any attempt to enable
another livepatch would return -EBUSY.

+ The livepatch module for "vmlinux" is never loaded automatically
because of any dependency. Other livepatch modules for other objects
might depend on it but the module for "vmlinux" must always be loaded
explicitly by the system administrator.

By other words, the recursion of the livepatch module for "vmlinux" that
is just being enabled is always caused by dependency of type C.
The recursive instance can and actually must return immediately. It will
allow to proceed. The entire operation will succeed only when the outer
load succeeds.

But there is still the problem that strong_try_module_get() could
not get reference. Isn't it?

Yes, it is solved by not taking references from livepatch modules.
The reference is needed to make sure that the module will not get
removed prematurely. But this is guaranteed by the livepatch code:

+ Livepatch module for "vmlinux" is always loaded first.

+ Livepatch modules for other objects can't be loaded before
the module for "vmlinux" is loaded, see the checks
in klp_add_object().

+ Livepatch module is always loaded and removed automatically
together with the patched module. The dependencies between
the patched modules are enough as long as the dependencies
of livepatch modules are symmetric.

Finally. dependency of type B will still cause deadlock. It happens
when a livepatch module uses a symbols from the patched module.
It cannot be solved easily because:

+ The patched module is loaded recursively (not the livepatch one).
There is no clear way how to make sure that the recursive
module load is the one triggered by "modprobe" called from
klp_module_coming().

As a result, -EEXIST might be returned also to modprobe
called by the user. It would force the user to double
check that the patched module has really been loaded.

+ If the above problem is not fixed then the result from
modprobe is not reliable. The livepatch code might need
to double check that request_module() really added the
klp_object into the related klp_patch structure.

Signed-off-by: Petr Mladek <[email protected]>
---
include/linux/livepatch.h | 2 ++
kernel/livepatch/core.c | 16 ++++++++++++++++
kernel/module.c | 16 ++++++++++++++++
3 files changed, 34 insertions(+)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 4afb7f3a5a36..4776deb7418c 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -207,6 +207,7 @@ int klp_add_object(struct klp_object *);
void arch_klp_init_object_loaded(struct klp_object *obj);

/* Called from the module loader during module coming/going states */
+bool klp_break_recursion(struct module *mod);
int klp_module_coming(struct module *mod);
void klp_module_going(struct module *mod);

@@ -244,6 +245,7 @@ struct klp_state *klp_get_prev_state(unsigned long id);

#else /* !CONFIG_LIVEPATCH */

+static inline bool klp_break_recursion(struct module *mod) { return false; }
static inline int klp_module_coming(struct module *mod) { return 0; }
static inline void klp_module_going(struct module *mod) {}
static inline bool klp_patch_pending(struct task_struct *task) { return false; }
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 6d4ec7642908..9e00871fbc06 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -1416,6 +1416,22 @@ static bool klp_is_object_module_alive(const char *patch_name,
return alive;
}

+bool klp_break_recursion(struct module *mod)
+{
+ bool ret = false;
+
+ if (!mod->klp)
+ return false;
+
+ mutex_lock(&klp_mutex);
+ if (klp_loading_patch &&
+ !strcmp(klp_loading_patch->obj->mod->name, mod->name))
+ ret = true;
+ mutex_unlock(&klp_mutex);
+
+ return ret;
+}
+
int klp_module_coming(struct module *mod)
{
char patch_name[MODULE_NAME_LEN];
diff --git a/kernel/module.c b/kernel/module.c
index ac45d465ff23..bd92854b42c2 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -867,6 +867,13 @@ int ref_module(struct module *a, struct module *b)
{
int err;

+ /*
+ * Livepatch modules have their own dependency handling.
+ * Implicit dependencies might cause cycles.
+ */
+ if (is_livepatch_module(a))
+ return 0;
+
if (b == NULL || already_uses(a, b))
return 0;

@@ -3730,6 +3737,15 @@ static int add_unformed_module(struct module *mod)

/* Wait in case it fails to load. */
mutex_unlock(&module_mutex);
+
+ /*
+ * Livepatch modules might use exported symbols from vmlinux.
+ * It creates automatic dependencies and recursive module load.
+ * Livepatch core handles the consistency on its own.
+ */
+ if (klp_break_recursion(mod))
+ return -EEXIST;
+
err = wait_event_interruptible(module_wq,
finished_loading(mod->name));
if (err)
--
2.16.4

2020-01-17 15:05:45

by Petr Mladek

[permalink] [raw]
Subject: [POC 23/23] module: Remove obsolete module_disable_ro()

The split livepatch modules are relocated immediately during the module
load. There is no longer needed to disable the RO protection. The function
can be finally remove because livepatching was the only user (sinner).

Signed-off-by: Petr Mladek <[email protected]>
---
include/linux/module.h | 2 --
kernel/module.c | 13 -------------
2 files changed, 15 deletions(-)

diff --git a/include/linux/module.h b/include/linux/module.h
index 8545f3087274..5c9e661ac3bc 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -854,12 +854,10 @@ extern int module_sysfs_initialized;
extern void set_all_modules_text_rw(void);
extern void set_all_modules_text_ro(void);
extern void module_enable_ro(const struct module *mod, bool after_init);
-extern void module_disable_ro(const struct module *mod);
#else
static inline void set_all_modules_text_rw(void) { }
static inline void set_all_modules_text_ro(void) { }
static inline void module_enable_ro(const struct module *mod, bool after_init) { }
-static inline void module_disable_ro(const struct module *mod) { }
#endif

#ifdef CONFIG_GENERIC_BUG
diff --git a/kernel/module.c b/kernel/module.c
index 442926fc5f34..d435bad80d7d 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2030,19 +2030,6 @@ static void frob_writable_data(const struct module_layout *layout,
(layout->size - layout->ro_after_init_size) >> PAGE_SHIFT);
}

-/* livepatching wants to disable read-only so it can frob module. */
-void module_disable_ro(const struct module *mod)
-{
- if (!rodata_enabled)
- return;
-
- frob_text(&mod->core_layout, set_memory_rw);
- frob_rodata(&mod->core_layout, set_memory_rw);
- frob_ro_after_init(&mod->core_layout, set_memory_rw);
- frob_text(&mod->init_layout, set_memory_rw);
- frob_rodata(&mod->init_layout, set_memory_rw);
-}
-
void module_enable_ro(const struct module *mod, bool after_init)
{
if (!rodata_enabled)
--
2.16.4

2020-01-17 15:05:53

by Petr Mladek

[permalink] [raw]
Subject: [POC 18/23] module: Refactor add_unformed_module()

The livepatching code will need to add another condition to avoid
waiting. Let's make the code slightly less hairy.

Of course, it is a matter of taste. The main ideas of refactoring:

+ Make it clear what happens when old->state == MODULE_STATE_LIVE.

+ Make it more clear when we are leaving the function immediately
and when we are doing some extra actions.

+ Be able to add another check that has to be done outside
module_mutex and can result into an immediate return.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/module.c | 28 +++++++++++++++-------------
1 file changed, 15 insertions(+), 13 deletions(-)

diff --git a/kernel/module.c b/kernel/module.c
index b3f11524f8f9..ac45d465ff23 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -3721,28 +3721,30 @@ static int add_unformed_module(struct module *mod)
again:
mutex_lock(&module_mutex);
old = find_module_all(mod->name, strlen(mod->name), true);
+
if (old != NULL) {
- if (old->state != MODULE_STATE_LIVE) {
- /* Wait in case it fails to load. */
+ if (old->state == MODULE_STATE_LIVE) {
mutex_unlock(&module_mutex);
- err = wait_event_interruptible(module_wq,
- finished_loading(mod->name));
- if (err)
- goto out_unlocked;
- goto again;
+ return -EEXIST;
}
- err = -EEXIST;
- goto out;
+
+ /* Wait in case it fails to load. */
+ mutex_unlock(&module_mutex);
+ err = wait_event_interruptible(module_wq,
+ finished_loading(mod->name));
+ if (err)
+ return err;
+
+ /* Did the load succeded or failed? */
+ goto again;
}
+
mod_update_bounds(mod);
list_add_rcu(&mod->list, &modules);
mod_tree_insert(mod);
- err = 0;

-out:
mutex_unlock(&module_mutex);
-out_unlocked:
- return err;
+ return 0;
}

static int complete_formation(struct module *mod, struct load_info *info)
--
2.16.4

2020-01-17 15:06:14

by Petr Mladek

[permalink] [raw]
Subject: [POC 17/23] livepatch: Load livepatches for modules when loading the main livepatch

The livepatch modules have been split per-object. The livepatch module for
"vmlinux" must always exist. It includes struct klp_patch and defines
the sysfs interface. It must be loaded first and removed last.

The related livepatch modules that are livepatching already loaded modules
must be loaded before the main livepatch gets enabled.

It might be possible to check if a livepatch module exists for each
loaded module by calling "modprobe -qR". But it would be a nightmare
to avoid races.

Instead, names of all livepatched modules are statically defined
in patch->obj_names array. klp_loaded_objects_livepatches()
iterates over this list and loads the needed modules one
by one. It must be called with klp_mutex released. It is
safe becase:

1. The livepatch itself could not be removed until it returns
from mod->init(). Note that it is not visible in sysfs
interface at the moment.

2. Any operation with other livepatches is prevented using
klp_loading_livepatch variable. The affected operations
are klp_enable_livepatch() and enabled_store().

3. Modules loaded and removed in parallel are properly handled
by klp_module_coming() and klp_module_going() callbacks.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 79 insertions(+), 1 deletion(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 06676ec63ba7..6d4ec7642908 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -45,6 +45,7 @@ DEFINE_MUTEX(klp_mutex);
LIST_HEAD(klp_patches);

static struct kobject *klp_root_kobj;
+static struct klp_patch *klp_loading_patch;

static bool klp_is_module(struct klp_object *obj)
{
@@ -332,6 +333,16 @@ static ssize_t enabled_store(struct kobject *kobj, struct kobj_attribute *attr,
goto out;
}

+ /*
+ * Do not manipulate other livepatches when a new one is being
+ * loaded to make our live easier. The patch that is being londed
+ * is not visible in sysfs at this point.
+ */
+ if (klp_loading_patch) {
+ ret = -EBUSY;
+ goto out;
+ }
+
/*
* Allow to reverse a pending transition in both ways. It might be
* necessary to complete the transition without forcing and breaking
@@ -1052,6 +1063,9 @@ int klp_add_object(struct klp_object *obj)
if (ret)
goto err;

+ if (!patch->enabled)
+ goto out;
+
ret = klp_init_object(patch, obj);
if (ret) {
pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
@@ -1081,6 +1095,7 @@ int klp_add_object(struct klp_object *obj)
if (patch != klp_transition_patch)
klp_post_patch_callback(obj);

+out:
mutex_unlock(&klp_mutex);
return 0;

@@ -1147,12 +1162,43 @@ static int klp_try_load_object(const char *patch_name, const char *obj_name)
return 0;
}

+/*
+ * This function is guarded by klp_loading_patch instead of klp_mutex to avoid
+ * deadlocks. It loads other livepatch modules via modprobe called in userspace.
+ * The other modules need to take klp-mutex as well.
+ */
+static int klp_load_objects_livepatches(struct klp_patch *patch)
+{
+ int i, ret;
+
+ for (i = 0; patch->obj_names[i]; i++) {
+ char *obj_name = patch->obj_names[i];
+ struct module *patched_mod;
+ bool obj_alive;
+
+ mutex_lock(&module_mutex);
+ patched_mod = find_module(obj_name);
+ obj_alive = patched_mod ? patched_mod->klp_alive : false;
+ mutex_unlock(&module_mutex);
+
+ /* Load livepatch module only for loaded objects. */
+ if (!obj_alive)
+ continue;
+
+ ret = klp_try_load_object(patch->obj->patch_name, obj_name);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
static int __klp_enable_patch(struct klp_patch *patch)
{
struct klp_object *obj;
int ret;

- if (klp_transition_patch)
+ if (klp_transition_patch || klp_loading_patch)
return -EBUSY;

if (WARN_ON(patch->enabled))
@@ -1199,6 +1245,21 @@ static int __klp_enable_patch(struct klp_patch *patch)
return ret;
}

+static int klp_check_patches_in_progress(void)
+{
+ struct klp_patch *patch;
+
+ if (klp_loading_patch)
+ patch = klp_loading_patch;
+ else if (klp_transition_patch)
+ patch = klp_transition_patch;
+ else
+ return 0;
+
+ pr_err("Already enabling livepatch: %s\n", patch->obj->mod->name);
+ return -EBUSY;
+}
+
/**
* klp_enable_patch() - enable the livepatch
* @patch: patch to be enabled
@@ -1233,6 +1294,12 @@ int klp_enable_patch(struct klp_patch *patch)

mutex_lock(&klp_mutex);

+ ret = klp_check_patches_in_progress();
+ if (ret) {
+ mutex_unlock(&klp_mutex);
+ return ret;
+ }
+
if (!klp_is_patch_compatible(patch)) {
pr_err("Livepatch patch (%s) is not compatible with the already installed livepatches.\n",
patch->obj->mod->name);
@@ -1246,6 +1313,17 @@ int klp_enable_patch(struct klp_patch *patch)
return ret;
}

+ klp_loading_patch = patch;
+ mutex_unlock(&klp_mutex);
+
+ ret = klp_load_objects_livepatches(patch);
+
+ mutex_lock(&klp_mutex);
+ klp_loading_patch = NULL;
+
+ if (ret)
+ goto err;
+
ret = klp_init_patch(patch);
if (ret)
goto err;
--
2.16.4

2020-01-17 15:06:18

by Petr Mladek

[permalink] [raw]
Subject: [POC 21/23] livepatch: Remove obsolete arch_klp_init_object_loaded()

The livepatch specific-relocation is finally done together with the normal
module relocations. As a result, alternatives, paraintructions, and other
architecture-specific modifications are done correctly by the module
loader out of box. There is no longer need to do them explicitely by
arch_klp_init_object_loaded().

Signed-off-by: Petr Mladek <[email protected]>
---
Documentation/livepatch/module-elf-format.rst | 29 ---------------
arch/x86/kernel/Makefile | 1 -
arch/x86/kernel/livepatch.c | 52 ---------------------------
include/linux/livepatch.h | 2 --
kernel/livepatch/core.c | 11 ------
5 files changed, 95 deletions(-)
delete mode 100644 arch/x86/kernel/livepatch.c

diff --git a/Documentation/livepatch/module-elf-format.rst b/Documentation/livepatch/module-elf-format.rst
index 2a591e6f8e6c..9f0c997d4940 100644
--- a/Documentation/livepatch/module-elf-format.rst
+++ b/Documentation/livepatch/module-elf-format.rst
@@ -14,8 +14,6 @@ This document outlines the Elf format requirements that livepatch modules must f
4. Livepatch symbols
4.1 A livepatch module's symbol table
4.2 Livepatch symbol format
- 5. Architecture-specific sections
- 6. Symbol table and Elf section access

1. Background and motivation
============================
@@ -297,30 +295,3 @@ See include/uapi/linux/elf.h for the actual definitions.
[*]
Note that the 'Ndx' (Section index) for these symbols is SHN_LIVEPATCH (0xff20).
"OS" means OS-specific.
-
-5. Architecture-specific sections
-=================================
-Architectures may override arch_klp_init_object_loaded() to perform
-additional arch-specific tasks when a target module loads, such as applying
-arch-specific sections. On x86 for example, we must apply per-object
-.altinstructions and .parainstructions sections when a target module loads.
-These sections must be prefixed with ".klp.arch.$objname." so that they can
-be easily identified when iterating through a patch module's Elf sections
-(See arch/x86/kernel/livepatch.c for a complete example).
-
-6. Symbol table and Elf section access
-======================================
-A livepatch module's symbol table is accessible through module->symtab.
-
-Since apply_relocate_add() requires access to a module's section headers,
-symbol table, and relocation section indices, Elf information is preserved for
-livepatch modules and is made accessible by the module loader through
-module->klp_info, which is a klp_modinfo struct. When a livepatch module loads,
-this struct is filled in by the module loader. Its fields are documented below::
-
- struct klp_modinfo {
- Elf_Ehdr hdr; /* Elf header */
- Elf_Shdr *sechdrs; /* Section header table */
- char *secstrings; /* String table for the section headers */
- unsigned int symndx; /* The symbol table section index */
- };
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 6175e370ee4a..21540cac67b9 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -89,7 +89,6 @@ obj-$(CONFIG_X86_MPPARSE) += mpparse.o
obj-y += apic/
obj-$(CONFIG_X86_REBOOTFIXUPS) += reboot_fixups_32.o
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
-obj-$(CONFIG_LIVEPATCH) += livepatch.o
obj-$(CONFIG_FUNCTION_TRACER) += ftrace_$(BITS).o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c
deleted file mode 100644
index 728b44eaa168..000000000000
--- a/arch/x86/kernel/livepatch.c
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * livepatch.c - x86-specific Kernel Live Patching Core
- */
-
-#include <linux/module.h>
-#include <linux/kallsyms.h>
-#include <linux/livepatch.h>
-#include <asm/text-patching.h>
-
-/* Apply per-object alternatives. Based on x86 module_finalize() */
-void arch_klp_init_object_loaded(struct klp_object *obj)
-{
- int cnt;
- struct klp_modinfo *info;
- Elf_Shdr *s, *alt = NULL, *para = NULL;
- void *aseg, *pseg;
- const char *objname;
- char sec_objname[MODULE_NAME_LEN];
- char secname[KSYM_NAME_LEN];
-
- info = obj->mod->klp_info;
- objname = obj->name ? obj->name : "vmlinux";
-
- /* See livepatch core code for BUILD_BUG_ON() explanation */
- BUILD_BUG_ON(MODULE_NAME_LEN < 56 || KSYM_NAME_LEN != 128);
-
- for (s = info->sechdrs; s < info->sechdrs + info->hdr.e_shnum; s++) {
- /* Apply per-object .klp.arch sections */
- cnt = sscanf(info->secstrings + s->sh_name,
- ".klp.arch.%55[^.].%127s",
- sec_objname, secname);
- if (cnt != 2)
- continue;
- if (strcmp(sec_objname, objname))
- continue;
- if (!strcmp(".altinstructions", secname))
- alt = s;
- if (!strcmp(".parainstructions", secname))
- para = s;
- }
-
- if (alt) {
- aseg = (void *) alt->sh_addr;
- apply_alternatives(aseg, aseg + alt->sh_size);
- }
-
- if (para) {
- pseg = (void *) para->sh_addr;
- apply_paravirt(pseg, pseg + para->sh_size);
- }
-}
diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index d721e79ac691..3b27ef1a7291 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -204,8 +204,6 @@ struct klp_patch {
int klp_enable_patch(struct klp_patch *);
int klp_add_object(struct klp_object *);

-void arch_klp_init_object_loaded(struct klp_object *obj);
-
/* Called from the module loader during module coming/going states */
bool klp_break_recursion(struct module *mod);
int klp_module_coming(struct module *mod);
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 4da6bbd687d0..cc0ac93fe8cd 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -701,23 +701,12 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
func->old_sympos ? func->old_sympos : 1);
}

-/* Arches may override this to finish any remaining arch-specific tasks */
-void __weak arch_klp_init_object_loaded(struct klp_object *obj)
-{
-}
-
/* parts of the initialization that is done only when the object is loaded */
static int klp_init_object_loaded(struct klp_object *obj)
{
struct klp_func *func;
int ret;

- mutex_lock(&text_mutex);
- module_disable_ro(obj->mod);
- arch_klp_init_object_loaded(obj);
- module_enable_ro(obj->mod, true);
- mutex_unlock(&text_mutex);
-
klp_for_each_func(obj, func) {
ret = klp_find_object_symbol(obj->name, func->old_name,
func->old_sympos,
--
2.16.4

2020-01-17 15:06:24

by Petr Mladek

[permalink] [raw]
Subject: [POC 10/23] livepatch: Handle modprobe exit code

request_module() returns classic negative error codes when it was
not even able to call "modprobe" from some reasons. Otherwise,
it returns the exit code multiplied by 256.

modprobe exit code is always 1 in case of error. Use -EINVAL
instead as the least ugly internal error code.

A better solution would be to somehow pass the original error
code from the init_module() syscall or at least the error code
from klp_module_add() functions. But there is no obvious way
how to pass the information.

Global variable is not enough because more livepatch modules
can be loaded simultaneously from klp_enable_patch() and
klp_module_comming().

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 3 +++
tools/testing/selftests/livepatch/test-callbacks.sh | 2 +-
2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 8e693c58b736..19ca8baa2f16 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -1109,6 +1109,9 @@ static int klp_try_load_object(const char *patch_name, const char *obj_name)

ret = request_module("%s__%s", patch_name, obj_name);
if (ret) {
+ /* modprobe always set exit code 1 on error */
+ if (ret > 0)
+ ret = -EINVAL;
pr_info("Module load failed: %s__%s\n", patch_name, obj_name);
return ret;
}
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
index 39a4f35e5f8e..060e5b512367 100755
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh
@@ -331,7 +331,7 @@ $MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full fo
livepatch: pre-patch callback failed for object '$MOD_TARGET'
livepatch: patch '$MOD_LIVEPATCH' failed for module '$MOD_TARGET', refusing to load module '$MOD_TARGET'
livepatch: Module load failed: ${MOD_LIVEPATCH}__${MOD_TARGET}
-modprobe: ERROR: could not insert '$MOD_TARGET': No such device
+modprobe: ERROR: could not insert '$MOD_TARGET': Invalid argument
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
$MOD_LIVEPATCH: pre_unpatch_callback: vmlinux
--
2.16.4

2020-01-17 15:06:29

by Petr Mladek

[permalink] [raw]
Subject: [POC 06/23] livepatch: Enable the livepatch submodule

The final step when loading livepatch module for livepatching a module
is to actually enable the livepatch.

The steps are exactly the same as in kmp_module_comming().

Note that there is no need to call klp_cleanup_module_patches_limited()
in the error path. klp_add_object() modifies only the single matching
struct klp_patch.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 6c27b635e5a7..c21bd9ec2012 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -1011,6 +1011,28 @@ int klp_add_object(struct klp_object *obj)
goto err_free;
}

+ pr_notice("applying patch '%s' to loading module '%s'\n",
+ patch->obj->patch_name, obj->name);
+
+ ret = klp_pre_patch_callback(obj);
+ if (ret) {
+ pr_warn("pre-patch callback failed for object '%s'\n",
+ obj->name);
+ goto err_free;
+ }
+
+ ret = klp_patch_object(obj);
+ if (ret) {
+ pr_warn("failed to apply patch '%s' to module '%s' (%d)\n",
+ patch->obj->patch_name, obj->name, ret);
+
+ klp_post_unpatch_callback(obj);
+ goto err_free;
+ }
+
+ if (patch != klp_transition_patch)
+ klp_post_patch_callback(obj);
+
mutex_unlock(&klp_mutex);
return 0;

--
2.16.4

2020-01-17 15:06:31

by Petr Mladek

[permalink] [raw]
Subject: [POC 15/23] livepatch: Prevent infinite loop when loading livepatch module

Livepatch modules should get automatically removed when the patched
module gets removed. But the problem with forced flag has shown that
there might be situations where it did not work as expected.

The problem with forced flag is solved now. But there might be other
situations that are not known at the moment.

Be on the safe side and add a paranoid check for preventing an infinite
loop in klp_module_coming() callback. It might happen when it tries to load
a livepatch module that has already been loaded in the past and that
was not removed because of a bug somewhere.

POC NOTE: The main purpose of this patch is to show potential problems
with the split livepatches. It is hard to say if the check is
worth it or whether even more checks are needed.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 4b55d805f3ec..688ad81def14 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -1316,6 +1316,25 @@ void klp_discard_nops(struct klp_patch *new_patch)
klp_free_objects_dynamic(klp_transition_patch);
}

+static bool klp_is_object_module_alive(const char *patch_name,
+ const char *obj_name)
+{
+ static char mod_name[MODULE_NAME_LEN];
+ struct module *mod;
+ bool alive = false;
+
+ mutex_lock(&module_mutex);
+
+ snprintf(mod_name, sizeof(mod_name), "%s__%s", patch_name, obj_name);
+ mod = find_module(mod_name);
+ if (mod && mod->state & MODULE_STATE_LIVE)
+ alive = true;
+
+ mutex_unlock(&module_mutex);
+
+ return alive;
+}
+
int klp_module_coming(struct module *mod)
{
char patch_name[MODULE_NAME_LEN];
@@ -1335,6 +1354,19 @@ int klp_module_coming(struct module *mod)
if (klp_is_object_loaded(patch, mod->name))
continue;

+ /*
+ * Paranoid check. Prevent infinite loop when a livepatch
+ * module is alive and klp_is_object_loaded() does not
+ * see it. It might happen when the object is removed
+ * and module_put() is not called. This should never happen
+ * when everything works as expected.
+ */
+ if (klp_is_object_module_alive(patch->obj->mod->name,
+ mod->name)) {
+ ret = -EBUSY;
+ goto err;
+ }
+
strncpy(patch_name, patch->obj->patch_name, sizeof(patch_name));
patch_ts = patch->ts;
mutex_unlock(&klp_mutex);
--
2.16.4

2020-01-17 15:06:34

by Petr Mladek

[permalink] [raw]
Subject: [POC 16/23] livepatch: Add patch into the global list early

The objects for livepatched modules need to be loaded before the livepatch
gets enabled. klp_add_module() has to find the patch. The code is easier
when the being-enabled patch can be found in the global list.

Fortunately, there is no problem to add patch into the list already
in the early init. klp_free_patch_start() will remove it in case
of any later error.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 688ad81def14..06676ec63ba7 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -852,6 +852,7 @@ static int klp_init_object_early(struct klp_patch *patch,
static int klp_init_patch_early(struct klp_patch *patch)
{
struct klp_object *obj = patch->obj;
+ int ret;

/* Main patch module is always for vmlinux */
if (obj->name)
@@ -865,7 +866,11 @@ static int klp_init_patch_early(struct klp_patch *patch)
INIT_WORK(&patch->free_work, klp_free_patch_work_fn);
init_completion(&patch->finish);

- return klp_init_object_early(patch, obj);
+ ret = klp_init_object_early(patch, obj);
+ if (!ret)
+ list_add_tail(&patch->list, &klp_patches);
+
+ return ret;
}

static int klp_init_patch(struct klp_patch *patch)
@@ -889,8 +894,6 @@ static int klp_init_patch(struct klp_patch *patch)
return ret;
}

- list_add_tail(&patch->list, &klp_patches);
-
return 0;
}

--
2.16.4

2020-01-17 15:06:55

by Petr Mladek

[permalink] [raw]
Subject: [POC 13/23] livepatch: Remove livepatch module when the livepatched module is unloaded

klp_module_going() does very similar things as before splitting
the livepatches. Especially, it unpatches the related code, calls
the callbacks, unlinks the structures from the linked lists.

It does not have to clean up the structure because the entire livepatch
module is going to be freed and removed.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 84 ++++++++++++++++++-------------------------------
1 file changed, 31 insertions(+), 53 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 6c51b194da57..73462b66f63f 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -599,21 +599,6 @@ static void __klp_free_funcs(struct klp_object *obj, bool nops_only)
}
}

-/* Clean up when a patched object is unloaded */
-static void klp_free_object_loaded(struct klp_object *obj)
-{
- struct klp_func *func;
-
- obj->mod = NULL;
-
- klp_for_each_func(obj, func) {
- func->old_func = NULL;
-
- if (func->nop)
- func->new_func = NULL;
- }
-}
-
static void klp_free_object(struct klp_object *obj, bool nops_only)
{
__klp_free_funcs(obj, nops_only);
@@ -909,6 +894,24 @@ static int klp_init_patch(struct klp_patch *patch)
return 0;
}

+static void klp_remove_object(struct klp_object *obj)
+{
+ struct klp_patch *patch =
+ container_of(obj->kobj.parent, struct klp_patch, kobj);
+
+ if (patch != klp_transition_patch)
+ klp_pre_unpatch_callback(obj);
+
+ pr_notice("reverting patch '%s' on unloading module '%s'\n",
+ patch->obj->patch_name, obj->name);
+
+ klp_unpatch_object(obj);
+
+ klp_post_unpatch_callback(obj);
+
+ klp_free_object(obj, false);
+}
+
static int klp_check_module_name(struct klp_object *obj, bool is_module)
{
char mod_name[MODULE_NAME_LEN];
@@ -1313,40 +1316,6 @@ void klp_discard_nops(struct klp_patch *new_patch)
klp_free_objects_dynamic(klp_transition_patch);
}

-/*
- * Remove parts of patches that touch a given kernel module. The list of
- * patches processed might be limited. When limit is NULL, all patches
- * will be handled.
- */
-static void klp_cleanup_module_patches_limited(struct module *mod,
- struct klp_patch *limit)
-{
- struct klp_patch *patch;
- struct klp_object *obj;
-
- klp_for_each_patch(patch) {
- if (patch == limit)
- break;
-
- klp_for_each_object(patch, obj) {
- if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
- continue;
-
- if (patch != klp_transition_patch)
- klp_pre_unpatch_callback(obj);
-
- pr_notice("reverting patch '%s' on unloading module '%s'\n",
- patch->obj->patch_name, obj->name);
- klp_unpatch_object(obj);
-
- klp_post_unpatch_callback(obj);
-
- klp_free_object_loaded(obj);
- break;
- }
- }
-}
-
int klp_module_coming(struct module *mod)
{
char patch_name[MODULE_NAME_LEN];
@@ -1404,19 +1373,28 @@ int klp_module_coming(struct module *mod)

void klp_module_going(struct module *mod)
{
+ struct klp_patch *patch;
+ struct klp_object *obj, *tmp_obj;
+
if (WARN_ON(mod->state != MODULE_STATE_GOING &&
mod->state != MODULE_STATE_COMING))
return;

mutex_lock(&klp_mutex);
/*
- * Each module has to know that klp_module_going()
- * has been called. We never know what module will
- * get patched by a new patch.
+ * All already enabled livepatches for this module as going to be
+ * removed now. From this point, klp_enable_patch() must not load
+ * any new livepatch modules for this module.
*/
mod->klp_alive = false;

- klp_cleanup_module_patches_limited(mod, NULL);
+ klp_for_each_patch(patch) {
+ klp_for_each_object_safe(patch, obj, tmp_obj) {
+ if (obj->name && !strcmp(obj->name, mod->name)) {
+ klp_remove_object(obj);
+ }
+ }
+ }

mutex_unlock(&klp_mutex);
}
--
2.16.4

2020-01-17 15:07:09

by Petr Mladek

[permalink] [raw]
Subject: [POC 04/23] livepatch: Prevent loading livepatch sub-module unintentionally.

Livepatch is split into several modules. The main module is for livepatching
vmlinux. The rest is for livepatching other modules.

Only the livepatch module for vmlinux can be loaded by users. Others are
loaded automatically when the related module is or gets loaded.

Users might try to load any livepatch module. It must be allowed
only when the related livepatch module for vmlinux and the livepatched
module are loaded.

Also it is important to check that obj->name is listed in patch->obj_names.
Otherwise this module would not be loaded automatically. And it would
lead into inconsistent behavier. Anyway, the missing name means a mistake
somewhere and must be reported.

klp_add_object() is taking over the job done by klp_module_coming().
The error message is taken from there so that selftests do not need
to get updated.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 77 insertions(+), 1 deletion(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index ec7ffc7db3a7..e2c7dc6c2d5f 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -88,6 +88,18 @@ static struct klp_object *klp_find_object(struct klp_patch *patch,
return NULL;
}

+static struct klp_patch *klp_find_patch(const char *patch_name)
+{
+ struct klp_patch *patch;
+
+ klp_for_each_patch(patch) {
+ if (!strcmp(patch->obj->patch_name, patch_name))
+ return patch;
+ }
+
+ return NULL;
+}
+
struct klp_find_arg {
const char *objname;
const char *name;
@@ -920,15 +932,79 @@ static int klp_check_object(struct klp_object *obj, bool is_module)
return klp_check_module_name(obj, is_module);
}

+static bool klp_is_object_name_supported(struct klp_patch *patch,
+ const char *name)
+{
+ int i;
+
+ for (i = 0; patch->obj_names[i]; i++) {
+ if (!strcmp(name, patch->obj_names[i]))
+ return true;
+ }
+
+ return false;
+}
+
+/* Must be called under klp_mutex */
+static bool klp_is_object_compatible(struct klp_patch *patch,
+ struct klp_object *obj)
+{
+ struct module *patched_mod;
+
+ if (!klp_is_object_name_supported(patch, obj->name)) {
+ pr_err("Livepatch (%s) is not supposed to livepatch the module: %s\n",
+ obj->patch_name, obj->name);
+ return false;
+ }
+
+ mutex_lock(&module_mutex);
+ patched_mod = find_module(obj->name);
+ mutex_unlock(&module_mutex);
+
+ if (!patched_mod) {
+ pr_err("Livepatched module is not loaded: %s\n", obj->name);
+ return false;
+ }
+
+ return true;
+}
+
int klp_add_object(struct klp_object *obj)
{
+ struct klp_patch *patch;
int ret;

ret = klp_check_object(obj, true);
if (ret)
return ret;

+ mutex_lock(&klp_mutex);
+
+ patch = klp_find_patch(obj->patch_name);
+ if (!patch) {
+ pr_err("Can't load livepatch (%s) for module when the livepatch (%s) for vmcore is not loaded\n",
+ obj->mod->name, obj->patch_name);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (!klp_is_object_compatible(patch, obj)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ mutex_unlock(&klp_mutex);
return 0;
+
+err:
+ /*
+ * If a patch is unsuccessfully applied, return
+ * error to the module loader.
+ */
+ pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n",
+ patch->obj->patch_name, obj->name, obj->name);
+ mutex_unlock(&klp_mutex);
+ return ret;
}
EXPORT_SYMBOL_GPL(klp_add_object);

@@ -1033,7 +1109,7 @@ int klp_enable_patch(struct klp_patch *patch)
{
int ret;

- if (!patch || !patch->obj)
+ if (!patch || !patch->obj || !patch->obj_names)
return -EINVAL;

ret = klp_check_object(patch->obj, false);
--
2.16.4

2020-01-17 15:07:25

by Petr Mladek

[permalink] [raw]
Subject: [POC 05/23] livepatch: Initialize and free livepatch submodule

Another step when loading livepatches to livepatch modules is
to initialize the structure, create sysfs entries, do livepatch
specific relocations.

These operation can fail and the objects must be freed that case.
The error message is taken from klp_module_coming() to match
selftests.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 34 +++++++++++++++++++++++++++-------
1 file changed, 27 insertions(+), 7 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index e2c7dc6c2d5f..6c27b635e5a7 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -583,18 +583,23 @@ static void klp_free_object_loaded(struct klp_object *obj)
}
}

+static void klp_free_object(struct klp_object *obj, bool nops_only)
+{
+ __klp_free_funcs(obj, nops_only);
+
+ if (nops_only && !obj->dynamic)
+ return;
+
+ list_del(&obj->node);
+ kobject_put(&obj->kobj);
+}
+
static void __klp_free_objects(struct klp_patch *patch, bool nops_only)
{
struct klp_object *obj, *tmp_obj;

klp_for_each_object_safe(patch, obj, tmp_obj) {
- __klp_free_funcs(obj, nops_only);
-
- if (nops_only && !obj->dynamic)
- continue;
-
- list_del(&obj->node);
- kobject_put(&obj->kobj);
+ klp_free_object(obj, nops_only);
}
}

@@ -812,6 +817,8 @@ static int klp_init_object_early(struct klp_patch *patch,
if (obj->dynamic || try_module_get(obj->mod))
return 0;

+ /* patch stays when this function fails in klp_add_object() */
+ list_del(&obj->node);
return -ENODEV;
}

@@ -993,9 +1000,22 @@ int klp_add_object(struct klp_object *obj)
goto err;
}

+ ret = klp_init_object_early(patch, obj);
+ if (ret)
+ goto err;
+
+ ret = klp_init_object(patch, obj);
+ if (ret) {
+ pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
+ patch->obj->patch_name, obj->name, ret);
+ goto err_free;
+ }
+
mutex_unlock(&klp_mutex);
return 0;

+err_free:
+ klp_free_object(obj, false);
err:
/*
* If a patch is unsuccessfully applied, return
--
2.16.4

2020-01-17 15:07:37

by Petr Mladek

[permalink] [raw]
Subject: [POC 07/23] livepatch: Remove obsolete functionality from klp_module_coming()

klp_module_coming() could not operate on the livepatch structures.
Instead it has to load the livepatch module that will call
klp_add_object().

The functionality has been migrated to klp_add_object() in
the previous commits and the code can be finally removed.

The only exception is mod->klp_alive flag. It will still be needed
to decide who is responsible for loading/removing the eventual
livepatch modules.

It is done this way to make the changes manageable and easier
to review.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 59 -------------------------------------------------
1 file changed, 59 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index c21bd9ec2012..bb851f916182 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -1290,10 +1290,6 @@ static void klp_cleanup_module_patches_limited(struct module *mod,

int klp_module_coming(struct module *mod)
{
- int ret;
- struct klp_patch *patch;
- struct klp_object *obj;
-
if (WARN_ON(mod->state != MODULE_STATE_COMING))
return -EINVAL;

@@ -1304,64 +1300,9 @@ int klp_module_coming(struct module *mod)
* get patched by a new patch.
*/
mod->klp_alive = true;
-
- klp_for_each_patch(patch) {
- klp_for_each_object(patch, obj) {
- if (!klp_is_module(obj) || strcmp(obj->name, mod->name))
- continue;
-
- obj->mod = mod;
-
- ret = klp_init_object_loaded(obj);
- if (ret) {
- pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
- patch->obj->patch_name, obj->name, ret);
- goto err;
- }
-
- pr_notice("applying patch '%s' to loading module '%s'\n",
- patch->obj->patch_name, obj->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->obj->patch_name, obj->name, ret);
-
- klp_post_unpatch_callback(obj);
- goto err;
- }
-
- if (patch != klp_transition_patch)
- klp_post_patch_callback(obj);
-
- break;
- }
- }
-
mutex_unlock(&klp_mutex);

return 0;
-
-err:
- /*
- * If a patch is unsuccessfully applied, return
- * error to the module loader.
- */
- pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n",
- patch->obj->patch_name, obj->name, obj->name);
- mod->klp_alive = false;
- obj->mod = NULL;
- klp_cleanup_module_patches_limited(mod, patch);
- mutex_unlock(&klp_mutex);
-
- return ret;
}

void klp_module_going(struct module *mod)
--
2.16.4

2020-01-17 15:07:57

by Petr Mladek

[permalink] [raw]
Subject: [POC 22/23] livepatch/module: Remove obsolete copy_module_elf()

The split livepatch modules can be relocated immedidately when they
are loaded. There is no longer needed to preserve the elf sections.

Signed-off-by: Petr Mladek <[email protected]>
---
Documentation/livepatch/module-elf-format.rst | 18 ++++++
include/linux/module.h | 3 -
kernel/module.c | 87 ---------------------------
3 files changed, 18 insertions(+), 90 deletions(-)

diff --git a/Documentation/livepatch/module-elf-format.rst b/Documentation/livepatch/module-elf-format.rst
index 9f0c997d4940..8c6b894c4661 100644
--- a/Documentation/livepatch/module-elf-format.rst
+++ b/Documentation/livepatch/module-elf-format.rst
@@ -14,6 +14,7 @@ This document outlines the Elf format requirements that livepatch modules must f
4. Livepatch symbols
4.1 A livepatch module's symbol table
4.2 Livepatch symbol format
+ 5. Symbol table and Elf section access

1. Background and motivation
============================
@@ -295,3 +296,20 @@ See include/uapi/linux/elf.h for the actual definitions.
[*]
Note that the 'Ndx' (Section index) for these symbols is SHN_LIVEPATCH (0xff20).
"OS" means OS-specific.
+
+5. Symbol table and Elf section access
+======================================
+A livepatch module's symbol table is accessible through module->symtab.
+
+Since apply_relocate_add() requires access to a module's section headers,
+symbol table, and relocation section indices, Elf information is preserved for
+livepatch modules and is made accessible by the module loader through
+module->klp_info, which is a klp_modinfo struct. When a livepatch module loads,
+this struct is filled in by the module loader. Its fields are documented below::
+
+ struct klp_modinfo {
+ Elf_Ehdr hdr; /* Elf header */
+ Elf_Shdr *sechdrs; /* Section header table */
+ char *secstrings; /* String table for the section headers */
+ unsigned int symndx; /* The symbol table section index */
+ };
diff --git a/include/linux/module.h b/include/linux/module.h
index f69f3fd72dd5..8545f3087274 100644
--- a/include/linux/module.h
+++ b/include/linux/module.h
@@ -483,9 +483,6 @@ struct module {
#ifdef CONFIG_LIVEPATCH
bool klp; /* Is this a livepatch module? */
bool klp_alive;
-
- /* Elf information */
- struct klp_modinfo *klp_info;
#endif

#ifdef CONFIG_MODULE_UNLOAD
diff --git a/kernel/module.c b/kernel/module.c
index c14b5135db27..442926fc5f34 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2125,82 +2125,6 @@ static void module_enable_nx(const struct module *mod) { }
static void module_enable_x(const struct module *mod) { }
#endif /* CONFIG_ARCH_HAS_STRICT_MODULE_RWX */

-
-#ifdef CONFIG_LIVEPATCH
-/*
- * Persist Elf information about a module. Copy the Elf header,
- * section header table, section string table, and symtab section
- * index from info to mod->klp_info.
- */
-static int copy_module_elf(struct module *mod, struct load_info *info)
-{
- unsigned int size, symndx;
- int ret;
-
- size = sizeof(*mod->klp_info);
- mod->klp_info = kmalloc(size, GFP_KERNEL);
- if (mod->klp_info == NULL)
- return -ENOMEM;
-
- /* Elf header */
- size = sizeof(mod->klp_info->hdr);
- memcpy(&mod->klp_info->hdr, info->hdr, size);
-
- /* Elf section header table */
- size = sizeof(*info->sechdrs) * info->hdr->e_shnum;
- mod->klp_info->sechdrs = kmemdup(info->sechdrs, size, GFP_KERNEL);
- if (mod->klp_info->sechdrs == NULL) {
- ret = -ENOMEM;
- goto free_info;
- }
-
- /* Elf section name string table */
- size = info->sechdrs[info->hdr->e_shstrndx].sh_size;
- mod->klp_info->secstrings = kmemdup(info->secstrings, size, GFP_KERNEL);
- if (mod->klp_info->secstrings == NULL) {
- ret = -ENOMEM;
- goto free_sechdrs;
- }
-
- /* Elf symbol section index */
- symndx = info->index.sym;
- mod->klp_info->symndx = symndx;
-
- /*
- * For livepatch modules, core_kallsyms.symtab is a complete
- * copy of the original symbol table. Adjust sh_addr to point
- * to core_kallsyms.symtab since the copy of the symtab in module
- * init memory is freed at the end of do_init_module().
- */
- mod->klp_info->sechdrs[symndx].sh_addr = \
- (unsigned long) mod->core_kallsyms.symtab;
-
- return 0;
-
-free_sechdrs:
- kfree(mod->klp_info->sechdrs);
-free_info:
- kfree(mod->klp_info);
- return ret;
-}
-
-static void free_module_elf(struct module *mod)
-{
- kfree(mod->klp_info->sechdrs);
- kfree(mod->klp_info->secstrings);
- kfree(mod->klp_info);
-}
-#else /* !CONFIG_LIVEPATCH */
-static int copy_module_elf(struct module *mod, struct load_info *info)
-{
- return 0;
-}
-
-static void free_module_elf(struct module *mod)
-{
-}
-#endif /* CONFIG_LIVEPATCH */
-
void __weak module_memfree(void *module_region)
{
/*
@@ -2244,9 +2168,6 @@ static void free_module(struct module *mod)
/* Free any allocated parameters. */
destroy_params(mod->kp, mod->num_kp);

- if (is_livepatch_module(mod))
- free_module_elf(mod);
-
/* Now we can delete it from the lists */
mutex_lock(&module_mutex);
/* Unlink carefully: kallsyms could be walking list. */
@@ -3967,12 +3888,6 @@ static int load_module(struct load_info *info, const char __user *uargs,
if (err < 0)
goto coming_cleanup;

- if (is_livepatch_module(mod)) {
- err = copy_module_elf(mod, info);
- if (err < 0)
- goto sysfs_cleanup;
- }
-
/* Get rid of temporary copy. */
free_copy(info);

@@ -3981,8 +3896,6 @@ static int load_module(struct load_info *info, const char __user *uargs,

return do_init_module(mod);

- sysfs_cleanup:
- mod_sysfs_teardown(mod);
coming_cleanup:
mod->state = MODULE_STATE_GOING;
destroy_params(mod->kp, mod->num_kp);
--
2.16.4

2020-01-17 15:08:10

by Petr Mladek

[permalink] [raw]
Subject: [POC 14/23] livepatch: Never block livepatch modules when the related module is being removed

Originally, it was not possible to remove the single livepatch module when
the code was used during a forced transition. There were no guarantees that
the code was no longer used.

Even the split livepatch modules have to stay when the entire livepatch
gets disabled/replaced and the livepatch modules were used during
a forced transition.

On the other hand, klp_module_going() callback is called when the patched
module is about to be removed. It's code should no longer be used.
The same should be true also for the related livepatch module. Therefore
the livepatch module could always get removed as well.

It allows to load the patched module again in the future. Otherwise,
it would get blocked because the related livepatch module could not
get loaded more times.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 73462b66f63f..4b55d805f3ec 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -1391,6 +1391,13 @@ void klp_module_going(struct module *mod)
klp_for_each_patch(patch) {
klp_for_each_object_safe(patch, obj, tmp_obj) {
if (obj->name && !strcmp(obj->name, mod->name)) {
+ /*
+ * The livepatched module is about to be
+ * destroyed. It's code is no longer used.
+ * Same is true for the livepatch even when
+ * it was part of forced transition.
+ */
+ obj->forced = false;
klp_remove_object(obj);
}
}
--
2.16.4

2020-01-17 15:08:20

by Petr Mladek

[permalink] [raw]
Subject: [POC 11/23] livepatch: Safely detect forced transition when removing split livepatch modules

The information about forced livepatch transition is currently stored
in struct klp_patch. But there is not any obvious safe way how to
access it when the split livepatch modules are removed.

module_put() for the livepatch module must be called only when neither
the code nor the data are accessed. The best location is
klp_kobj_release_object(). It can be called asynchronously when
the kobject hierarchy is already destroyed and struct klp_patch might
already be gone.

Solve this by moving the "force" flag into struct klp_object.

Signed-off-by: Petr Mladek <[email protected]>
---
include/linux/livepatch.h | 4 ++--
kernel/livepatch/core.c | 11 ++++++++---
kernel/livepatch/transition.c | 8 ++++++--
3 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index feb33f023f9f..e021e512b207 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -114,6 +114,7 @@ struct klp_callbacks {
* @node: list node for klp_patch obj_list
* @dynamic: temporary object for nop functions; dynamically allocated
* @patched: the object's funcs have been added to the klp_ops list
+ * @forced: was involved in a forced transition
*/
struct klp_object {
/* external */
@@ -129,6 +130,7 @@ struct klp_object {
struct list_head node;
bool dynamic;
bool patched;
+ bool forced;
};

/**
@@ -154,7 +156,6 @@ struct klp_state {
* @kobj: kobject for sysfs resources
* @obj_list: dynamic list of the object entries
* @enabled: the patch is enabled (but operation may be incomplete)
- * @forced: was involved in a forced transition
* ts: timestamp when the livepatch has been loaded
* @free_work: patch cleanup from workqueue-context
* @finish: for waiting till it is safe to remove the patch module
@@ -171,7 +172,6 @@ struct klp_patch {
struct kobject kobj;
struct list_head obj_list;
bool enabled;
- bool forced;
u64 ts;
struct work_struct free_work;
struct completion finish;
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 19ca8baa2f16..2f15ff360676 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -551,8 +551,13 @@ static void klp_kobj_release_object(struct kobject *kobj)

obj = container_of(kobj, struct klp_object, kobj);

- if (obj->dynamic)
+ if (obj->dynamic) {
klp_free_object_dynamic(obj);
+ return;
+ }
+
+ if (klp_is_module(obj) && !obj->forced)
+ module_put(obj->mod);
}

static struct kobj_type klp_ktype_object = {
@@ -668,7 +673,7 @@ static void klp_free_patch_finish(struct klp_patch *patch)
wait_for_completion(&patch->finish);

/* Put the module after the last access to struct klp_patch. */
- if (!patch->forced)
+ if (!patch->obj->forced)
module_put(patch->obj->mod);
}

@@ -829,6 +834,7 @@ static int klp_init_object_early(struct klp_patch *patch,
INIT_LIST_HEAD(&obj->func_list);
kobject_init(&obj->kobj, &klp_ktype_object);
list_add_tail(&obj->node, &patch->obj_list);
+ obj->forced = false;

klp_for_each_func_static(obj, func) {
klp_init_func_early(obj, func);
@@ -854,7 +860,6 @@ static int klp_init_patch_early(struct klp_patch *patch)
INIT_LIST_HEAD(&patch->obj_list);
kobject_init(&patch->kobj, &klp_ktype_patch);
patch->enabled = false;
- patch->forced = false;
patch->ts = local_clock();
INIT_WORK(&patch->free_work, klp_free_patch_work_fn);
init_completion(&patch->finish);
diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
index 78e3280560cd..e99c27a5ddbf 100644
--- a/kernel/livepatch/transition.c
+++ b/kernel/livepatch/transition.c
@@ -626,6 +626,7 @@ void klp_copy_process(struct task_struct *child)
void klp_force_transition(void)
{
struct klp_patch *patch;
+ struct klp_object *obj;
struct task_struct *g, *task;
unsigned int cpu;

@@ -639,6 +640,9 @@ void klp_force_transition(void)
for_each_possible_cpu(cpu)
klp_update_patch_state(idle_task(cpu));

- klp_for_each_patch(patch)
- patch->forced = true;
+ klp_for_each_patch(patch) {
+ klp_for_each_object(patch, obj) {
+ obj->forced = true;
+ }
+ }
}
--
2.16.4

2020-01-17 15:08:31

by Petr Mladek

[permalink] [raw]
Subject: [POC 12/23] livepatch: Automatically remove livepatch module when the object is freed

Make it easy to deal with the split livepatch modules. Remove the livepatch
modules that livepatched modules when they are no longer used.

It must be done from workqueue context to avoid deadlocks.

It must not be done when klp_add_module() fails because is called from
the livepatch module init callback. The module will not load at
all in this case.

Signed-off-by: Petr Mladek <[email protected]>
---
include/linux/livepatch.h | 4 ++++
kernel/livepatch/core.c | 19 ++++++++++++++++++-
2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index e021e512b207..4afb7f3a5a36 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -115,6 +115,8 @@ struct klp_callbacks {
* @dynamic: temporary object for nop functions; dynamically allocated
* @patched: the object's funcs have been added to the klp_ops list
* @forced: was involved in a forced transition
+ * @add_err: failed to add the object when loading the livepatch module
+ * @remove_work: remove module from workqueue-context
*/
struct klp_object {
/* external */
@@ -131,6 +133,8 @@ struct klp_object {
bool dynamic;
bool patched;
bool forced;
+ bool add_err;
+ struct work_struct remove_work;
};

/**
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 2f15ff360676..6c51b194da57 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -556,8 +556,14 @@ static void klp_kobj_release_object(struct kobject *kobj)
return;
}

- if (klp_is_module(obj) && !obj->forced)
+ if (obj->forced || !klp_is_module(obj))
+ return;
+
+ /* Must not explicitely remove module when adding failed. */
+ if (obj->add_err)
module_put(obj->mod);
+ else
+ schedule_work(&obj->remove_work);
}

static struct kobj_type klp_ktype_object = {
@@ -677,6 +683,14 @@ static void klp_free_patch_finish(struct klp_patch *patch)
module_put(patch->obj->mod);
}

+static void klp_remove_module_work_fn(struct work_struct *work)
+{
+ struct klp_object *obj =
+ container_of(work, struct klp_object, remove_work);
+
+ module_put_and_delete(obj->mod);
+}
+
/*
* The livepatch might be freed from sysfs interface created by the patch.
* This work allows to wait until the interface is destroyed in a separate
@@ -835,6 +849,8 @@ static int klp_init_object_early(struct klp_patch *patch,
kobject_init(&obj->kobj, &klp_ktype_object);
list_add_tail(&obj->node, &patch->obj_list);
obj->forced = false;
+ obj->add_err = false;
+ INIT_WORK(&obj->remove_work, klp_remove_module_work_fn);

klp_for_each_func_static(obj, func) {
klp_init_func_early(obj, func);
@@ -1063,6 +1079,7 @@ int klp_add_object(struct klp_object *obj)
return 0;

err_free:
+ obj->add_err = true;
klp_free_object(obj, false);
err:
/*
--
2.16.4

2020-01-17 15:08:35

by Petr Mladek

[permalink] [raw]
Subject: [POC 08/23] livepatch: Automatically load livepatch module when the patch module is loaded

The klp_module_coming() callback is called from the module loader when
any module is being loaded. It allows to load the related livepatch modules
in MODULE_COMMING state before mod->init() is called. It prevents the module
from loading when the livepatching fails from any reason.

klp_module_coming() originally did several tasks: livepatch-specific
reallocations, registered ftrace handlers, and called object callbacks
when any defined.

All the mentioned tasks were moved into by klp_add_object() that is
called in mod->init() of livepatch modules that are livepatching
other modules.

Instead, klp_module_coming() has to load the needed livepatch module(s).
This functionality is already used in the kernel.

It is solved by two tricks:

1. The list is searched repeatedly from the beginning. The already
loaded objects are skipped. One object is handled in each
iteration.

This solves the problem when a livepatch is removed or added
in the meantime. Especially, it prevents a crash when
the given struct klp_patch disappeared from the list in
the meantime.

The object will get removed automatically when the livepatch
gets removed.

There might be an attempt to load the module twice: via
the coming notifier and via klp_enable_livepatch(). It is
already handled in the module loader code. Both call will
either succeed or fail the same way.

2. There might be a false error when the livepatch gets removed
in the meantime. It is solved by double checking the existence.

It does not solve the situation when the livepatch is removed
and loaded again in the meantime. It will be solved by a separate
patch. Anyway, it is not much realistic scenario.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 81 ++++++++++++++++++++--
.../testing/selftests/livepatch/test-callbacks.sh | 1 +
2 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index bb851f916182..34e3ee2be7ef 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -8,6 +8,7 @@

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

+#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mutex.h>
@@ -100,6 +101,24 @@ static struct klp_patch *klp_find_patch(const char *patch_name)
return NULL;
}

+/*
+ * Search whether livepatch for a module is loaded.
+ * Do not use for "vmlinux" that is always loaded.
+ * Must be called under klp_mutex.
+ */
+static bool klp_is_object_loaded(struct klp_patch *patch,
+ char *object_name)
+{
+ struct klp_object *obj;
+
+ klp_for_each_object(patch, obj) {
+ if (obj->name && !strcmp(object_name, obj->name))
+ return true;
+ }
+
+ return false;
+}
+
struct klp_find_arg {
const char *objname;
const char *name;
@@ -1082,6 +1101,19 @@ static int __klp_disable_patch(struct klp_patch *patch)
return 0;
}

+static int klp_try_load_object(const char *patch_name, const char *obj_name)
+{
+ int ret;
+
+ ret = request_module("%s__%s", patch_name, obj_name);
+ if (ret) {
+ pr_info("Module load failed: %s__%s\n", patch_name, obj_name);
+ return ret;
+ }
+
+ return 0;
+}
+
static int __klp_enable_patch(struct klp_patch *patch)
{
struct klp_object *obj;
@@ -1290,19 +1322,58 @@ static void klp_cleanup_module_patches_limited(struct module *mod,

int klp_module_coming(struct module *mod)
{
+ char patch_name[MODULE_NAME_LEN];
+ struct klp_patch *patch;
+ int ret = 0;
+
if (WARN_ON(mod->state != MODULE_STATE_COMING))
return -EINVAL;

mutex_lock(&klp_mutex);
+restart:
+ klp_for_each_patch(patch) {
+ if (!klp_is_object_name_supported(patch, mod->name))
+ continue;
+
+ if (klp_is_object_loaded(patch, mod->name))
+ continue;
+
+ strncpy(patch_name, patch->obj->patch_name, sizeof(patch_name));
+ mutex_unlock(&klp_mutex);
+
+ ret = klp_try_load_object(patch_name, mod->name);
+ /*
+ * The load might have failed because the patch has
+ * been removed in the meantime. In this case, the
+ * error might be ignored.
+ *
+ * FIXME: It is not fully proof. The patch might have be
+ * unloaded and loaded again in the mean time.
+ */
+ mutex_lock(&klp_mutex);
+ if (ret) {
+ patch = klp_find_patch(patch_name);
+ if (patch)
+ goto err;
+ ret = 0;
+ }
+
+ /*
+ * The list of patches might have been manipulated
+ * in the meantime.
+ */
+ goto restart;
+ }
+
/*
- * Each module has to know that klp_module_coming()
- * has been called. We never know what module will
- * get patched by a new patch.
+ * All enabled livepatches are loaded now. From this point, any newly
+ * enabled livepatch is responsible for loading the related livepatch
+ * module in klp_enable_patch().
*/
mod->klp_alive = true;
+err:
mutex_unlock(&klp_mutex);
-
- return 0;
+ return ret;
}

void klp_module_going(struct module *mod)
diff --git a/tools/testing/selftests/livepatch/test-callbacks.sh b/tools/testing/selftests/livepatch/test-callbacks.sh
index ccaed35d0901..39a4f35e5f8e 100755
--- a/tools/testing/selftests/livepatch/test-callbacks.sh
+++ b/tools/testing/selftests/livepatch/test-callbacks.sh
@@ -330,6 +330,7 @@ livepatch: applying patch '$MOD_LIVEPATCH' to loading module '$MOD_TARGET'
$MOD_LIVEPATCH: pre_patch_callback: $MOD_TARGET -> [MODULE_STATE_COMING] Full formed, running module_init
livepatch: pre-patch callback failed for object '$MOD_TARGET'
livepatch: patch '$MOD_LIVEPATCH' failed for module '$MOD_TARGET', refusing to load module '$MOD_TARGET'
+livepatch: Module load failed: ${MOD_LIVEPATCH}__${MOD_TARGET}
modprobe: ERROR: could not insert '$MOD_TARGET': No such device
% echo 0 > /sys/kernel/livepatch/$MOD_LIVEPATCH/enabled
livepatch: '$MOD_LIVEPATCH': initializing unpatching transition
--
2.16.4

2020-01-17 15:08:36

by Petr Mladek

[permalink] [raw]
Subject: [POC 03/23] livepatch: Better checks of struct klp_object definition

The number of user defined fields increased in struct klp_object after
spliting per-object livepatches. It was sometimes unclear why exactly
the module could not get loded when returned -EINVAL.

Add more checks for the split modules and write useful error
messages on particular errors.

Signed-off-by: Petr Mladek <[email protected]>
---
kernel/livepatch/core.c | 91 ++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 82 insertions(+), 9 deletions(-)

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index bb62c5407b75..ec7ffc7db3a7 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -756,9 +756,6 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
int ret;
const char *name;

- if (klp_is_module(obj) && strlen(obj->name) >= MODULE_NAME_LEN)
- return -EINVAL;
-
obj->patched = false;

name = obj->name ? obj->name : "vmlinux";
@@ -851,8 +848,86 @@ static int klp_init_patch(struct klp_patch *patch)
return 0;
}

+static int klp_check_module_name(struct klp_object *obj, bool is_module)
+{
+ char mod_name[MODULE_NAME_LEN];
+ const char *expected_name;
+
+ if (is_module) {
+ snprintf(mod_name, sizeof(mod_name), "%s__%s",
+ obj->patch_name, obj->name);
+ expected_name = mod_name;
+ } else {
+ expected_name = obj->patch_name;
+ }
+
+ if (strcmp(expected_name, obj->mod->name)) {
+ pr_err("The module name %s does not match with obj->patch_name and obj->name. The expected name is: %s\n",
+ obj->mod->name, expected_name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int klp_check_object(struct klp_object *obj, bool is_module)
+{
+
+ if (!obj->mod)
+ return -EINVAL;
+
+ if (!is_livepatch_module(obj->mod)) {
+ pr_err("module %s is not marked as a livepatch module\n",
+ obj->mod->name);
+ return -EINVAL;
+ }
+
+ if (!obj->patch_name) {
+ pr_err("module %s does not have set obj->patch_name\n",
+ obj->mod->name);
+ return -EINVAL;
+ }
+
+ if (strlen(obj->patch_name) >= MODULE_NAME_LEN) {
+ pr_err("module %s has too long obj->patch_name\n",
+ obj->mod->name);
+ return -EINVAL;
+ }
+
+ if (is_module) {
+ if (!obj->name) {
+ pr_err("module %s does not have set obj->name\n",
+ obj->mod->name);
+ return -EINVAL;
+ }
+ if (strlen(obj->name) >= MODULE_NAME_LEN) {
+ pr_err("module %s has too long obj->name\n",
+ obj->mod->name);
+ return -EINVAL;
+ }
+ } else if (obj->name) {
+ pr_err("module %s for vmlinux must not have set obj->name\n",
+ obj->mod->name);
+ return -EINVAL;
+ }
+
+ if (!obj->funcs) {
+ pr_err("module %s does not have set obj->funcs\n",
+ obj->mod->name);
+ return -EINVAL;
+ }
+
+ return klp_check_module_name(obj, is_module);
+}
+
int klp_add_object(struct klp_object *obj)
{
+ int ret;
+
+ ret = klp_check_object(obj, true);
+ if (ret)
+ return ret;
+
return 0;
}
EXPORT_SYMBOL_GPL(klp_add_object);
@@ -958,14 +1033,12 @@ int klp_enable_patch(struct klp_patch *patch)
{
int ret;

- if (!patch || !patch->obj || !patch->obj->mod)
+ if (!patch || !patch->obj)
return -EINVAL;

- if (!is_livepatch_module(patch->obj->mod)) {
- pr_err("module %s is not marked as a livepatch module\n",
- patch->obj->patch_name);
- return -EINVAL;
- }
+ ret = klp_check_object(patch->obj, false);
+ if (ret)
+ return ret;

if (!klp_initialized())
return -ENODEV;
--
2.16.4

2020-01-17 15:08:36

by Petr Mladek

[permalink] [raw]
Subject: [POC 09/23] livepatch: Handle race when livepatches are reloaded during a module load

klp_module_coming() might fail to load a livepatch module when
the related livepatch gets reloaded in the meantime.

Detect this situation by adding a timestamp into struct klp_patch.
local_clock is enough because klp_mutex must be released and taken
several times during this scenario.

Signed-off-by: Petr Mladek <[email protected]>
---
include/linux/livepatch.h | 2 ++
kernel/livepatch/core.c | 9 +++++----
2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index a4567c17a9f2..feb33f023f9f 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -155,6 +155,7 @@ struct klp_state {
* @obj_list: dynamic list of the object entries
* @enabled: the patch is enabled (but operation may be incomplete)
* @forced: was involved in a forced transition
+ * ts: timestamp when the livepatch has been loaded
* @free_work: patch cleanup from workqueue-context
* @finish: for waiting till it is safe to remove the patch module
*/
@@ -171,6 +172,7 @@ struct klp_patch {
struct list_head obj_list;
bool enabled;
bool forced;
+ u64 ts;
struct work_struct free_work;
struct completion finish;
};
diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index 34e3ee2be7ef..8e693c58b736 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -20,6 +20,7 @@
#include <linux/moduleloader.h>
#include <linux/completion.h>
#include <linux/memory.h>
+#include <linux/sched/clock.h>
#include <asm/cacheflush.h>
#include "core.h"
#include "patch.h"
@@ -854,6 +855,7 @@ static int klp_init_patch_early(struct klp_patch *patch)
kobject_init(&patch->kobj, &klp_ktype_patch);
patch->enabled = false;
patch->forced = false;
+ patch->ts = local_clock();
INIT_WORK(&patch->free_work, klp_free_patch_work_fn);
init_completion(&patch->finish);

@@ -1324,6 +1326,7 @@ int klp_module_coming(struct module *mod)
{
char patch_name[MODULE_NAME_LEN];
struct klp_patch *patch;
+ u64 patch_ts;
int ret = 0;

if (WARN_ON(mod->state != MODULE_STATE_COMING))
@@ -1339,6 +1342,7 @@ int klp_module_coming(struct module *mod)
continue;

strncpy(patch_name, patch->obj->patch_name, sizeof(patch_name));
+ patch_ts = patch->ts;
mutex_unlock(&klp_mutex);

ret = klp_try_load_object(patch_name, mod->name);
@@ -1346,14 +1350,11 @@ int klp_module_coming(struct module *mod)
* The load might have failed because the patch has
* been removed in the meantime. In this case, the
* error might be ignored.
- *
- * FIXME: It is not fully proof. The patch might have be
- * unloaded and loaded again in the mean time.
*/
mutex_lock(&klp_mutex);
if (ret) {
patch = klp_find_patch(patch_name);
- if (patch)
+ if (patch && patch->ts == patch_ts)
goto err;
ret = 0;
}
--
2.16.4

2020-01-18 10:32:50

by kernel test robot

[permalink] [raw]
Subject: Re: [POC 20/23] module/livepatch: Relocate local variables in the module loaded when the livepatch is being loaded

Hi Petr,

I love your patch! Yet something to improve:

[auto build test ERROR on jeyu/modules-next]
[also build test ERROR on kselftest/next tip/x86/core linus/master v5.5-rc6 next-20200117]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url: https://github.com/0day-ci/linux/commits/Petr-Mladek/livepatch-Split-livepatch-module-per-object/20200118-090135
base: https://git.kernel.org/pub/scm/linux/kernel/git/jeyu/linux.git modules-next
config: mips-32r2_defconfig (attached as .config)
compiler: mips-linux-gcc (GCC) 7.5.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
GCC_VERSION=7.5.0 make.cross ARCH=mips

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <[email protected]>

All errors (new ones prefixed by >>):

kernel/module.c: In function 'apply_relocations':
>> kernel/module.c:2418:10: error: implicit declaration of function 'klp_resolve_symbols'; did you mean 'resolve_symbol'? [-Werror=implicit-function-declaration]
err = klp_resolve_symbols(info->sechdrs, i, mod);
^~~~~~~~~~~~~~~~~~~
resolve_symbol
cc1: some warnings being treated as errors

vim +2418 kernel/module.c

2398
2399 static int apply_relocations(struct module *mod, const struct load_info *info)
2400 {
2401 unsigned int i;
2402 int err = 0;
2403
2404 /* Now do relocations. */
2405 for (i = 1; i < info->hdr->e_shnum; i++) {
2406 unsigned int infosec = info->sechdrs[i].sh_info;
2407
2408 /* Not a valid relocation section? */
2409 if (infosec >= info->hdr->e_shnum)
2410 continue;
2411
2412 /* Don't bother with non-allocated sections */
2413 if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
2414 continue;
2415
2416 /* Livepatch need to resolve static symbols. */
2417 if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) {
> 2418 err = klp_resolve_symbols(info->sechdrs, i, mod);
2419 if (err < 0)
2420 break;
2421 err = apply_relocate_add(info->sechdrs, info->strtab,
2422 info->index.sym, i, mod);
2423 } else if (info->sechdrs[i].sh_type == SHT_REL) {
2424 err = apply_relocate(info->sechdrs, info->strtab,
2425 info->index.sym, i, mod);
2426 } else if (info->sechdrs[i].sh_type == SHT_RELA) {
2427 err = apply_relocate_add(info->sechdrs, info->strtab,
2428 info->index.sym, i, mod);
2429 }
2430 if (err < 0)
2431 break;
2432 }
2433 return err;
2434 }
2435

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/[email protected] Intel Corporation


Attachments:
(No filename) (3.01 kB)
.config.gz (21.82 kB)
Download all attachments

2020-01-21 11:12:55

by Julien Thierry

[permalink] [raw]
Subject: Re: [POC 01/23] module: Allow to delete module also from inside kernel

Hi Petr,

On 1/17/20 3:03 PM, Petr Mladek wrote:
> Livepatch subsystems will need to automatically load and delete
> livepatch module when the livepatched one is being removed
> or when the entire livepatch is being removed.
>
> The code stopping the kernel module is moved to separate function
> so that it can be reused.
>
> The function always have rights to do the action. Also it does not
> need to search for struct module because it is already passed as
> a parameter.
>
> On the other hand, it has to make sure that the given struct module
> can't be released in parallel. It is achieved by combining module_put()
> and module_delete() functionality in a single function.
>
> This patch does not change the existing behavior of delete_module
> syscall.
>
> Signed-off-by: Petr Mladek <[email protected]>
>
> module: Add module_put_and_delete()
> ---
> include/linux/module.h | 5 +++
> kernel/module.c | 119 +++++++++++++++++++++++++++++++------------------
> 2 files changed, 80 insertions(+), 44 deletions(-)
>
> diff --git a/include/linux/module.h b/include/linux/module.h
> index bd165ba68617..f69f3fd72dd5 100644
> --- a/include/linux/module.h
> +++ b/include/linux/module.h
> @@ -623,6 +623,7 @@ extern void __module_get(struct module *module);
> extern bool try_module_get(struct module *module);
>
> extern void module_put(struct module *module);
> +extern int module_put_and_delete(struct module *mod);
>
> #else /*!CONFIG_MODULE_UNLOAD*/
> static inline bool try_module_get(struct module *module)
> @@ -632,6 +633,10 @@ static inline bool try_module_get(struct module *module)
> static inline void module_put(struct module *module)
> {
> }
> +static inline int module_put_and_delete(struct module *mod)
> +{
> + return 0;
> +}
> static inline void __module_get(struct module *module)
> {
> }
> diff --git a/kernel/module.c b/kernel/module.c
> index b56f3224b161..b3f11524f8f9 100644
> --- a/kernel/module.c
> +++ b/kernel/module.c
> @@ -964,62 +964,36 @@ EXPORT_SYMBOL(module_refcount);
> /* This exists whether we can unload or not */
> static void free_module(struct module *mod);
>
> -SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
> - unsigned int, flags)
> +int stop_module(struct module *mod, int flags)
> {
> - struct module *mod;
> - char name[MODULE_NAME_LEN];
> - int ret, forced = 0;
> -
> - if (!capable(CAP_SYS_MODULE) || modules_disabled)
> - return -EPERM;
> -
> - if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
> - return -EFAULT;
> - name[MODULE_NAME_LEN-1] = '\0';
> + int forced = 0;
>
> - audit_log_kern_module(name);
> -
> - if (mutex_lock_interruptible(&module_mutex) != 0)
> - return -EINTR;
> -
> - mod = find_module(name);
> - if (!mod) {
> - ret = -ENOENT;
> - goto out;
> - }
> -
> - if (!list_empty(&mod->source_list)) {
> - /* Other modules depend on us: get rid of them first. */
> - ret = -EWOULDBLOCK;
> - goto out;
> - }
> + /* Other modules depend on us: get rid of them first. */
> + if (!list_empty(&mod->source_list))
> + return -EWOULDBLOCK;
>
> /* Doing init or already dying? */
> if (mod->state != MODULE_STATE_LIVE) {
> /* FIXME: if (force), slam module count damn the torpedoes */
> pr_debug("%s already dying\n", mod->name);
> - ret = -EBUSY;
> - goto out;
> + return -EBUSY;
> }
>
> /* If it has an init func, it must have an exit func to unload */
> if (mod->init && !mod->exit) {
> forced = try_force_unload(flags);
> - if (!forced) {
> - /* This module can't be removed */
> - ret = -EBUSY;
> - goto out;
> - }
> + /* This module can't be removed */
> + if (!forced)
> + return -EBUSY;
> }
>
> /* Stop the machine so refcounts can't move and disable module. */
> - ret = try_stop_module(mod, flags, &forced);
> - if (ret != 0)
> - goto out;
> + return try_stop_module(mod, flags, &forced);
> +}
>
> - mutex_unlock(&module_mutex);
> - /* Final destruction now no one is using it. */
> +/* Final destruction now no one is using it. */

Nit: Looks like some copy/paste mixup

> +static void destruct_module(struct module *mod)
> +{
> if (mod->exit != NULL)
> mod->exit();
> blocking_notifier_call_chain(&module_notify_list,
> @@ -1033,8 +1007,44 @@ SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
> strlcpy(last_unloaded_module, mod->name, sizeof(last_unloaded_module));
>
> free_module(mod);
> +
> /* someone could wait for the module in add_unformed_module() */
> wake_up_all(&module_wq);
> +}
> +
> +SYSCALL_DEFINE2(delete_module, const char __user *, name_user,
> + unsigned int, flags)
> +{
> + struct module *mod;
> + char name[MODULE_NAME_LEN];
> + int ret;
> +
> + if (!capable(CAP_SYS_MODULE) || modules_disabled)
> + return -EPERM;
> +
> + if (strncpy_from_user(name, name_user, MODULE_NAME_LEN-1) < 0)
> + return -EFAULT;
> + name[MODULE_NAME_LEN-1] = '\0';
> +
> + audit_log_kern_module(name);
> +
> + if (mutex_lock_interruptible(&module_mutex) != 0)
> + return -EINTR;
> +
> + mod = find_module(name);
> + if (!mod) {
> + ret = -ENOENT;
> + goto out;
> + }
> +
> + ret = stop_module(mod, flags);
> + if (ret)
> + goto out;
> +
> + mutex_unlock(&module_mutex);
> +
> +/* Final destruction now no one is using it. */
> + destruct_module(mod);
> return 0;
> out:
> mutex_unlock(&module_mutex);
> @@ -1138,20 +1148,41 @@ bool try_module_get(struct module *module)
> }
> EXPORT_SYMBOL(try_module_get);
>
> -void module_put(struct module *module)
> +/* Must be called under module_mutex or with preemtion disabled */

Might be worth adding some asserts to check for that.

> +static void __module_put(struct module* module)
> {
> int ret;
>
> + ret = atomic_dec_if_positive(&module->refcnt);
> + WARN_ON(ret < 0); /* Failed to put refcount */
> + trace_module_put(module, _RET_IP_);
> +}
> +
> +void module_put(struct module *module)
> +{
> if (module) {
> preempt_disable();
> - ret = atomic_dec_if_positive(&module->refcnt);
> - WARN_ON(ret < 0); /* Failed to put refcount */
> - trace_module_put(module, _RET_IP_);
> + __module_put(module);
> preempt_enable();
> }
> }
> EXPORT_SYMBOL(module_put);
>
> +int module_put_and_delete(struct module *mod)
> +{
> + int ret;
> + mutex_lock(&module_mutex);
> + __module_put(mod);
> + ret = stop_module(mod, 0);
> + mutex_unlock(&module_mutex);
> +
> + if (ret)
> + return ret;
> +
> + destruct_module(mod);
> + return 0;
> +}
> +
> #else /* !CONFIG_MODULE_UNLOAD */
> static inline void print_unload_info(struct seq_file *m, struct module *mod)
> {
>

Thanks,

--
Julien Thierry

2020-01-21 11:13:22

by Julien Thierry

[permalink] [raw]
Subject: Re: [POC 02/23] livepatch: Split livepatch modules per livepatched object

Hi Petr,

On 1/17/20 3:03 PM, Petr Mladek wrote:
> One livepatch module allows to fix vmlinux and any number of modules
> while providing some guarantees defined by the consistency model.
>
> The patched modules can be loaded at any time: before, during,
> or after the livepatch module gets loaded. They can even get
> removed and loaded again. This variety of scenarios bring some
> troubles. For example, some livepatch symbols could be relocated
> only after the related module gets loaded. These changes need
> to get cleared when the module gets unloaded so that it can
> get eventually loaded again.
>
> As a result some functionality needs to be duplicated by
> the livepatching code. Some elf sections need to be preserved
> even when they normally can be removed during the module load.
> Architecture specific code is involved which makes harder
> adding support for new architectures and the maintainace.
>
> The solution is to split the livepatch module per livepatched
> object (vmlinux or module). Then both livepatch module and
> the livepatched modules could get loaded and removed at the
> same time.
>
> This require many changes in the livepatch subsystem, module
> loader, sample livepatches and livepatches needed for selftests.
>
> The bad news is that bisection will not work by definition.
> The good news is that it allows to do the changes in smaller
> steps.
>
> The first step allows to split the existing sample and testing
> modules so that they can be later user. It is done by
> the following changes:
>
> 1. struct klp_patch:
>
> + Add "patch_name" and "obj_names" to match all the related
> livepatch modules.
>
> + Replace "objs" array with a pointer to a single struct object.
>
> + move "mod" to struct object.
>
> 2. struct klp_object:
>
> + Add "patch_name" to match all the related livepatch modules.
>
> + "mod" points to the livepatch module instead of the livepatched
> one. The pointer to the livepatched module was used only to
> detect whether it was loaded. It will be always loaded
> with related livepatch module now.
>
> 3. klp_find_object_module() and klp_is_object_loaded() are no longer
> needed. Livepatch module is loaded only when the related livepatched
> module is loaded.
>
> 4. Add klp_add_object() function that will need to initialize
> struct object, link it into the related struct klp_patch,
> and patch the functions. It will get implemented later.
>
> The livepatches for modules are put into separate source files
> that define only struct klp_object() and call the new klp_add_object()
> in the init() callback. The name of the module follows the pattern:
>
> <patch_name>__<object_name>
>

Is that a requirement? Or is it just the convention followed for the
current tests?

> Signed-off-by: Petr Mladek <[email protected]>
> ---
> arch/x86/kernel/livepatch.c | 5 +-
> include/linux/livepatch.h | 20 +--
> kernel/livepatch/core.c | 139 +++++++-----------
> kernel/livepatch/core.h | 5 -
> kernel/livepatch/transition.c | 14 +-
> lib/livepatch/Makefile | 2 +
> lib/livepatch/test_klp_atomic_replace.c | 18 ++-
> lib/livepatch/test_klp_callbacks_demo.c | 90 ++++++------
> lib/livepatch/test_klp_callbacks_demo.h | 11 ++
> lib/livepatch/test_klp_callbacks_demo2.c | 62 ++++++---
> lib/livepatch/test_klp_callbacks_demo2.h | 11 ++
> ...t_klp_callbacks_demo__test_klp_callbacks_busy.c | 50 +++++++
> ...st_klp_callbacks_demo__test_klp_callbacks_mod.c | 42 ++++++
> lib/livepatch/test_klp_livepatch.c | 18 ++-
> lib/livepatch/test_klp_state.c | 53 ++++---
> lib/livepatch/test_klp_state2.c | 53 ++++---
> samples/livepatch/Makefile | 4 +
> samples/livepatch/livepatch-callbacks-demo.c | 90 ++++++------
> samples/livepatch/livepatch-callbacks-demo.h | 11 ++
> ...h-callbacks-demo__livepatch-callbacks-busymod.c | 54 +++++++
> ...patch-callbacks-demo__livepatch-callbacks-mod.c | 46 ++++++
> samples/livepatch/livepatch-sample.c | 18 ++-
> samples/livepatch/livepatch-shadow-fix1.c | 120 ++--------------
> .../livepatch-shadow-fix1__livepatch-shadow-mod.c | 155 +++++++++++++++++++++
> samples/livepatch/livepatch-shadow-fix2.c | 92 ++----------
> .../livepatch-shadow-fix2__livepatch-shadow-mod.c | 127 +++++++++++++++++
> .../testing/selftests/livepatch/test-callbacks.sh | 16 +--
> 27 files changed, 841 insertions(+), 485 deletions(-)
> create mode 100644 lib/livepatch/test_klp_callbacks_demo.h
> create mode 100644 lib/livepatch/test_klp_callbacks_demo2.h
> create mode 100644 lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_busy.c
> create mode 100644 lib/livepatch/test_klp_callbacks_demo__test_klp_callbacks_mod.c
> create mode 100644 samples/livepatch/livepatch-callbacks-demo.h
> create mode 100644 samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-busymod.c
> create mode 100644 samples/livepatch/livepatch-callbacks-demo__livepatch-callbacks-mod.c
> create mode 100644 samples/livepatch/livepatch-shadow-fix1__livepatch-shadow-mod.c
> create mode 100644 samples/livepatch/livepatch-shadow-fix2__livepatch-shadow-mod.c
>
> diff --git a/arch/x86/kernel/livepatch.c b/arch/x86/kernel/livepatch.c
> index 6a68e41206e7..728b44eaa168 100644
> --- a/arch/x86/kernel/livepatch.c
> +++ b/arch/x86/kernel/livepatch.c
> @@ -9,8 +9,7 @@
> #include <asm/text-patching.h>
>
> /* Apply per-object alternatives. Based on x86 module_finalize() */
> -void arch_klp_init_object_loaded(struct klp_patch *patch,
> - struct klp_object *obj)
> +void arch_klp_init_object_loaded(struct klp_object *obj)
> {
> int cnt;
> struct klp_modinfo *info;
> @@ -20,7 +19,7 @@ void arch_klp_init_object_loaded(struct klp_patch *patch,
> char sec_objname[MODULE_NAME_LEN];
> char secname[KSYM_NAME_LEN];
>
> - info = patch->mod->klp_info;
> + info = obj->mod->klp_info;
> objname = obj->name ? obj->name : "vmlinux";
>
> /* See livepatch core code for BUILD_BUG_ON() explanation */
> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> index e894e74905f3..a4567c17a9f2 100644
> --- a/include/linux/livepatch.h
> +++ b/include/linux/livepatch.h
> @@ -105,19 +105,21 @@ struct klp_callbacks {
> /**
> * struct klp_object - kernel object structure for live patching
> * @name: module name (or NULL for vmlinux)
> + * @patch_name: module name for vmlinux
> + * @mod: reference to the live patch module for this object
> * @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
> * @func_list: dynamic list of the function entries
> * @node: list node for klp_patch obj_list
> - * @mod: kernel module associated with the patched object
> - * (NULL for vmlinux)
> * @dynamic: temporary object for nop functions; dynamically allocated
> * @patched: the object's funcs have been added to the klp_ops list
> */
> struct klp_object {
> /* external */
> const char *name;
> + const char *patch_name;
> + struct module *mod;
> struct klp_func *funcs;
> struct klp_callbacks callbacks;
>
> @@ -125,7 +127,6 @@ struct klp_object {
> struct kobject kobj;
> struct list_head func_list;
> struct list_head node;
> - struct module *mod;
> bool dynamic;
> bool patched;
> };
> @@ -144,8 +145,9 @@ struct klp_state {
>
> /**
> * struct klp_patch - patch structure for live patching
> - * @mod: reference to the live patch module
> - * @objs: object entries for kernel objects to be patched
> + * @patch_name: livepatch name; same for related livepatch against other objects

You forgot to add that to the structure.

> + * @objs: object entry for vmlinux object

Nit: s/objs/obj/

> + * @obj_names: names of modules synchronously livepatched with this patch

Not sure I understand the purpose of this. Is it to check that the
klp_object that will get linked to this patch are part of a
pre-established set?

> * @states: system states that can get modified
> * @replace: replace all actively used patches
> * @list: list node for global list of actively used patches
> @@ -158,9 +160,9 @@ struct klp_state {
> */
> struct klp_patch {
> /* external */
> - struct module *mod;
> - struct klp_object *objs;
> struct klp_state *states;
> + struct klp_object *obj;
> + char **obj_names;
> bool replace;
>
> /* internal */
> @@ -194,9 +196,9 @@ struct klp_patch {
> list_for_each_entry(func, &obj->func_list, node)
>
> int klp_enable_patch(struct klp_patch *);
> +int klp_add_object(struct klp_object *);
>
> -void arch_klp_init_object_loaded(struct klp_patch *patch,
> - struct klp_object *obj);
> +void arch_klp_init_object_loaded(struct klp_object *obj);
>
> /* Called from the module loader during module coming/going states */
> int klp_module_coming(struct module *mod);
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index c3512e7e0801..bb62c5407b75 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -49,34 +49,6 @@ static bool klp_is_module(struct klp_object *obj)
> return obj->name;
> }
>
> -/* sets obj->mod if object is not vmlinux and module is found */
> -static void klp_find_object_module(struct klp_object *obj)
> -{
> - struct module *mod;
> -
> - if (!klp_is_module(obj))
> - return;
> -
> - mutex_lock(&module_mutex);
> - /*
> - * We do not want to block removal of patched modules and therefore
> - * we do not take a reference here. The patches are removed by
> - * klp_module_going() instead.
> - */
> - mod = find_module(obj->name);
> - /*
> - * Do not mess work of klp_module_coming() and klp_module_going().
> - * Note that the patch might still be needed before klp_module_going()
> - * is called. Module functions can be called even in the GOING state
> - * until mod->exit() finishes. This is especially important for
> - * patches that modify semantic of the functions.
> - */
> - if (mod && mod->klp_alive)
> - obj->mod = mod;
> -
> - mutex_unlock(&module_mutex);
> -}
> -
> static bool klp_initialized(void)
> {
> return !!klp_root_kobj;
> @@ -246,18 +218,16 @@ static int klp_resolve_symbols(Elf_Shdr *relasec, struct module *pmod)
> return 0;
> }
>
> -static int klp_write_object_relocations(struct module *pmod,
> - struct klp_object *obj)
> +static int klp_write_object_relocations(struct klp_object *obj)
> {
> int i, cnt, ret = 0;
> const char *objname, *secname;
> char sec_objname[MODULE_NAME_LEN];
> + struct module *pmod;
> Elf_Shdr *sec;
>
> - if (WARN_ON(!klp_is_object_loaded(obj)))
> - return -EINVAL;
> -
> objname = klp_is_module(obj) ? obj->name : "vmlinux";
> + pmod = obj->mod;
>
> /* For each klp relocation section */
> for (i = 1; i < pmod->klp_info->hdr.e_shnum; i++) {
> @@ -419,8 +389,8 @@ static void klp_free_object_dynamic(struct klp_object *obj)
>
> static void klp_init_func_early(struct klp_object *obj,
> struct klp_func *func);
> -static void klp_init_object_early(struct klp_patch *patch,
> - struct klp_object *obj);
> +static int klp_init_object_early(struct klp_patch *patch,
> + struct klp_object *obj);
>
> static struct klp_object *klp_alloc_object_dynamic(const char *name,
> struct klp_patch *patch)
> @@ -662,7 +632,7 @@ static void klp_free_patch_finish(struct klp_patch *patch)
>
> /* Put the module after the last access to struct klp_patch. */
> if (!patch->forced)
> - module_put(patch->mod);
> + module_put(patch->obj->mod);
> }
>
> /*
> @@ -725,30 +695,28 @@ static int klp_init_func(struct klp_object *obj, struct klp_func *func)
> }
>
> /* Arches may override this to finish any remaining arch-specific tasks */
> -void __weak arch_klp_init_object_loaded(struct klp_patch *patch,
> - struct klp_object *obj)
> +void __weak arch_klp_init_object_loaded(struct klp_object *obj)
> {
> }
>
> /* parts of the initialization that is done only when the object is loaded */
> -static int klp_init_object_loaded(struct klp_patch *patch,
> - struct klp_object *obj)
> +static int klp_init_object_loaded(struct klp_object *obj)
> {
> struct klp_func *func;
> int ret;
>
> mutex_lock(&text_mutex);
>
> - module_disable_ro(patch->mod);
> - ret = klp_write_object_relocations(patch->mod, obj);
> + module_disable_ro(obj->mod);
> + ret = klp_write_object_relocations(obj);
> if (ret) {
> - module_enable_ro(patch->mod, true);
> + module_enable_ro(obj->mod, true);
> mutex_unlock(&text_mutex);
> return ret;
> }
>
> - arch_klp_init_object_loaded(patch, obj);
> - module_enable_ro(patch->mod, true);
> + arch_klp_init_object_loaded(obj);
> + module_enable_ro(obj->mod, true);
>
> mutex_unlock(&text_mutex);
>
> @@ -792,11 +760,8 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
> return -EINVAL;
>
> obj->patched = false;
> - obj->mod = NULL;
>
> - klp_find_object_module(obj);
> -
> - name = klp_is_module(obj) ? obj->name : "vmlinux";
> + name = obj->name ? obj->name : "vmlinux";
> ret = kobject_add(&obj->kobj, &patch->kobj, "%s", name);
> if (ret)
> return ret;
> @@ -807,8 +772,7 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
> return ret;
> }
>
> - if (klp_is_object_loaded(obj))
> - ret = klp_init_object_loaded(patch, obj);
> + ret = klp_init_object_loaded(obj);
>
> return ret;
> }
> @@ -820,20 +784,34 @@ static void klp_init_func_early(struct klp_object *obj,
> list_add_tail(&func->node, &obj->func_list);
> }
>
> -static void klp_init_object_early(struct klp_patch *patch,
> +static int klp_init_object_early(struct klp_patch *patch,
> struct klp_object *obj)
> {
> + struct klp_func *func;
> +
> + if (!obj->funcs)
> + return -EINVAL;
> +
> INIT_LIST_HEAD(&obj->func_list);
> kobject_init(&obj->kobj, &klp_ktype_object);
> list_add_tail(&obj->node, &patch->obj_list);
> +
> + klp_for_each_func_static(obj, func) {
> + klp_init_func_early(obj, func);
> + }
> +
> + if (obj->dynamic || try_module_get(obj->mod))
> + return 0;
> +
> + return -ENODEV;
> }
>
> static int klp_init_patch_early(struct klp_patch *patch)
> {
> - struct klp_object *obj;
> - struct klp_func *func;
> + struct klp_object *obj = patch->obj;
>
> - if (!patch->objs)
> + /* Main patch module is always for vmlinux */
> + if (obj->name)
> return -EINVAL;
>
> INIT_LIST_HEAD(&patch->list);
> @@ -844,21 +822,7 @@ static int klp_init_patch_early(struct klp_patch *patch)
> INIT_WORK(&patch->free_work, klp_free_patch_work_fn);
> init_completion(&patch->finish);
>
> - klp_for_each_object_static(patch, obj) {

I think we can get rid of klp_for_each_object_static(), no? Now the
klp_patch is only associated to a single klp_object, so everything will
be dynamic. Is this correct?

> - if (!obj->funcs)
> - return -EINVAL;
> -
> - klp_init_object_early(patch, obj);
> -
> - klp_for_each_func_static(obj, func) {
> - klp_init_func_early(obj, func);
> - }
> - }
> -
> - if (!try_module_get(patch->mod))
> - return -ENODEV;
> -
> - return 0;
> + return klp_init_object_early(patch, obj);
> }
>
> static int klp_init_patch(struct klp_patch *patch)
> @@ -866,7 +830,7 @@ static int klp_init_patch(struct klp_patch *patch)
> struct klp_object *obj;
> int ret;
>
> - ret = kobject_add(&patch->kobj, klp_root_kobj, "%s", patch->mod->name);
> + ret = kobject_add(&patch->kobj, klp_root_kobj, "%s", patch->obj->mod->name);
> if (ret)
> return ret;
>
> @@ -887,6 +851,12 @@ static int klp_init_patch(struct klp_patch *patch)
> return 0;
> }
>
> +int klp_add_object(struct klp_object *obj)
> +{
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(klp_add_object);
> +
> static int __klp_disable_patch(struct klp_patch *patch)
> {
> struct klp_object *obj;
> @@ -930,7 +900,7 @@ static int __klp_enable_patch(struct klp_patch *patch)
> if (WARN_ON(patch->enabled))
> return -EINVAL;
>
> - pr_notice("enabling patch '%s'\n", patch->mod->name);
> + pr_notice("enabling patch '%s'\n", patch->obj->patch_name);
>
> klp_init_transition(patch, KLP_PATCHED);
>
> @@ -944,9 +914,6 @@ static int __klp_enable_patch(struct klp_patch *patch)
> smp_wmb();
>
> klp_for_each_object(patch, obj) {
> - if (!klp_is_object_loaded(obj))
> - continue;
> -
> ret = klp_pre_patch_callback(obj);
> if (ret) {
> pr_warn("pre-patch callback failed for object '%s'\n",
> @@ -968,7 +935,7 @@ static int __klp_enable_patch(struct klp_patch *patch)
>
> return 0;
> err:
> - pr_warn("failed to enable patch '%s'\n", patch->mod->name);
> + pr_warn("failed to enable patch '%s'\n", patch->obj->patch_name);
>
> klp_cancel_transition();
> return ret;
> @@ -991,12 +958,12 @@ int klp_enable_patch(struct klp_patch *patch)
> {
> int ret;
>
> - if (!patch || !patch->mod)
> + if (!patch || !patch->obj || !patch->obj->mod)
> return -EINVAL;
>
> - if (!is_livepatch_module(patch->mod)) {
> + if (!is_livepatch_module(patch->obj->mod)) {
> pr_err("module %s is not marked as a livepatch module\n",
> - patch->mod->name);
> + patch->obj->patch_name);

Shouldn't that be "patch->obj->mod->name" ?

> return -EINVAL;
> }
>
> @@ -1012,7 +979,7 @@ int klp_enable_patch(struct klp_patch *patch)
>
> if (!klp_is_patch_compatible(patch)) {
> pr_err("Livepatch patch (%s) is not compatible with the already installed livepatches.\n",
> - patch->mod->name);
> + patch->obj->mod->name);
> mutex_unlock(&klp_mutex);
> return -EINVAL;
> }
> @@ -1119,7 +1086,7 @@ static void klp_cleanup_module_patches_limited(struct module *mod,
> klp_pre_unpatch_callback(obj);
>
> pr_notice("reverting patch '%s' on unloading module '%s'\n",
> - patch->mod->name, obj->mod->name);
> + patch->obj->patch_name, obj->name);
> klp_unpatch_object(obj);
>
> klp_post_unpatch_callback(obj);
> @@ -1154,15 +1121,15 @@ int klp_module_coming(struct module *mod)
>
> obj->mod = mod;
>
> - ret = klp_init_object_loaded(patch, obj);
> + ret = klp_init_object_loaded(obj);
> if (ret) {
> pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
> - patch->mod->name, obj->mod->name, ret);
> + patch->obj->patch_name, obj->name, ret);
> goto err;
> }
>
> pr_notice("applying patch '%s' to loading module '%s'\n",
> - patch->mod->name, obj->mod->name);
> + patch->obj->patch_name, obj->name);
>
> ret = klp_pre_patch_callback(obj);
> if (ret) {
> @@ -1174,7 +1141,7 @@ int klp_module_coming(struct module *mod)
> 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);
> + patch->obj->patch_name, obj->name, ret);
>
> klp_post_unpatch_callback(obj);
> goto err;
> @@ -1197,7 +1164,7 @@ int klp_module_coming(struct module *mod)
> * error to the module loader.
> */
> pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n",
> - patch->mod->name, obj->mod->name, obj->mod->name);
> + patch->obj->patch_name, obj->name, obj->name);
> mod->klp_alive = false;
> obj->mod = NULL;
> klp_cleanup_module_patches_limited(mod, patch);
> diff --git a/kernel/livepatch/core.h b/kernel/livepatch/core.h
> index 38209c7361b6..01980cc0509b 100644
> --- a/kernel/livepatch/core.h
> +++ b/kernel/livepatch/core.h
> @@ -18,11 +18,6 @@ void klp_free_replaced_patches_async(struct klp_patch *new_patch);
> void klp_unpatch_replaced_patches(struct klp_patch *new_patch);
> void klp_discard_nops(struct klp_patch *new_patch);
>
> -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 = 0;
> diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
> index f6310f848f34..78e3280560cd 100644
> --- a/kernel/livepatch/transition.c
> +++ b/kernel/livepatch/transition.c
> @@ -74,7 +74,7 @@ static void klp_complete_transition(void)
> unsigned int cpu;
>
> pr_debug("'%s': completing %s transition\n",
> - klp_transition_patch->mod->name,
> + klp_transition_patch->obj->patch_name,
> klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
>
> if (klp_transition_patch->replace && klp_target_state == KLP_PATCHED) {
> @@ -120,15 +120,13 @@ static void klp_complete_transition(void)
> }
>
> klp_for_each_object(klp_transition_patch, obj) {
> - if (!klp_is_object_loaded(obj))
> - continue;
> if (klp_target_state == KLP_PATCHED)
> klp_post_patch_callback(obj);
> else if (klp_target_state == KLP_UNPATCHED)
> klp_post_unpatch_callback(obj);
> }
>
> - pr_notice("'%s': %s complete\n", klp_transition_patch->mod->name,
> + pr_notice("'%s': %s complete\n", klp_transition_patch->obj->patch_name,
> klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
>
> klp_target_state = KLP_UNDEFINED;
> @@ -147,7 +145,7 @@ void klp_cancel_transition(void)
> return;
>
> pr_debug("'%s': canceling patching transition, going to unpatch\n",
> - klp_transition_patch->mod->name);
> + klp_transition_patch->obj->patch_name);
>
> klp_target_state = KLP_UNPATCHED;
> klp_complete_transition();
> @@ -468,7 +466,7 @@ void klp_start_transition(void)
> WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED);
>
> pr_notice("'%s': starting %s transition\n",
> - klp_transition_patch->mod->name,
> + klp_transition_patch->obj->patch_name,

Isn't the transition per livepatched module rather than per-patch now?
If so, would it make more sense to display also the name of the module
being patched/unpatched?

> klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
>
> /*
> @@ -519,7 +517,7 @@ void klp_init_transition(struct klp_patch *patch, int state)
> */
> klp_target_state = state;
>
> - pr_debug("'%s': initializing %s transition\n", patch->mod->name,
> + pr_debug("'%s': initializing %s transition\n", patch->obj->patch_name,

Ditto.

> klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
>
> /*
> @@ -581,7 +579,7 @@ void klp_reverse_transition(void)
> struct task_struct *g, *task;
>
> pr_debug("'%s': reversing transition from %s\n",
> - klp_transition_patch->mod->name,
> + klp_transition_patch->obj->patch_name,

Ditto.

> klp_target_state == KLP_PATCHED ? "patching to unpatching" :
> "unpatching to patching");
>

[...]

Cheers,

--
Julien Thierry

2020-01-21 11:29:41

by Julien Thierry

[permalink] [raw]
Subject: Re: [POC 03/23] livepatch: Better checks of struct klp_object definition

Hi Petr,

On 1/17/20 3:03 PM, Petr Mladek wrote:
> The number of user defined fields increased in struct klp_object after
> spliting per-object livepatches. It was sometimes unclear why exactly
> the module could not get loded when returned -EINVAL.
>
> Add more checks for the split modules and write useful error
> messages on particular errors.
>
> Signed-off-by: Petr Mladek <[email protected]>
> ---
> kernel/livepatch/core.c | 91 ++++++++++++++++++++++++++++++++++++++++++++-----
> 1 file changed, 82 insertions(+), 9 deletions(-)
>
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index bb62c5407b75..ec7ffc7db3a7 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -756,9 +756,6 @@ static int klp_init_object(struct klp_patch *patch, struct klp_object *obj)
> int ret;
> const char *name;
>
> - if (klp_is_module(obj) && strlen(obj->name) >= MODULE_NAME_LEN)
> - return -EINVAL;
> -
> obj->patched = false;
>
> name = obj->name ? obj->name : "vmlinux";
> @@ -851,8 +848,86 @@ static int klp_init_patch(struct klp_patch *patch)
> return 0;
> }
>
> +static int klp_check_module_name(struct klp_object *obj, bool is_module)
> +{
> + char mod_name[MODULE_NAME_LEN];
> + const char *expected_name;
> +
> + if (is_module) {
> + snprintf(mod_name, sizeof(mod_name), "%s__%s",
> + obj->patch_name, obj->name);
> + expected_name = mod_name;
> + } else {
> + expected_name = obj->patch_name;
> + }
> +
> + if (strcmp(expected_name, obj->mod->name)) {

I'm not sure I understand the point of enforcing this.

> + pr_err("The module name %s does not match with obj->patch_name and obj->name. The expected name is: %s\n",
> + obj->mod->name, expected_name);
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int klp_check_object(struct klp_object *obj, bool is_module)
> +{
> +
> + if (!obj->mod)
> + return -EINVAL;
> +
> + if (!is_livepatch_module(obj->mod)) {
> + pr_err("module %s is not marked as a livepatch module\n",
> + obj->mod->name);
> + return -EINVAL;
> + }
> +
> + if (!obj->patch_name) {
> + pr_err("module %s does not have set obj->patch_name\n",
> + obj->mod->name);
> + return -EINVAL;
> + }
> +
> + if (strlen(obj->patch_name) >= MODULE_NAME_LEN) {
> + pr_err("module %s has too long obj->patch_name\n",
> + obj->mod->name);
> + return -EINVAL;
> + }
> +
> + if (is_module) {
> + if (!obj->name) {
> + pr_err("module %s does not have set obj->name\n",
> + obj->mod->name);
> + return -EINVAL;
> + }
> + if (strlen(obj->name) >= MODULE_NAME_LEN) {
> + pr_err("module %s has too long obj->name\n",
> + obj->mod->name);
> + return -EINVAL;
> + }
> + } else if (obj->name) {
> + pr_err("module %s for vmlinux must not have set obj->name\n",
> + obj->mod->name);
> + return -EINVAL;
> + }
> +
> + if (!obj->funcs) {
> + pr_err("module %s does not have set obj->funcs\n",
> + obj->mod->name);
> + return -EINVAL;
> + }
> +
> + return klp_check_module_name(obj, is_module);
> +}
> +
> int klp_add_object(struct klp_object *obj)
> {
> + int ret;
> +
> + ret = klp_check_object(obj, true);
> + if (ret)
> + return ret;
> +
> return 0;
> }
> EXPORT_SYMBOL_GPL(klp_add_object);
> @@ -958,14 +1033,12 @@ int klp_enable_patch(struct klp_patch *patch)
> {
> int ret;
>
> - if (!patch || !patch->obj || !patch->obj->mod)
> + if (!patch || !patch->obj)
> return -EINVAL;
>
> - if (!is_livepatch_module(patch->obj->mod)) {
> - pr_err("module %s is not marked as a livepatch module\n",
> - patch->obj->patch_name);
> - return -EINVAL;
> - }
> + ret = klp_check_object(patch->obj, false);
> + if (ret)
> + return ret;
>
> if (!klp_initialized())
> return -ENODEV;
>

Otherwise this looks good to me.

Cheers,

--
Julien Thierry

2020-01-21 12:01:15

by Julien Thierry

[permalink] [raw]
Subject: Re: [POC 05/23] livepatch: Initialize and free livepatch submodule

Hi Petr,

On 1/17/20 3:03 PM, Petr Mladek wrote:
> Another step when loading livepatches to livepatch modules is
> to initialize the structure, create sysfs entries, do livepatch
> specific relocations.
>
> These operation can fail and the objects must be freed that case.
> The error message is taken from klp_module_coming() to match
> selftests.
>
> Signed-off-by: Petr Mladek <[email protected]>
> ---
> kernel/livepatch/core.c | 34 +++++++++++++++++++++++++++-------
> 1 file changed, 27 insertions(+), 7 deletions(-)
>
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index e2c7dc6c2d5f..6c27b635e5a7 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -583,18 +583,23 @@ static void klp_free_object_loaded(struct klp_object *obj)
> }
> }
>
> +static void klp_free_object(struct klp_object *obj, bool nops_only)
> +{
> + __klp_free_funcs(obj, nops_only);
> +
> + if (nops_only && !obj->dynamic)
> + return;
> +
> + list_del(&obj->node);
> + kobject_put(&obj->kobj);
> +}
> +
> static void __klp_free_objects(struct klp_patch *patch, bool nops_only)
> {
> struct klp_object *obj, *tmp_obj;
>
> klp_for_each_object_safe(patch, obj, tmp_obj) {
> - __klp_free_funcs(obj, nops_only);
> -
> - if (nops_only && !obj->dynamic)
> - continue;
> -
> - list_del(&obj->node);
> - kobject_put(&obj->kobj);
> + klp_free_object(obj, nops_only);
> }
> }
>
> @@ -812,6 +817,8 @@ static int klp_init_object_early(struct klp_patch *patch,
> if (obj->dynamic || try_module_get(obj->mod))
> return 0;
>
> + /* patch stays when this function fails in klp_add_object() */
> + list_del(&obj->node);
> return -ENODEV;
> }
>
> @@ -993,9 +1000,22 @@ int klp_add_object(struct klp_object *obj)
> goto err;
> }
>
> + ret = klp_init_object_early(patch, obj);

klp_init_object_early() can fail after adding obj to patch->obj_list.
This wouldn't get cleaned up by the "err" path.

It probably would keep things simple to only add obj to patch->obj_list
if early initialization is successful in patch 2 (ofc I'm talking about
the actual patch of this patch series ;) ).

> + if (ret)
> + goto err;
> +
> + ret = klp_init_object(patch, obj);
> + if (ret) {
> + pr_warn("failed to initialize patch '%s' for module '%s' (%d)\n",
> + patch->obj->patch_name, obj->name, ret);
> + goto err_free;
> + }
> +
> mutex_unlock(&klp_mutex);
> return 0;
>
> +err_free:
> + klp_free_object(obj, false);
> err:
> /*
> * If a patch is unsuccessfully applied, return
>

Cheers,

--
Julien Thierry

2020-01-22 10:16:25

by Julien Thierry

[permalink] [raw]
Subject: Re: [POC 11/23] livepatch: Safely detect forced transition when removing split livepatch modules

Hi Petr,

On 1/17/20 3:03 PM, Petr Mladek wrote:
> The information about forced livepatch transition is currently stored
> in struct klp_patch. But there is not any obvious safe way how to
> access it when the split livepatch modules are removed.
>

If that's the only motivation to do this, klp_objects could have a
reference to the klp_patch they are part of. This could easily be set
when adding the klp_object to the klp_patch->obj_list in
klp_init_object_early() .

Having this reference could also prove useful in future scenarios.

Cheers,

--
Julien Thierry

2020-01-22 18:54:41

by Julien Thierry

[permalink] [raw]
Subject: Re: [POC 09/23] livepatch: Handle race when livepatches are reloaded during a module load

Hi Petr,

On 1/17/20 3:03 PM, Petr Mladek wrote:
> klp_module_coming() might fail to load a livepatch module when
> the related livepatch gets reloaded in the meantime.
>
> Detect this situation by adding a timestamp into struct klp_patch.
> local_clock is enough because klp_mutex must be released and taken
> several times during this scenario.
>
> Signed-off-by: Petr Mladek <[email protected]>
> ---
> include/linux/livepatch.h | 2 ++
> kernel/livepatch/core.c | 9 +++++----
> 2 files changed, 7 insertions(+), 4 deletions(-)
>
> diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
> index a4567c17a9f2..feb33f023f9f 100644
> --- a/include/linux/livepatch.h
> +++ b/include/linux/livepatch.h
> @@ -155,6 +155,7 @@ struct klp_state {
> * @obj_list: dynamic list of the object entries
> * @enabled: the patch is enabled (but operation may be incomplete)
> * @forced: was involved in a forced transition
> + * ts: timestamp when the livepatch has been loaded

Nit: Missing '@'.

> * @free_work: patch cleanup from workqueue-context
> * @finish: for waiting till it is safe to remove the patch module
> */
> @@ -171,6 +172,7 @@ struct klp_patch {
> struct list_head obj_list;
> bool enabled;
> bool forced;
> + u64 ts;
> struct work_struct free_work;
> struct completion finish;
> };
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index 34e3ee2be7ef..8e693c58b736 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
> @@ -20,6 +20,7 @@
> #include <linux/moduleloader.h>
> #include <linux/completion.h>
> #include <linux/memory.h>
> +#include <linux/sched/clock.h>
> #include <asm/cacheflush.h>
> #include "core.h"
> #include "patch.h"
> @@ -854,6 +855,7 @@ static int klp_init_patch_early(struct klp_patch *patch)
> kobject_init(&patch->kobj, &klp_ktype_patch);
> patch->enabled = false;
> patch->forced = false;
> + patch->ts = local_clock();
> INIT_WORK(&patch->free_work, klp_free_patch_work_fn);
> init_completion(&patch->finish);
>
> @@ -1324,6 +1326,7 @@ int klp_module_coming(struct module *mod)
> {
> char patch_name[MODULE_NAME_LEN];
> struct klp_patch *patch;
> + u64 patch_ts;
> int ret = 0;
>
> if (WARN_ON(mod->state != MODULE_STATE_COMING))
> @@ -1339,6 +1342,7 @@ int klp_module_coming(struct module *mod)
> continue;
>
> strncpy(patch_name, patch->obj->patch_name, sizeof(patch_name));
> + patch_ts = patch->ts;
> mutex_unlock(&klp_mutex);
>
> ret = klp_try_load_object(patch_name, mod->name);
> @@ -1346,14 +1350,11 @@ int klp_module_coming(struct module *mod)
> * The load might have failed because the patch has
> * been removed in the meantime. In this case, the
> * error might be ignored.
> - *
> - * FIXME: It is not fully proof. The patch might have be
> - * unloaded and loaded again in the mean time.
> */
> mutex_lock(&klp_mutex);
> if (ret) {
> patch = klp_find_patch(patch_name);
> - if (patch)
> + if (patch && patch->ts == patch_ts)
> goto err;

If the timestamps differ, we have found the klp_patch, feels a bit of a
waste to go through the list again without trying to load it right away.

Admittedly this is to solve a race condition which should not even
happen very often...

> ret = 0;
> }
>

Cheers,

--
Julien Thierry

2020-01-28 12:18:07

by Petr Mladek

[permalink] [raw]
Subject: Re: [POC 02/23] livepatch: Split livepatch modules per livepatched object

On Tue 2020-01-21 11:11:45, Julien Thierry wrote:
> Hi Petr,
>
> On 1/17/20 3:03 PM, Petr Mladek wrote:
> > One livepatch module allows to fix vmlinux and any number of modules
> > while providing some guarantees defined by the consistency model.
> >
> > The solution is to split the livepatch module per livepatched
> > object (vmlinux or module). Then both livepatch module and
> > the livepatched modules could get loaded and removed at the
> > same time.
> >
> > The livepatches for modules are put into separate source files
> > that define only struct klp_object() and call the new klp_add_object()
> > in the init() callback. The name of the module follows the pattern:
> >
> > <patch_name>__<object_name>
> >
>
> Is that a requirement? Or is it just the convention followed for the current
> tests?

This naming pattern is enforced by the code. The reason is to
distinguish the purpose of each livepatch module.

+ Livepatch module for "vmlinux" and the related livepatch modules
for other objects.

+ Different livepatches (versions) that might be installed at the
same time. This happens even with cumulative livepatches.


It is important for the functionality:

+ Consistency checks that all and right livepatch modules are
loaded.

+ Automatic loading of livepatch modules for modules when the patched
module is being loaded.

But it should be "clear" even for humans because the livepatch modules are
listed by lsmod, ...

Of course, we could talk about other naming scheme, another approach.


> > @@ -844,21 +822,7 @@ static int klp_init_patch_early(struct klp_patch *patch)
> > INIT_WORK(&patch->free_work, klp_free_patch_work_fn);
> > init_completion(&patch->finish);
> > - klp_for_each_object_static(patch, obj) {
>
> I think we can get rid of klp_for_each_object_static(), no? Now the
> klp_patch is only associated to a single klp_object, so everything will be
> dynamic. Is this correct?

Yes, the macro klp_for_each_object_static() is not longer needed.

Just to be sure. It would be better to say that all klp_object
structures will be in the linked lists only.

Most structures are still defined statically. The name "dynamic" is
used for the dynamically allocated structures. They are used for
"nop" functions that might be needed when doing atomic replace
of cumulative patches and functions that are not longer patched.
See obj->dynamic and func->nop.


> > @@ -991,12 +958,12 @@ int klp_enable_patch(struct klp_patch *patch)
> > {
> > int ret;
> > - if (!patch || !patch->mod)
> > + if (!patch || !patch->obj || !patch->obj->mod)
> > return -EINVAL;
> > - if (!is_livepatch_module(patch->mod)) {
> > + if (!is_livepatch_module(patch->obj->mod)) {
> > pr_err("module %s is not marked as a livepatch module\n",
> > - patch->mod->name);
> > + patch->obj->patch_name);
>
> Shouldn't that be "patch->obj->mod->name" ?

They are actually the same. Note that it is redundant only in
struct klp_object that is in the livepatch module for vmlinux.

Hmm, it might be possible to get rid of it after I added the array
patch->obj_names. But I would prefer to keep it as a consistency
check.

One big drawback of the split modules approach is that there are
suddenly many more livepatch modules. The kernel code has to make
sure always the right ones are loaded. It is great to have some
cross-checks.


> > return -EINVAL;
> > }

> > diff --git a/kernel/livepatch/transition.c b/kernel/livepatch/transition.c
> > index f6310f848f34..78e3280560cd 100644
> > --- a/kernel/livepatch/transition.c
> > +++ b/kernel/livepatch/transition.c
> > @@ -147,7 +145,7 @@ void klp_cancel_transition(void)
> > return;
> > pr_debug("'%s': canceling patching transition, going to unpatch\n",
> > - klp_transition_patch->mod->name);
> > + klp_transition_patch->obj->patch_name);
> > klp_target_state = KLP_UNPATCHED;
> > klp_complete_transition();
> > @@ -468,7 +466,7 @@ void klp_start_transition(void)
> > WARN_ON_ONCE(klp_target_state == KLP_UNDEFINED);
> > pr_notice("'%s': starting %s transition\n",
> > - klp_transition_patch->mod->name,
> > + klp_transition_patch->obj->patch_name,
>
> Isn't the transition per livepatched module rather than per-patch now?
> If so, would it make more sense to display also the name of the module being
> patched/unpatched?

The transition still happens for the entire livepatch defined by
struct klp_patch. All needed livepatch modules for the other objects
are loaded before the transition starts, see the patch 17/24
("livepatch: Load livepatches for modules when loading the main
livepatch").

> > klp_target_state == KLP_PATCHED ? "patching" : "unpatching");
> > /*

Best Regards,
Petr

PS: Julien,

first, thanks a lot for looking at the patchset.

I am going to answer questions and comments that are related to
the overall design. The most important question is if the split
livepatch modules are the way to go. I hope that this patchset
shows possible wins and catches so that we could decide if it
is worth the effort.

Anyway, feel free to comment even details when you notice
a mistake. There might be some catches that I missed, ...

Best Regards,
Petr

2020-04-03 17:56:55

by Joe Lawrence

[permalink] [raw]
Subject: Re: [POC 04/23] livepatch: Prevent loading livepatch sub-module unintentionally.

On Fri, Jan 17, 2020 at 04:03:04PM +0100, Petr Mladek wrote:
> Livepatch is split into several modules. The main module is for livepatching
> vmlinux. The rest is for livepatching other modules.
>
> Only the livepatch module for vmlinux can be loaded by users. Others are
> loaded automatically when the related module is or gets loaded.
>
> Users might try to load any livepatch module. It must be allowed
> only when the related livepatch module for vmlinux and the livepatched
> module are loaded.
>
> Also it is important to check that obj->name is listed in patch->obj_names.
> Otherwise this module would not be loaded automatically. And it would
> lead into inconsistent behavier. Anyway, the missing name means a mistake
> somewhere and must be reported.
>
> klp_add_object() is taking over the job done by klp_module_coming().
> The error message is taken from there so that selftests do not need
> to get updated.
>
> Signed-off-by: Petr Mladek <[email protected]>
> ---
> kernel/livepatch/core.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 77 insertions(+), 1 deletion(-)
>
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> index ec7ffc7db3a7..e2c7dc6c2d5f 100644
> --- a/kernel/livepatch/core.c
> +++ b/kernel/livepatch/core.c
>
> [ ... snip ... ]
>
> int klp_add_object(struct klp_object *obj)
> {
> + struct klp_patch *patch;
> int ret;
>
> ret = klp_check_object(obj, true);
> if (ret)
> return ret;
>
> + mutex_lock(&klp_mutex);
> +
> + patch = klp_find_patch(obj->patch_name);
> + if (!patch) {
> + pr_err("Can't load livepatch (%s) for module when the livepatch (%s) for vmcore is not loaded\n",
> + obj->mod->name, obj->patch_name);

nit: s/vmcore/vmlinux in the error message?

> + ret = -EINVAL;
> + goto err;

Minor code snafu: !patch for this exit path means ...

> + }
> +
> + if (!klp_is_object_compatible(patch, obj)) {
> + ret = -EINVAL;
> + goto err;
> + }
> +
> + mutex_unlock(&klp_mutex);
> return 0;
> +
> +err:
> + /*
> + * If a patch is unsuccessfully applied, return
> + * error to the module loader.
> + */
> + pr_warn("patch '%s' failed for module '%s', refusing to load module '%s'\n",
> + patch->obj->patch_name, obj->name, obj->name);

... we can't access patch->obj->patch_name here.

-- Joe

2020-04-03 18:03:27

by Joe Lawrence

[permalink] [raw]
Subject: Re: [POC 20/23] module/livepatch: Relocate local variables in the module loaded when the livepatch is being loaded

On Fri, Jan 17, 2020 at 04:03:20PM +0100, Petr Mladek wrote:
> The special SHF_RELA_LIVEPATCH section is still needed to find static
> (non-exported) symbols. But it can be done together with the other
> relocations when the livepatch module is being loaded.
>
> There is no longer needed to copy the info section. The related
> code in the module loaded will get removed in separate patch.
>
> Signed-off-by: Petr Mladek <[email protected]>
> ---
> include/linux/livepatch.h | 4 +++
> kernel/livepatch/core.c | 62 +++--------------------------------------------
> kernel/module.c | 16 +++++++-----
> 3 files changed, 18 insertions(+), 64 deletions(-)
>
>
> [ ... snip ... ]
>
> diff --git a/kernel/module.c b/kernel/module.c
> index bd92854b42c2..c14b5135db27 100644
> --- a/kernel/module.c
> +++ b/kernel/module.c
> @@ -2410,16 +2410,20 @@ static int apply_relocations(struct module *mod, const struct load_info *info)
> if (!(info->sechdrs[infosec].sh_flags & SHF_ALLOC))
> continue;
>
> - /* Livepatch relocation sections are applied by livepatch */
> - if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH)
> - continue;
> -
> - if (info->sechdrs[i].sh_type == SHT_REL)
> + /* Livepatch need to resolve static symbols. */
> + if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) {
> + err = klp_resolve_symbols(info->sechdrs, i, mod);
> + if (err < 0)
> + break;
> + err = apply_relocate_add(info->sechdrs, info->strtab,
> + info->index.sym, i, mod);
> + } else if (info->sechdrs[i].sh_type == SHT_REL) {
> err = apply_relocate(info->sechdrs, info->strtab,
> info->index.sym, i, mod);
> - else if (info->sechdrs[i].sh_type == SHT_RELA)
> + } else if (info->sechdrs[i].sh_type == SHT_RELA) {
> err = apply_relocate_add(info->sechdrs, info->strtab,
> info->index.sym, i, mod);
> + }
> if (err < 0)
> break;
> }


Hi Petr,

At first I thought there was a simple order of operations problem here
with respect to klp_resolve_symbols() accessing core_kallsyms before
they were setup by add_kallsyms():

load_module
apply_relocations

/* Livepatch need to resolve static symbols. */
if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) {
err = klp_resolve_symbols(info->sechdrs, i, mod);

klp_resolve_symbols

sym = pmod->core_kallsyms.symtab + ELF_R_SYM(relas[i].r_info);
^^^^^^^^^^^^^^^^^^^^
used before init (below)
...
post_relocation
add_kallsyms

/*
* Now populate the cut down core kallsyms for after init
* and set types up while we still have access to sections.
*/
mod->core_kallsyms.symtab = dst = mod->core_layout.base + info->symoffs;
mod->core_kallsyms.strtab = s = mod->core_layout.base + info->stroffs;
mod->core_kallsyms.typetab = mod->core_layout.base + info->core_typeoffs;
^^^^^^^^^^^^^^^^^^^^^
core_kallsyms initialized here

But after tinkering with the patchset, a larger problem is that
klp_resolve_symbols() writes st_values to the core_kallsyms copies, but
then apply_relocate_add() references the originals in the load_info
structure.

I assume that klp_resolve_symbols() originally looked at the
core_kallsyms copies for handling the late module patching case. If we
no longer need to support that, then how about this slight modification
to klp_resolve_symbols() to make it look more the like
apply_relocate{_add,} calls?

-->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8--

diff --git a/include/linux/livepatch.h b/include/linux/livepatch.h
index 3b27ef1a7291..54d5a4045e5a 100644
--- a/include/linux/livepatch.h
+++ b/include/linux/livepatch.h
@@ -210,6 +210,8 @@ int klp_module_coming(struct module *mod);
void klp_module_going(struct module *mod);

int klp_resolve_symbols(Elf_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
unsigned int relsec,
struct module *pmod);

diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
index cc0ac93fe8cd..02638e3b09b0 100644
--- a/kernel/livepatch/core.c
+++ b/kernel/livepatch/core.c
@@ -197,13 +197,14 @@ static int klp_find_object_symbol(const char *objname, const char *name,
}

int klp_resolve_symbols(Elf_Shdr *sechdrs,
+ const char *strtab,
+ unsigned int symindex,
unsigned int relsec,
struct module *pmod)
{
int i, cnt, vmlinux, ret;
char objname[MODULE_NAME_LEN];
char symname[KSYM_NAME_LEN];
- char *strtab = pmod->core_kallsyms.strtab;
Elf_Shdr *relasec = sechdrs + relsec;
Elf_Rela *relas;
Elf_Sym *sym;
@@ -224,7 +225,8 @@ int klp_resolve_symbols(Elf_Shdr *sechdrs,
relas = (Elf_Rela *) relasec->sh_addr;
/* For each rela in this klp relocation section */
for (i = 0; i < relasec->sh_size / sizeof(Elf_Rela); i++) {
- sym = pmod->core_kallsyms.symtab + ELF_R_SYM(relas[i].r_info);
+ sym = (Elf64_Sym *)sechdrs[symindex].sh_addr +
+ ELF_R_SYM(relas[i].r_info);
if (sym->st_shndx != SHN_LIVEPATCH) {
pr_err("symbol %s is not marked as a livepatch symbol\n",
strtab + sym->st_name);
diff --git a/kernel/module.c b/kernel/module.c
index d435bad80d7d..a65f089f19c9 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -2320,7 +2320,8 @@ static int apply_relocations(struct module *mod, const struct load_info *info)

/* Livepatch need to resolve static symbols. */
if (info->sechdrs[i].sh_flags & SHF_RELA_LIVEPATCH) {
- err = klp_resolve_symbols(info->sechdrs, i, mod);
+ err = klp_resolve_symbols(info->sechdrs, info->strtab,
+ info->index.sym, i, mod);
if (err < 0)
break;
err = apply_relocate_add(info->sechdrs, info->strtab,

-->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8--


-- Joe

2020-04-03 18:04:14

by Joe Lawrence

[permalink] [raw]
Subject: Re: [POC 22/23] livepatch/module: Remove obsolete copy_module_elf()

On Fri, Jan 17, 2020 at 04:03:22PM +0100, Petr Mladek wrote:
> The split livepatch modules can be relocated immedidately when they
> are loaded. There is no longer needed to preserve the elf sections.
>
> Signed-off-by: Petr Mladek <[email protected]>
> ---
> Documentation/livepatch/module-elf-format.rst | 18 ++++++
> include/linux/module.h | 3 -
> kernel/module.c | 87 ---------------------------
> 3 files changed, 18 insertions(+), 90 deletions(-)
>
> diff --git a/Documentation/livepatch/module-elf-format.rst b/Documentation/livepatch/module-elf-format.rst
> index 9f0c997d4940..8c6b894c4661 100644
> --- a/Documentation/livepatch/module-elf-format.rst
> +++ b/Documentation/livepatch/module-elf-format.rst
> @@ -14,6 +14,7 @@ This document outlines the Elf format requirements that livepatch modules must f
> 4. Livepatch symbols
> 4.1 A livepatch module's symbol table
> 4.2 Livepatch symbol format
> + 5. Symbol table and Elf section access
>
> 1. Background and motivation
> ============================
> @@ -295,3 +296,20 @@ See include/uapi/linux/elf.h for the actual definitions.
> [*]
> Note that the 'Ndx' (Section index) for these symbols is SHN_LIVEPATCH (0xff20).
> "OS" means OS-specific.
> +
> +5. Symbol table and Elf section access
> +======================================
> +A livepatch module's symbol table is accessible through module->symtab.
> +
> +Since apply_relocate_add() requires access to a module's section headers,
> +symbol table, and relocation section indices, Elf information is preserved for
> +livepatch modules and is made accessible by the module loader through
> +module->klp_info, which is a klp_modinfo struct. When a livepatch module loads,
> +this struct is filled in by the module loader. Its fields are documented below::
> +
> + struct klp_modinfo {
> + Elf_Ehdr hdr; /* Elf header */
> + Elf_Shdr *sechdrs; /* Section header table */
> + char *secstrings; /* String table for the section headers */
> + unsigned int symndx; /* The symbol table section index */
> + };

I think this file was inadvertently reverted, or at least the Symbol
table and Elf section access section was supposed to stay gone, right?

-- Joe

2020-04-06 18:50:01

by Joe Lawrence

[permalink] [raw]
Subject: Re: [POC 19/23] module/livepatch: Allow to use exported symbols from livepatch module for "vmlinux"

On Fri, Jan 17, 2020 at 04:03:19PM +0100, Petr Mladek wrote:
> HINT: Get some coffee before reading this commit message.
>
> Stop reading when it gets too complicated. It is possible that we
> will need to resolve symbols from livepatch modules another way.
> Livepatches need to access also non-exported symbols anyway.
>
> Or just ask me to explain the problem a better way. I have
> ended in many cycles when thinking about it. And it might
> be much easier from another point of view.
>
> The split per-object livepatches brings even more types of module
> dependencies. Let's split them into few categories:
>
> A. Livepatch module using an exported symbol from "vmlinux".
>
> It is quite common and works by definition. Livepatch module is just
> a module from this point of view.
>

Hi Petr,

If only all the cases were so easy :)

> B. Livepatch module using an exported symbol from the patched module.
>
> It should be avoided even with the non-split livepatch module. The module
> loader automatically takes reference to make sure the modules are
> unloaded in the right order. This would basically prevent the livepatched
> module from unloading.
>
> Note that it would be perfectly safe to remove this automatic
> dependency. The livepatch framework makes sure that the livepatch
> module is loaded only when the patched one is loaded. But it cannot
> be implemented easily, see below.

Do you envision klp-convert providing this functionality?

For reference, kpatch-build will take this example input patch:

diff -U 15 -Nupr linux-3.10.0-1133.el7.x86_64.old/drivers/cdrom/cdrom.c linux-3.10.0-1133.el7.x86_64/drivers/cdrom/cdrom.c
--- linux-3.10.0-1133.el7.x86_64.old/drivers/cdrom/cdrom.c 2020-04-06 11:58:34.470969120 -0400
+++ linux-3.10.0-1133.el7.x86_64/drivers/cdrom/cdrom.c 2020-04-06 12:01:07.882719611 -0400
@@ -1429,30 +1429,32 @@ unsigned int cdrom_check_events(struct c
EXPORT_SYMBOL(cdrom_check_events);

/* We want to make media_changed accessible to the user through an
* ioctl. The main problem now is that we must double-buffer the
* low-level implementation, to assure that the VFS and the user both
* see a medium change once.
*/

static
int media_changed(struct cdrom_device_info *cdi, int queue)
{
unsigned int mask = (1 << (queue & 1));
int ret = !!(cdi->mc_flags & mask);
bool changed;

+pr_info("%lx\n", (unsigned long) cdrom_check_events);
+
if (!CDROM_CAN(CDC_MEDIA_CHANGED))
return ret;

/* changed since last call? */
if (cdi->ops->check_events) {
BUG_ON(!queue); /* shouldn't be called from VFS path */
cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE);
changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE;
cdi->ioctl_events = 0;
} else
changed = cdi->ops->media_changed(cdi, CDSL_CURRENT);

if (changed) {
cdi->mc_flags = 0x3; /* set bit on both queues */
ret |= 1;

and you'll have the original cdrom.ko, owner of exported
cdrom_check_events, and a new livepatch-cdrom.ko that references it.
kpatch-build converts the symbol/rela combination like so:

% readelf --wide --symbols livepatch-test.ko | grep cdrom_check_events
79: 0000000000000000 0 FUNC GLOBAL DEFAULT OS [0xff20] .klp.sym.cdrom.cdrom_check_events,0

% readelf --wide --relocs livepatch-test.ko | awk '/cdrom_check_events/' RS="\n\n" ORS="\n\n"
Relocation section '.klp.rela.cdrom..text.media_changed' at offset 0x97fc0 contains 1 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
0000000000000016 0000004f0000000b R_X86_64_32S 0000000000000000 .klp.sym.cdrom.cdrom_check_events,0 + 0

That dodges the implicit module reference on cdrom.ko, but would it
still be safe in this klp-split POC?

FWIW, I have been working an updated klp-convert for v5.6 that includes
a bunch of fixes and such... modifying it to convert module-export
references like this was quite easy.

> C. Livepatch module using an exported symbol from the main livepatch module
> for "vmlinux".
>
> It is the 2nd most realistic variant. It even exists in current
> selftests. Namely, test_klp_callback_demo* modules share
> the implementation of callbacks. It avoids code duplication.
> And it is actually needed to make module parameters working.

I had to double check this and the exports were introduced by this POC
just to avoid code duplication, right? If so, would it be worth the
extra code to avoid providing bad example usage? Or at least do so for
sample/livepatch/ and not selftests.

> Note that the current implementation allows to pass module parameters
> only to the main livepatch module for "vmlinux". It should not be a real
> life problem. The parameters are used in selftests. But they are
> not used in practice.

Yeah, we don't use module parameters for kpatch either, AFAIK they are
only useful as a quick and easy method to poke those selftests.

On a related note, one possible work around for case C is to use shadow
variables created by the main livepatch module to store symbol
locations or the values themselves. This might get tedious for real
kernel API, but has been reasonable for livepatch bookkeeping states,
counts, etc.

> D. Livepatch modules might depend on each other. Note that dependency on
> the main livepatch module for "vmlinux" has got a separate category 'C'.
>
> The dependencies between modules are quite rare. But they exist.
> One can assume that this might be useful also on the livepatching
> level.
>
> To keep it sane, the livepatch modules should just follow
> the dependencies of the related patched modules. By other words,
> the livepatch modules might or should have the same dependencies
> as the patched counter parts but nothing more.
>

I agree, I think this is the only sane way to approach case D.

> Do these dependencies need some special handling?
>
> [ ... snip ... ]
>

This is the multiple-coffee cup section that I'm still trying to wrap my
brain around.

In the meantime, could we simplify or avoid any of these by enforcing
things on the build side? I don't know if these are even detectable,
but perhaps we could prevent them from even occuring. /shrugs

-- Joe

2020-04-07 07:35:00

by Miroslav Benes

[permalink] [raw]
Subject: Re: [POC 19/23] module/livepatch: Allow to use exported symbols from livepatch module for "vmlinux"

On Mon, 6 Apr 2020, Joe Lawrence wrote:

> On Fri, Jan 17, 2020 at 04:03:19PM +0100, Petr Mladek wrote:
> > HINT: Get some coffee before reading this commit message.
> >
> > Stop reading when it gets too complicated. It is possible that we
> > will need to resolve symbols from livepatch modules another way.
> > Livepatches need to access also non-exported symbols anyway.
> >
> > Or just ask me to explain the problem a better way. I have
> > ended in many cycles when thinking about it. And it might
> > be much easier from another point of view.
> >
> > The split per-object livepatches brings even more types of module
> > dependencies. Let's split them into few categories:
> >
> > A. Livepatch module using an exported symbol from "vmlinux".
> >
> > It is quite common and works by definition. Livepatch module is just
> > a module from this point of view.
> >
>
> Hi Petr,
>
> If only all the cases were so easy :)
>
> > B. Livepatch module using an exported symbol from the patched module.
> >
> > It should be avoided even with the non-split livepatch module. The module
> > loader automatically takes reference to make sure the modules are
> > unloaded in the right order. This would basically prevent the livepatched
> > module from unloading.
> >
> > Note that it would be perfectly safe to remove this automatic
> > dependency. The livepatch framework makes sure that the livepatch
> > module is loaded only when the patched one is loaded. But it cannot
> > be implemented easily, see below.
>
> Do you envision klp-convert providing this functionality?
>
> For reference, kpatch-build will take this example input patch:
>
> diff -U 15 -Nupr linux-3.10.0-1133.el7.x86_64.old/drivers/cdrom/cdrom.c linux-3.10.0-1133.el7.x86_64/drivers/cdrom/cdrom.c
> --- linux-3.10.0-1133.el7.x86_64.old/drivers/cdrom/cdrom.c 2020-04-06 11:58:34.470969120 -0400
> +++ linux-3.10.0-1133.el7.x86_64/drivers/cdrom/cdrom.c 2020-04-06 12:01:07.882719611 -0400
> @@ -1429,30 +1429,32 @@ unsigned int cdrom_check_events(struct c
> EXPORT_SYMBOL(cdrom_check_events);
>
> /* We want to make media_changed accessible to the user through an
> * ioctl. The main problem now is that we must double-buffer the
> * low-level implementation, to assure that the VFS and the user both
> * see a medium change once.
> */
>
> static
> int media_changed(struct cdrom_device_info *cdi, int queue)
> {
> unsigned int mask = (1 << (queue & 1));
> int ret = !!(cdi->mc_flags & mask);
> bool changed;
>
> +pr_info("%lx\n", (unsigned long) cdrom_check_events);
> +
> if (!CDROM_CAN(CDC_MEDIA_CHANGED))
> return ret;
>
> /* changed since last call? */
> if (cdi->ops->check_events) {
> BUG_ON(!queue); /* shouldn't be called from VFS path */
> cdrom_update_events(cdi, DISK_EVENT_MEDIA_CHANGE);
> changed = cdi->ioctl_events & DISK_EVENT_MEDIA_CHANGE;
> cdi->ioctl_events = 0;
> } else
> changed = cdi->ops->media_changed(cdi, CDSL_CURRENT);
>
> if (changed) {
> cdi->mc_flags = 0x3; /* set bit on both queues */
> ret |= 1;
>
> and you'll have the original cdrom.ko, owner of exported
> cdrom_check_events, and a new livepatch-cdrom.ko that references it.
> kpatch-build converts the symbol/rela combination like so:
>
> % readelf --wide --symbols livepatch-test.ko | grep cdrom_check_events
> 79: 0000000000000000 0 FUNC GLOBAL DEFAULT OS [0xff20] .klp.sym.cdrom.cdrom_check_events,0
>
> % readelf --wide --relocs livepatch-test.ko | awk '/cdrom_check_events/' RS="\n\n" ORS="\n\n"
> Relocation section '.klp.rela.cdrom..text.media_changed' at offset 0x97fc0 contains 1 entries:
> Offset Info Type Symbol's Value Symbol's Name + Addend
> 0000000000000016 0000004f0000000b R_X86_64_32S 0000000000000000 .klp.sym.cdrom.cdrom_check_events,0 + 0
>
> That dodges the implicit module reference on cdrom.ko, but would it
> still be safe in this klp-split POC?

That is one way to get around the dependency problem. And I think it
should work even with the PoC. It should (and I don't remember all details
now unfortunately) guarantee that the patched module is always available
for the livepatch and since there is no explicit dependency, the recursion
issue is gone.

However, I think the goal was to follow the most natural road and leverage
the existing dependency system. Meaning, since the presence of a patched
module in the system before its patching is guaranteed now, there is no
reason not to use its exported symbols directly like anywhere else. But it
introduces the recursion problem, so we may drop it.

> FWIW, I have been working an updated klp-convert for v5.6 that includes
> a bunch of fixes and such... modifying it to convert module-export
> references like this was quite easy.

*THUMBS UP* :)

> > C. Livepatch module using an exported symbol from the main livepatch module
> > for "vmlinux".
> >
> > It is the 2nd most realistic variant. It even exists in current
> > selftests. Namely, test_klp_callback_demo* modules share
> > the implementation of callbacks. It avoids code duplication.
> > And it is actually needed to make module parameters working.
>
> I had to double check this and the exports were introduced by this POC
> just to avoid code duplication, right? If so, would it be worth the
> extra code to avoid providing bad example usage? Or at least do so for
> sample/livepatch/ and not selftests.

I agree.

This case sounds a bit quirky to me.

> > Note that the current implementation allows to pass module parameters
> > only to the main livepatch module for "vmlinux". It should not be a real
> > life problem. The parameters are used in selftests. But they are
> > not used in practice.
>
> Yeah, we don't use module parameters for kpatch either, AFAIK they are
> only useful as a quick and easy method to poke those selftests.
>
> On a related note, one possible work around for case C is to use shadow
> variables created by the main livepatch module to store symbol
> locations or the values themselves. This might get tedious for real
> kernel API, but has been reasonable for livepatch bookkeeping states,
> counts, etc.
>
> > D. Livepatch modules might depend on each other. Note that dependency on
> > the main livepatch module for "vmlinux" has got a separate category 'C'.
> >
> > The dependencies between modules are quite rare. But they exist.
> > One can assume that this might be useful also on the livepatching
> > level.
> >
> > To keep it sane, the livepatch modules should just follow
> > the dependencies of the related patched modules. By other words,
> > the livepatch modules might or should have the same dependencies
> > as the patched counter parts but nothing more.
> >
>
> I agree, I think this is the only sane way to approach case D.
>
> > Do these dependencies need some special handling?
> >
> > [ ... snip ... ]
> >
>
> This is the multiple-coffee cup section that I'm still trying to wrap my
> brain around.

Yes, I really need to go through the patch set once again and try to
digest it.

Miroslav

2020-04-07 20:58:47

by Joe Lawrence

[permalink] [raw]
Subject: Re: [POC 19/23] module/livepatch: Allow to use exported symbols from livepatch module for "vmlinux"

On Tue, Apr 07, 2020 at 09:33:08AM +0200, Miroslav Benes wrote:
> On Mon, 6 Apr 2020, Joe Lawrence wrote:
>
> > On Fri, Jan 17, 2020 at 04:03:19PM +0100, Petr Mladek wrote:
> > > HINT: Get some coffee before reading this commit message.
> > >
> > > [ ... snip ... ]
> > >
> > > B. Livepatch module using an exported symbol from the patched module.
> > >
> > > It should be avoided even with the non-split livepatch module. The module
> > > loader automatically takes reference to make sure the modules are
> > > unloaded in the right order. This would basically prevent the livepatched
> > > module from unloading.
> > >
> > > Note that it would be perfectly safe to remove this automatic
> > > dependency. The livepatch framework makes sure that the livepatch
> > > module is loaded only when the patched one is loaded. But it cannot
> > > be implemented easily, see below.
> >
> > Do you envision klp-convert providing this functionality?
> >
> > [ ... snip ... ]
>
> That is one way to get around the dependency problem. And I think it
> should work even with the PoC. It should (and I don't remember all details
> now unfortunately) guarantee that the patched module is always available
> for the livepatch and since there is no explicit dependency, the recursion
> issue is gone.
>
> However, I think the goal was to follow the most natural road and leverage
> the existing dependency system. Meaning, since the presence of a patched
> module in the system before its patching is guaranteed now, there is no
> reason not to use its exported symbols directly like anywhere else. But it
> introduces the recursion problem, so we may drop it.
>
> > FWIW, I have been working an updated klp-convert for v5.6 that includes
> > a bunch of fixes and such... modifying it to convert module-export
> > references like this was quite easy.
>
> *THUMBS UP* :)

Hi Miroslav,

Perhaps the following bug report is premature, but it was far easier to
start playing with the PoC code than audit all the individual race
conditions :) This came out of the mentioned klp-convert rebase that I
later put on-top of Petr's PoC, and then started writing up some more
selftests to see how the new livepatching world might look.

Forgive me if I'm violating some obvious assumption that the PoC makes,
but I think the experiment may still be useful in pointing out a
problematic use-case / potential pitfall a livepatch author might
encounter.

tl;dr: prepare_coming_module() calls jump_label_add_module() *after* it
calls klp_module_coming(). In the PoC, the latter function now
searches for any livepatches that may apply to the loading
module and tries to load them before proceeding. If one of
those livepatch modules references any static key defined by the
originally loading module, the static key entry structures may
not be setup correctly.


The test case:

- A kernel module defines a static key called test_klp_true_key and on
__init, calls static_branch_disable(). I don't think the __init part
is required for this case, however it was just the easiest way to
write the test that I was working on at the time. AFAIK we could
change the key any where else with the same problem.

- A livepatch module that references test_klp_true_key. The overarching
test case was for the key's module owner (target) and
livepatch (livepatch__target) to toggle the key and for both target
and livepatch__target modules to be patched accordingly.

- klp-convert was enhanced to modify relocations to test_klp_true_key in
both .text and __jump_table sections. It previously could not handle
this scenario.


Pseudocode
==========

target.c
--------
static DEFINE_STATIC_KEY_TRUE(test_klp_true_key);
...
__init function() {
static_branch_disable(&test_klp_true_key);
}

livepatch__target.c
-------------------
extern struct static_key_true test_klp_true_key;
...
pr_info("static_branch_likely(&test_klp_true_key) is %s\n",
static_branch_likely(&test_klp_true_key) ? "true" : "false");


Test
====

% modprobe livepatch # livepatch__target only loads when target loads
% modprobe target

(crash in jump_label_update)


Callchain and notes
===================

(livepatch is already loaded)

target
------
load_module
apply_relocations
post_relocations
module_finalize
jump_label_apply_nops
complete_formation
mod->state = MODULE_STATE_COMING
prepare_coming_module
klp_module_coming
klp_try_load_object
patch_name = livepatch, obj_name = target

livepatch__target
-----------------
load_module
apply_relocations
post_relocations
module_finalize
jump_label_apply_nops
complete_formation
mod->state = MODULE_STATE_COMING
prepare_coming_module
blocking_notifier_call_chain(MODULE_STATE_COMING)
jump_label_module_notify (MODULE_STATE_COMING)
jump_label_add_module

first time processing test_klp_true_key, within_module()
returns false (the key is defined in the other module),
and we treat it as !static_key_linked() so the code goes
ahead and makes it linked

static_key_set_linked
key->type |= JUMP_TYPE_LINKED

do_init_module
mod->state = MODULE_STATE_LIVE;
blocking_notifier_call_chain(MODULE_STATE_LIVE)
jump_label_module_notify (MODULE_STATE_LIVE)

target
------
(prepare_coming_module cont...)
blocking_notifier_call_chain(MODULE_STATE_COMING)
jump_label_module_notify(MODULE_STATE_COMING)
jump_label_add_module

second time processing test_klp_true_key, within_module()
returns true this time and we go straight to
static_key_set_entries(), which is careful enough to verify the
that the entries aren't already linked, but not the key itself

static_key_set_entries
WARN_ON_ONCE((unsigned long)entries & JUMP_TYPE_MASK)


do_init_module
mod->state = MODULE_STATE_LIVE;
blocking_notifier_call_chain(MODULE_STATE_LIVE)
jump_label_module_notify (MODULE_STATE_LIVE)


Ok, so it seems that jump_label_add_module() assumes that any given key
that isn't present in said module, is assumed to have already been
initialized and therefore it enters that convoluted JUMP_TYPE_LINKED
code.

When we later try call jump_label_add_module() for the target module,
the same function assumes that the key is not linked and we're left with
a weird static_key_mod entry that later crashes the box.

tl;dr2: Could we safely reorder klp_module_{coming,going}() with respect
to the module_notifier callbacks? i.e.

blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_COMING, mod);
klp_module_coming(mod);
... and ...
klp_module_going(mod);
blocking_notifier_call_chain(&module_notify_list, MODULE_STATE_GOING, mod);

This fixes the above test case, but I wasn't sure if it now violates
some other part of the PoC or consistency model.

-- Joe