linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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

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