From: Jiri Slaby <jslaby@suse.cz>
To: Seth Jennings <sjenning@redhat.com>,
Josh Poimboeuf <jpoimboe@redhat.com>,
Jiri Kosina <jkosina@suse.cz>, Vojtech Pavlik <vojtech@suse.cz>,
Steven Rostedt <rostedt@goodmis.org>
Cc: live-patching@vger.kernel.org, kpatch@redhat.com,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH 2/2] kernel: add support for live patching
Date: Thu, 06 Nov 2014 16:51:02 +0100 [thread overview]
Message-ID: <545B98E6.2070009@suse.cz> (raw)
In-Reply-To: <1415284748-14648-3-git-send-email-sjenning@redhat.com>
On 11/06/2014, 03:39 PM, Seth Jennings wrote:
> This commit introduces code for the live patching core. It implements
> an ftrace-based mechanism and kernel interface for doing live patching
> of kernel and kernel module functions.
Hi,
nice! So we have something to start with. Brilliant!
I have some comments below now. Yet, it obviously needs deeper review
which will take more time.
> --- /dev/null
> +++ b/include/linux/livepatch.h
> @@ -0,0 +1,45 @@
> +#ifndef _LIVEPATCH_H_
> +#define _LIVEPATCH_H_
This should follow the linux kernel naming: LINUX_LIVEPATCH_H
> +#include <linux/module.h>
> +
> +struct lp_func {
I am not much happy with "lp" which effectively means parallel printer
support. What about lip?
> + const char *old_name; /* function to be patched */
> + void *new_func; /* replacement function in patch module */
> + /*
> + * The old_addr field is optional and can be used to resolve
> + * duplicate symbol names in the vmlinux object. If this
> + * information is not present, the symbol is located by name
> + * with kallsyms. If the name is not unique and old_addr is
> + * not provided, the patch application fails as there is no
> + * way to resolve the ambiguity.
> + */
> + unsigned long old_addr;
> +};
>
> +struct lp_dynrela {
> + unsigned long dest;
> + unsigned long src;
> + unsigned long type;
> + const char *name;
> + int addend;
> + int external;
> +};
> +
> +struct lp_object {
> + const char *name; /* "vmlinux" or module name */
> + struct lp_func *funcs;
> + struct lp_dynrela *dynrelas;
> +};
> +
> +struct lp_patch {
> + struct module *mod; /* module containing the patch */
> + struct lp_object *objs;
> +};
Please document all the structures and all its members. And use
kernel-doc format for that. (You can take an inspiration in kgraft.)
> +int lp_register_patch(struct lp_patch *);
> +int lp_unregister_patch(struct lp_patch *);
> +int lp_enable_patch(struct lp_patch *);
> +int lp_disable_patch(struct lp_patch *);
> +
> +#endif /* _LIVEPATCH_H_ */
...
> --- /dev/null
> +++ b/kernel/livepatch/Makefile
> @@ -0,0 +1,3 @@
> +obj-$(CONFIG_LIVE_PATCHING) += livepatch.o
> +
> +livepatch-objs := core.o
> diff --git a/kernel/livepatch/core.c b/kernel/livepatch/core.c
> new file mode 100644
> index 0000000..b32dbb5
> --- /dev/null
> +++ b/kernel/livepatch/core.c
> @@ -0,0 +1,1020 @@
...
> +/*************************************
> + * Core structures
> + ************************************/
> +
> +/*
> + * lp_ structs vs lpc_ structs
> + *
> + * For each element (patch, object, func) in the live-patching code,
> + * there are two types with two different prefixes: lp_ and lpc_.
> + *
> + * Structures used by the live-patch modules to register with this core module
> + * are prefixed with lp_ (live patching). These structures are part of the
> + * registration API and are defined in livepatch.h. The structures used
> + * internally by this core module are prefixed with lpc_ (live patching core).
> + */
I am not sure if the separation and the allocations/kobj handling are
worth it. It makes the code really less understandable. Can we have just
struct lip_function (don't unnecessarily abbreviate), lip_objectfile
(object is too generic, like Java object) and lip_patch containing all
the needed information? It would clean up the code a lot. (Yes, we would
have profited from c++ here.)
> +static DEFINE_SEMAPHORE(lpc_mutex);
Ugh, those are deprecated. Use mutex. (Or am I missing the need of
recursive locking?)
> +static LIST_HEAD(lpc_patches);
> +
> +enum lpc_state {
> + DISABLED,
> + ENABLED
These are too generic names. This is prone to conflicts in the tree.
> +};
> +
> +struct lpc_func {
> + struct list_head list;
> + struct kobject kobj;
> + struct ftrace_ops fops;
> + enum lpc_state state;
> +
> + const char *old_name;
So you do lpc_func->old_name = lp_func->old_name.
Why? Duplication is always bad and introduces errors. The same for the
other members here and there. Well, lip_function would solve that.
> + unsigned long new_addr;
> + unsigned long old_addr;
> +};
> +
> +struct lpc_object {
> + struct list_head list;
> + struct kobject kobj;
> + struct module *mod; /* module associated with object */
> + enum lpc_state state;
> +
> + const char *name;
> + struct list_head funcs;
> + struct lp_dynrela *dynrelas;
> +};
> +
> +struct lpc_patch {
> + struct list_head list;
> + struct kobject kobj;
> + struct lp_patch *userpatch; /* for correlation during unregister */
> + enum lpc_state state;
> +
> + struct module *mod;
> + struct list_head objs;
> +};
> +
> +/*******************************************
> + * Helpers
> + *******************************************/
> +
> +/* sets obj->mod if object is not vmlinux and module was found */
> +static bool is_object_loaded(struct lpc_object *obj)
Always prefix function names. We try to avoid kallsyms duplicates ;).
> +{
> + struct module *mod;
> +
> + if (!strcmp(obj->name, "vmlinux"))
> + return 1;
> +
> + mutex_lock(&module_mutex);
> + mod = find_module(obj->name);
> + mutex_unlock(&module_mutex);
> + obj->mod = mod;
This is racy. Mod can be already gone now, right?.
> +
> + return !!mod;
> +}
> +
> +/************************************
> + * kallsyms
> + ***********************************/
> +
> +struct lpc_find_arg {
> + const char *objname;
> + const char *name;
> + unsigned long addr;
> + /*
> + * If count == 0, the symbol was not found. If count == 1, a unique
> + * match was found and addr is set. If count > 1, there is
> + * unresolvable ambiguity among "count" number of symbols with the same
> + * name in the same object.
> + */
> + unsigned long count;
> +};
...
> +static int lpc_find_symbol(const char *objname, const char *name,
> + unsigned long *addr)
The first two params can be const, right?
> +{
> + struct lpc_find_arg args = {
> + .objname = objname,
> + .name = name,
> + .addr = 0,
> + .count = 0
> + };
> +
> + if (objname && !strcmp(objname, "vmlinux"))
> + args.objname = NULL;
> +
> + kallsyms_on_each_symbol(lpc_find_callback, &args);
> +
> + if (args.count == 0)
> + pr_err("symbol '%s' not found in symbol table\n", name);
> + else if (args.count > 1)
> + pr_err("unresolvable ambiguity (%lu matches) on symbol '%s' in object '%s'\n",
> + args.count, name, objname);
> + else {
> + *addr = args.addr;
> + return 0;
> + }
> +
> + *addr = 0;
> + return -EINVAL;
> +}
...
> +/****************************************
> + * dynamic relocations (load-time linker)
> + ****************************************/
I am skipping this now (see Jiri's e-mail).
> +/***********************************
> + * ftrace registration
> + **********************************/
> +
> +static void lpc_ftrace_handler(unsigned long ip, unsigned long parent_ip,
> + struct ftrace_ops *ops, struct pt_regs *regs)
> +{
> + struct lpc_func *func = ops->private;
> +
> + regs->ip = func->new_addr;
> +}
> +
> +static int lpc_enable_func(struct lpc_func *func)
> +{
> + int ret;
> +
> + BUG_ON(!func->old_addr);
> + BUG_ON(func->state != DISABLED);
No BUGs please, just return appropriately. Possibly with WARN_ON.
> + ret = ftrace_set_filter_ip(&func->fops, func->old_addr, 0, 0);
> + if (ret) {
> + pr_err("failed to set ftrace filter for function '%s' (%d)\n",
> + func->old_name, ret);
> + return ret;
> + }
> + ret = register_ftrace_function(&func->fops);
> + if (ret) {
> + pr_err("failed to register ftrace handler for function '%s' (%d)\n",
> + func->old_name, ret);
> + ftrace_set_filter_ip(&func->fops, func->old_addr, 1, 0);
> + } else
> + func->state = ENABLED;
> +
> + return ret;
> +}
> +
> +static int lpc_unregister_func(struct lpc_func *func)
> +{
> + int ret;
> +
> + BUG_ON(func->state != ENABLED);
Detto.
> + if (!func->old_addr)
> + /* parent object is not loaded */
> + return 0;
> + ret = unregister_ftrace_function(&func->fops);
> + if (ret) {
> + pr_err("failed to unregister ftrace handler for function '%s' (%d)\n",
> + func->old_name, ret);
> + return ret;
> + }
> + ret = ftrace_set_filter_ip(&func->fops, func->old_addr, 1, 0);
> + if (ret)
> + pr_warn("function unregister succeeded but failed to clear the filter\n");
> + func->state = DISABLED;
> +
> + return 0;
> +}
> +/******************************
> + * enable/disable
> + ******************************/
...
> +/* must be called with lpc_mutex held */
> +static int lpc_enable_patch(struct lpc_patch *patch)
The question I want to raise here is whether we need two-state
registration: register+enable. We don't in kGraft. Why do you?
> +{
> + struct lpc_object *obj;
> + int ret;
> +
> + BUG_ON(patch->state != DISABLED);
No bugs...
> +
> + pr_notice_once("tainting kernel with TAINT_LIVEPATCH\n");
> + add_taint(TAINT_LIVEPATCH, LOCKDEP_STILL_OK);
> +
> + pr_notice("enabling patch '%s'\n", patch->mod->name);
> +
> + list_for_each_entry(obj, &patch->objs, list) {
> + if (!is_object_loaded(obj))
> + continue;
> + ret = lpc_enable_object(patch->mod, obj);
> + if (ret)
> + goto unregister;
> + }
> + patch->state = ENABLED;
> + return 0;
> +
> +unregister:
> + WARN_ON(lpc_disable_patch(patch));
> + return ret;
> +}
> +
> +int lp_enable_patch(struct lp_patch *userpatch)
> +{
> + struct lpc_patch *patch;
> + int ret;
> +
> + down(&lpc_mutex);
> + patch = lpc_find_patch(userpatch);
> + if (!patch) {
> + ret = -ENODEV;
> + goto out;
> + }
> + ret = lpc_enable_patch(patch);
> +out:
> + up(&lpc_mutex);
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(lp_enable_patch);
...
> +/************************************
> + * register/unregister
> + ***********************************/
> +
> +int lp_register_patch(struct lp_patch *userpatch)
This and other guys forming the interface should be documented.
> +{
> + int ret;
> +
> + if (!userpatch || !userpatch->mod || !userpatch->objs)
> + return -EINVAL;
> +
> + /*
> + * A reference is taken on the patch module to prevent it from being
> + * unloaded. Right now, we don't allow patch modules to unload since
> + * there is currently no method to determine if a thread is still
> + * running in the patched code contained in the patch module once
> + * the ftrace registration is successful.
> + */
> + if (!try_module_get(userpatch->mod))
> + return -ENODEV;
> +
> + down(&lpc_mutex);
> + ret = lpc_create_patch(userpatch);
> + up(&lpc_mutex);
> + if (ret)
> + module_put(userpatch->mod);
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(lp_register_patch);
...
Thanks for the work!
--
js
suse labs
next prev parent reply other threads:[~2014-11-06 15:51 UTC|newest]
Thread overview: 73+ messages / expand[flat|nested] mbox.gz Atom feed top
2014-11-06 14:39 [PATCH 0/2] Kernel Live Patching Seth Jennings
2014-11-06 14:39 ` [PATCH 1/2] kernel: add TAINT_LIVEPATCH Seth Jennings
2014-11-09 20:19 ` Greg KH
2014-11-11 14:54 ` Seth Jennings
2014-11-06 14:39 ` [PATCH 2/2] kernel: add support for live patching Seth Jennings
2014-11-06 15:11 ` Jiri Kosina
2014-11-06 16:20 ` Seth Jennings
2014-11-06 16:32 ` Josh Poimboeuf
2014-11-06 18:00 ` Vojtech Pavlik
2014-11-06 22:20 ` Jiri Kosina
2014-11-07 12:50 ` Josh Poimboeuf
2014-11-07 13:13 ` Jiri Kosina
2014-11-07 13:22 ` Josh Poimboeuf
2014-11-07 14:57 ` Seth Jennings
2014-11-06 15:51 ` Jiri Slaby [this message]
2014-11-06 16:57 ` Seth Jennings
2014-11-06 17:12 ` Josh Poimboeuf
2014-11-07 18:21 ` Petr Mladek
2014-11-07 20:31 ` Josh Poimboeuf
2014-11-30 12:23 ` Pavel Machek
2014-12-01 16:49 ` Seth Jennings
2014-11-06 20:02 ` Steven Rostedt
2014-11-06 20:19 ` Seth Jennings
2014-11-07 17:13 ` module notifier: was " Petr Mladek
2014-11-07 18:07 ` Seth Jennings
2014-11-07 18:40 ` Petr Mladek
2014-11-07 18:55 ` Seth Jennings
2014-11-11 19:40 ` Seth Jennings
2014-11-11 22:17 ` Jiri Kosina
2014-11-11 22:48 ` Seth Jennings
2014-11-07 17:39 ` more patches for the same func: " Petr Mladek
2014-11-07 21:54 ` Josh Poimboeuf
2014-11-07 19:40 ` Andy Lutomirski
2014-11-07 19:42 ` Seth Jennings
2014-11-07 19:52 ` Seth Jennings
2014-11-10 10:08 ` Jiri Kosina
2014-11-10 17:31 ` Josh Poimboeuf
2014-11-13 10:16 ` Miroslav Benes
2014-11-13 14:38 ` Josh Poimboeuf
2014-11-13 17:12 ` Seth Jennings
2014-11-14 13:30 ` Miroslav Benes
2014-11-14 14:52 ` Petr Mladek
2014-11-06 18:44 ` [PATCH 0/2] Kernel Live Patching Christoph Hellwig
2014-11-06 18:51 ` Vojtech Pavlik
2014-11-06 18:58 ` Christoph Hellwig
2014-11-06 19:34 ` Josh Poimboeuf
2014-11-06 19:49 ` Steven Rostedt
2014-11-06 20:02 ` Josh Poimboeuf
2014-11-07 7:46 ` Christoph Hellwig
2014-11-07 7:45 ` Christoph Hellwig
2014-11-06 20:24 ` Vojtech Pavlik
2014-11-07 7:47 ` Christoph Hellwig
2014-11-07 13:11 ` Josh Poimboeuf
2014-11-07 14:04 ` Vojtech Pavlik
2014-11-07 15:45 ` Josh Poimboeuf
2014-11-07 21:27 ` Vojtech Pavlik
2014-11-08 3:45 ` Josh Poimboeuf
2014-11-08 8:07 ` Vojtech Pavlik
2014-11-10 17:09 ` Josh Poimboeuf
2014-11-11 9:05 ` Vojtech Pavlik
2014-11-11 17:45 ` Josh Poimboeuf
2014-11-11 1:24 ` Masami Hiramatsu
2014-11-11 10:26 ` Vojtech Pavlik
2014-11-12 17:33 ` Masami Hiramatsu
2014-11-12 21:47 ` Vojtech Pavlik
2014-11-13 15:56 ` Masami Hiramatsu
2014-11-13 16:38 ` Vojtech Pavlik
2014-11-18 12:47 ` Petr Mladek
2014-11-18 18:58 ` Josh Poimboeuf
2014-11-07 12:31 ` Josh Poimboeuf
2014-11-07 12:48 ` Vojtech Pavlik
2014-11-07 13:06 ` Josh Poimboeuf
2014-11-09 20:16 ` Greg KH
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=545B98E6.2070009@suse.cz \
--to=jslaby@suse.cz \
--cc=jkosina@suse.cz \
--cc=jpoimboe@redhat.com \
--cc=kpatch@redhat.com \
--cc=linux-kernel@vger.kernel.org \
--cc=live-patching@vger.kernel.org \
--cc=rostedt@goodmis.org \
--cc=sjenning@redhat.com \
--cc=vojtech@suse.cz \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).