linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Petr Mladek <pmladek@suse.cz>
To: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Seth Jennings <sjenning@redhat.com>,
	Jiri Kosina <jkosina@suse.cz>,
	Rusty Russell <rusty@rustcorp.com.au>,
	Miroslav Benes <mbenes@suse.cz>,
	Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>,
	mingo@kernel.org, mathieu.desnoyers@efficios.com,
	oleg@redhat.com, paulmck@linux.vnet.ibm.com,
	live-patching@vger.kernel.org, linux-kernel@vger.kernel.org,
	andi@firstfloor.org, rostedt@goodmis.org, tglx@linutronix.de
Subject: Re: [PATCH v2 1/2] livepatch/module: Apply patch when loaded module is unformed
Date: Fri, 6 Mar 2015 15:00:13 +0100	[thread overview]
Message-ID: <20150306140013.GL15177@pathway.suse.cz> (raw)
In-Reply-To: <20150306102032.GI15177@pathway.suse.cz>

On Fri 2015-03-06 11:20:32, Petr Mladek wrote:
> On Thu 2015-03-05 13:34:33, Josh Poimboeuf wrote:
> > On Thu, Mar 05, 2015 at 04:45:13PM +0100, Petr Mladek wrote:
> > > Existing live patches are applied to loaded modules using a notify handler.
> > > There are two problems with this approach.
> > > 
> > > First, errors from module notifiers are ignored and could not stop the module
> > > from being loaded. But we will need to refuse the module when there are
> > > semantics dependencies between functions and there are some problems
> > > to apply the patch to the module. Otherwise, the system might become
> > > into an inconsistent state.
> > > 
> > > Second, the module notifiers are called when the module is in
> > > STATE_MODULE_COMING. It means that it is visible by find_module()
> > > and can be detected by klp_find_object_module() when a new patch is
> > > registered.
> > > 
> > > Now, the timing is important. If the new patch is registered after the module
> > > notifier has been called, it has to initialize the module object for the new
> > > patch. Note that, in this case, the new patch has to see the module as loaded
> > > even when it is still in the COMING state.
> > > 
> > > But when the new patch is registered before the module notifier, it _should_
> > > not initialize the module object, see below for detailed explanation.
> > > 
> > > This patch solves both problems by calling klp_module_init() directly in
> > > load_module(). We could handle the error there. Also it is called in
> > > MODULE_STATE_UNFORMED and therefore before the module is visible via
> > > find_module().
> > > 
> > > The implementation creates three functions for module init and three
> > > functions for going modules. We need to revert already initialized
> > > patches when something fails and thus need to be able to call
> > > the code for going modules without leaving klp_mutex.
> > > 
> > > Detailed explanation of the last problem:
> > > 
> > > Why should not we initialize the module object for a new patch when
> > > the related module coming notifier has not been called yet?
> > > 
> > > Note that the notifier could _not_ _simply_ ignore already initialized module
> > > objects. The notifier initializes the module object for all existing patches.
> > > If the new patch is registered and enabled before, it would crate wrong
> > > order of patches in fops->func_stack.
> > > 
> > > For example, let's have three patches (P1, P2, P3) for the functions a()
> > > and b() where a() is from vmcore and b() is from a module M. Something
> > > like:
> > > 
> > > 	a()	b()
> > > P1	a1()	b1()
> > > P2	a2()	b2()
> > > P3	a3()	b3(3)
> > > 
> > > If you load the module M after all patches are registered and enabled.
> > > The ftrace ops for function a() and b() has listed the functions in this
> > > order
> > > 
> > > 	ops_a->func_stack -> list(a3,a2,a1)
> > > 	ops_b->func_stack -> list(b3,b2,b1)
> > > 
> > > , so the pointer to b3() is the first and will be used.
> > > 
> > > Then you might have the following scenario. Let's start with state
> > > when patches P1 and P2 are registered and enabled but the module M
> > > is not loaded. Then ftrace ops for b() does not exist. Then we
> > > get into the following race:
> > > 
> > > CPU0					CPU1
> > > 
> > > load_module(M)
> > > 
> > >   complete_formation()
> > > 
> > >   mod->state = MODULE_STATE_COMING;
> > >   mutex_unlock(&module_mutex);
> > > 
> > > 					klp_register_patch(P3);
> > > 					klp_enable_patch(P3);
> > > 
> > > 					# STATE 1
> > > 
> > >   klp_module_notify(M)
> > >     klp_module_notify_coming(P1);
> > >     klp_module_notify_coming(P2);
> > >     klp_module_notify_coming(P3);
> > > 
> > > 					# STATE 2
> > > 
> > > The ftrace ops for a() and b() then looks:
> > > 
> > >   STATE1:
> > > 
> > > 	ops_a->func_stack -> list(a3,a2,a1);
> > > 	ops_b->func_stack -> list(b3);
> > > 
> > >   STATE2:
> > > 	ops_a->func_stack -> list(a3,a2,a1);
> > > 	ops_b->func_stack -> list(b2,b1,b3);
> > > 
> > > therefore, b2() is used for the module but a3() is used for vmcore
> > > because they were the last added.
> > > 
> > > Signed-off-by: Petr Mladek <pmladek@suse.cz>
> > > ---
[...]

> > > diff --git a/kernel/module.c b/kernel/module.c
> > > index d856e96a3cce..f744a639460d 100644
> > > --- a/kernel/module.c
> > > +++ b/kernel/module.c
> > > @@ -53,6 +53,7 @@
> > >  #include <asm/sections.h>
> > >  #include <linux/tracepoint.h>
> > >  #include <linux/ftrace.h>
> > > +#include <linux/livepatch.h>
> > >  #include <linux/async.h>
> > >  #include <linux/percpu.h>
> > >  #include <linux/kmemleak.h>
> > > @@ -3321,6 +3322,14 @@ static int load_module(struct load_info *info, const char __user *uargs,
> > >  	/* Ftrace init must be called in the MODULE_STATE_UNFORMED state */
> > >  	ftrace_module_init(mod);
> > >  
> > > +	/*
> > > +	 * LivePatch init must be called in the MODULE_STATE_UNFORMED state
> > > +	 * and it might reject the module to avoid a system inconsistency.
> > > +	 */
> > 
> > nit: I thought we were calling it livepatch (all lowercase).
> 
> will fix
> 
> > > +	err = klp_module_init(mod);
> > > +	if (err)
> > > +		goto ddebug_cleanup;
> > > +
> > >  	/* Finally it's fully formed, ready to start executing. */
> > >  	err = complete_formation(mod, info);
> > >  	if (err)
> > 
> > Hm, we still have a problem with the timing here.  The kallsyms lookup
> > functions ignore MODULE_STATE_UNFORMED modules.  So
> > klp_find_verify_func_addr() will fail to find the func address and the
> > module will always fail to load.
> 
> Hrmmpfffff, my head was relaxing somewhere in the corner when I tested
> the patch. You are right, it does not work. Huh, I wonder why we are
> able to find the address in kGraft. We are using this approach there
> for a long time.

Sigh, kGraft calls kgr_module_init() after complete_formation() and
thus in MODULE_STATE_COMING. I should have refreshed my mind. There is
even a comment about this that I have written many months ago.

This brings me back to the original idea with that boolean that
marks the state before and after the coming notifier (module_init).
We could use a bitfield instead of the two booleans when requested.


Alternative solutions:

+ reject new patches when a module is coming; this is ugly

+ wait with adding new patch until the module leaves the COMING
  state; this might be dangerous or complicated; we would need
  to leave kgr_lock in the middle of the patch registration to
  avoid a deadlock with klp_module_init(); also we might need
  a waitqueue for each module which seems to be even bigger
  overhead than the two booleans

+ always register/enable new patches and fix up the potential
  mess (registered patches order) in klp_module_init(); This
  is nasty and prone to regressions in the future development;

+ add another MODULE_STATE where the kallsyms are visible
  but the module is not used yet; this looks to complex;
  the module states are checked on "many" locations


I will wait with v3 over the weekend. I hope that it will bring fresh
mind. Sigh, if I could have slept more with the baby twins.

Best Regards,
Petr

  reply	other threads:[~2015-03-06 13:59 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-03-05 15:45 [PATCH v2 0/2] livepatch/module: Avoid races between modules and live patches Petr Mladek
2015-03-05 15:45 ` [PATCH v2 1/2] livepatch/module: Apply patch when loaded module is unformed Petr Mladek
2015-03-05 19:34   ` Josh Poimboeuf
2015-03-06 10:20     ` Petr Mladek
2015-03-06 14:00       ` Petr Mladek [this message]
2015-03-06 14:54         ` Josh Poimboeuf
2015-03-06 15:35           ` Petr Mladek
2015-03-05 15:45 ` [PATCH v2 2/2] livepatch/module: Correctly handle going modules Petr Mladek
2015-03-05 19:35   ` Josh Poimboeuf
2015-03-07  1:04   ` Rusty Russell
2015-03-09  9:16     ` Petr Mladek
2015-03-10  2:23       ` Rusty Russell
2015-03-10 11:15         ` Petr Mladek

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=20150306140013.GL15177@pathway.suse.cz \
    --to=pmladek@suse.cz \
    --cc=andi@firstfloor.org \
    --cc=jkosina@suse.cz \
    --cc=jpoimboe@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=masami.hiramatsu.pt@hitachi.com \
    --cc=mathieu.desnoyers@efficios.com \
    --cc=mbenes@suse.cz \
    --cc=mingo@kernel.org \
    --cc=oleg@redhat.com \
    --cc=paulmck@linux.vnet.ibm.com \
    --cc=rostedt@goodmis.org \
    --cc=rusty@rustcorp.com.au \
    --cc=sjenning@redhat.com \
    --cc=tglx@linutronix.de \
    /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).