mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Joe Lawrence <joe.lawrence@redhat.com>
To: live-patching@vger.kernel.org
Cc: Petr Mladek <pmladek@suse.com>,
	linux-kernel@vger.kernel.org,
	Josh Poimboeuf <jpoimboe@redhat.com>,
	Jessica Yu <jeyu@redhat.com>, Jiri Kosina <jikos@kernel.org>,
	Miroslav Benes <mbenes@suse.cz>
Subject: Re: [PATCH 3/3] livepatch: add shadow variable sample program
Date: Wed, 14 Jun 2017 11:15:09 -0400	[thread overview]
Message-ID: <035502e6-6daf-d7e3-5f34-18723c977dda@redhat.com> (raw)
In-Reply-To: <20170614142102.GA2583@pathway.suse.cz>

On 06/14/2017 10:21 AM, Petr Mladek wrote:
> On Thu 2017-06-01 14:25:26, Joe Lawrence wrote:
>> Modify the sample livepatch to demonstrate the shadow variable API.
>>
>> Signed-off-by: Joe Lawrence <joe.lawrence@redhat.com>
>> ---
>>  samples/livepatch/livepatch-sample.c | 39 +++++++++++++++++++++++++++++++++++-
>>  1 file changed, 38 insertions(+), 1 deletion(-)
>>
>> diff --git a/samples/livepatch/livepatch-sample.c b/samples/livepatch/livepatch-sample.c
>> index 84795223f15f..e0236750cefb 100644
>> --- a/samples/livepatch/livepatch-sample.c
>> +++ b/samples/livepatch/livepatch-sample.c
>> @@ -25,26 +25,57 @@
>>  
>>  /*
>>   * This (dumb) live patch overrides the function that prints the
>> - * kernel boot cmdline when /proc/cmdline is read.
>> + * kernel boot cmdline when /proc/cmdline is read.  It also
>> + * demonstrates a contrived shadow-variable usage.
>>   *
>>   * Example:
>>   *
>>   * $ cat /proc/cmdline
>>   * <your cmdline>
>> + * current=<current task pointer> count=<shadow variable counter>
>>   *
>>   * $ insmod livepatch-sample.ko
>>   * $ cat /proc/cmdline
>>   * this has been live patched
>> + * current=ffff8800331c9540 count=1
>> + * $ cat /proc/cmdline
>> + * this has been live patched
>> + * current=ffff8800331c9540 count=2
>>   *
>>   * $ echo 0 > /sys/kernel/livepatch/livepatch_sample/enabled
>>   * $ cat /proc/cmdline
>>   * <your cmdline>
>>   */
>>  
>> +static LIST_HEAD(shadow_list);
>> +
>> +struct task_ctr {
>> +	struct list_head list;
>> +	int count;
>> +};
>> +
>>  #include <linux/seq_file.h>
>> +#include <linux/slab.h>
>>  static int livepatch_cmdline_proc_show(struct seq_file *m, void *v)
>>  {
>> +	struct task_ctr *nd;
>> +
>> +	nd = klp_shadow_get(current, "task_ctr");
>> +	if (!nd) {
>> +		nd = kzalloc(sizeof(*nd), GFP_KERNEL);
>> +		if (nd) {
>> +			list_add(&nd->list, &shadow_list);
>> +			klp_shadow_attach(current, "task_ctr", GFP_KERNEL, nd);
>> +		}
>> +	}
> 
> Hmm, this looks racy:
> 
> CPU0				CPU1
> 
> klp_shadow_get()		klp_shadow_get()
> # returns NULL			# returns NULL
> if (!nd) {			if (!nd) {
>   nd = kzalloc();		  nd = kzalloc();
>   list_add(&nd->list,		  list_add(&nd->list,
> 	   &shadow_list);		&shadow_list);
> 
> BANG: This is one race. We add items into the shared shadow_list
> in parallel. The question is if we need it. IMHO, the API
> could help here, see the comments for livepatch_exit() below.
> 
>   klp_shadow_attach();		klp_shadow_attach();
> 
> WARNING: This is fine because the shadow objects are for
> different objects. I mean that "current" must point
> to different processes on the two CPUs.
> 
> But it is racy in general.

Good catch.  Let me add a disclaimer here and state that this example is
definitely contrived as I was trying to minimize its size.  Applying
shadow variables to a more real life use case would drag in a bunch of
"changed" function dependencies.  I didn't want to work around those
with a pile of kallsyms workarounds.  It did lead you to ask an
interesting question though:

>                            The question is if the API
> could help here. A possibility might be to allow to
> define a callback function that would create the shadow
> structure when it does not exist. I mean something like
> 
> typedef void (*klp_shadow_create_obj_func_t)(void * obj);
> 
> void *klp_shadow_get_or_create(void *obj, int key, gfp_t gfp,
> 				klp_shadow_create_obj_fun_t *create)
> {
> 	struct klp_shadow *shadow;
> 
> 	shadow = klp_shadow_get(obj, key);
> 
> 	if (!shadow && create) {
> 		void *shadow_obj;
> 
> 		spin_lock_irqsave(&klp_shadow_lock, flags);
> 		shadow = klp_shadow_get(obj, key);
> 		if (shadow)
> 			goto out;
> 
> 		shadow_obj = create(obj);
> 		shadow = __klp_shadow_attach(obj, key, gfp,
> 					shadow_obj);
> out:
> 		spin_unlock_irqrestore(&klp_shadow_lock, flags);
> 	}
> 
> 	return shadow;
> }
> 
> I do not know. Maybe it is too ugly. Or will it safe a duplicated code
> in many cases?

In the kpatch experience, we've typically created shadow variables when
new host variables are allocated.  That means that patched code must
handle instances of host variables with and without their shadow
counterparts.  It also avoids the race scenario above since we're not
calling klp_shadow_attach for the same <obj,key> concurrently.

That said, I think we may have written a few test patches that create
new shadow vars if klp_shadow_get fails.

A callback would eliminate a potentially racy klp_shadow_get +
klp_shadow_attach combination.  One wrinkle would be initialization.
Once the callback creates the shadow variable, any subsequent
klp_shadow_get may return it...

Perhaps it would be sufficient to add additional argument to
klp_shadow_get_or_create that gets passed to the create() function?

>>  	seq_printf(m, "%s\n", "this has been live patched");
>> +
>> +	if (nd) {
>> +		nd->count++;
>> +		seq_printf(m, "current=%p count=%d\n", current, nd->count);
>> +	}
>> +
>>  	return 0;
>>  }
>>  
>> @@ -99,6 +130,12 @@ static int livepatch_init(void)
>>  
>>  static void livepatch_exit(void)
>>  {
>> +	struct task_ctr *nd, *tmp;
>> +
>> +	list_for_each_entry_safe(nd, tmp, &shadow_list, list) {
>> +		list_del(&nd->list);
>> +		kfree(nd);
> 
> I think that this will be a rather common operation. Do we need
> the extra list? It might be enough to delete all entries
> in the hash table with a given key. Well, we might need
> a callback again:

Sorry to drop the disclaimer here again, but this was required by the
contrived example... in kpatch experience, just like we usually spot the
klp_shadow_attach to the host allocation code, we do the same for when
releasing both.  If I had done that here, I would have needed to drag in
a lot more code in order to resolve all the symbols (maybe needing
kallsym lookups :(

> typedef void (*klp_shadow_free_obj_func_t)(void *shadow_obj);
> 
> void klp_shadow_free_all(int key, klp_shadow_free_obj_fun_t *shadow_free)
> {
> 	int i;
> 	struct klp_shadow *shadow;
> 
> 	spin_lock_irqsave(&klp_shadow_lock, flags);
> 
> 	hash_for_each(klp_shadow_hash, i, shadow, node) {
> 			hash_del_rcu(&shadow->node);
> 			/* patch and shadow data are not longer used. */
> 			shadow_free(shadow->shadow_obj);
> 			/*
> 			 * shadow structure might still be traversed
> 			 * by someone
> 			 */
> 			kfree_rcu(shadow, rcu_head);
> 		}
> 	}
> 
> 	spin_unlocklock_irqrestore(&klp_shadow_lock, flags);
> }
> 
> Best Regards,
> Petr

Thanks for the review, questions and suggestions!  Let me stew on the
attach/free callback ideas for a bit -- they address a use-case that
kpatch doesn't really utilize, but may be otherwise valid.  My choice
for the sample program was definitely confusing, so I may see how
complicated a more expected implementation turns out.

-- Joe

  parent reply	other threads:[~2017-06-14 15:15 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-06-01 18:25 [PATCH 0/3] livepatch: add shadow variable API Joe Lawrence
2017-06-01 18:25 ` [PATCH 1/3] livepatch: introduce " Joe Lawrence
2017-06-08 16:49   ` Josh Poimboeuf
2017-06-09 15:36     ` Joe Lawrence
2017-06-09 18:34       ` Josh Poimboeuf
2017-06-12 13:49         ` Joe Lawrence
2017-06-14 12:59   ` Petr Mladek
2017-06-14 13:17     ` Josh Poimboeuf
2017-06-14 14:26       ` Petr Mladek
2017-06-19 16:09     ` Miroslav Benes
2017-06-01 18:25 ` [PATCH 2/3] livepatch: add shadow variable documentation Joe Lawrence
2017-06-14 13:10   ` Petr Mladek
2017-06-01 18:25 ` [PATCH 3/3] livepatch: add shadow variable sample program Joe Lawrence
2017-06-09 18:38   ` Josh Poimboeuf
2017-06-09 19:05     ` Joe Lawrence
2017-06-13 11:00   ` Miroslav Benes
2017-06-13 17:32     ` Joe Lawrence
2017-06-14  7:45       ` Miroslav Benes
2017-06-14 14:21   ` Petr Mladek
2017-06-14 14:57     ` Josh Poimboeuf
2017-06-15 10:59       ` Petr Mladek
2017-06-15 13:49         ` Josh Poimboeuf
     [not found]           ` <20170615143800.d6f5p5bdm4ueecly@redhat.com>
2017-06-15 15:23             ` Josh Poimboeuf
2017-06-19 16:56           ` Miroslav Benes
2017-06-30 20:32             ` Josh Poimboeuf
2017-07-01  6:53               ` Miroslav Benes
2017-06-14 15:15     ` Joe Lawrence [this message]
2017-06-19 16:43       ` Miroslav Benes
2017-06-01 20:05 ` [PATCH 0/3] livepatch: add shadow variable API Jiri Kosina
2017-06-01 20:23   ` Joe Lawrence
2017-06-01 20:32     ` Jiri Kosina
2017-06-13 10:19     ` Miroslav Benes
2017-06-13 17:29       ` Joe Lawrence
2017-06-14  8:02         ` Miroslav Benes

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=035502e6-6daf-d7e3-5f34-18723c977dda@redhat.com \
    --to=joe.lawrence@redhat.com \
    --cc=jeyu@redhat.com \
    --cc=jikos@kernel.org \
    --cc=jpoimboe@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=pmladek@suse.com \
    /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

all inboxes | Powered by JetHome®