linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Masami Hiramatsu <mhiramat@kernel.org>
To: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Ingo Molnar <mingo@redhat.com>,
	Josh Poimboeuf <jpoimboe@redhat.com>,
	linux-kernel@vger.kernel.org,
	Peter Zijlstra <peterz@infradead.org>,
	Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>,
	Thomas Gleixner <tglx@linutronix.de>,
	"H . Peter Anvin" <hpa@zytor.com>,
	Andrey Konovalov <andreyknvl@google.com>
Subject: Re: [PATCH tip/master v2] kprobes: extable: Identify kprobes' insn-slots as kernel text area
Date: Tue, 27 Dec 2016 15:13:15 +0900	[thread overview]
Message-ID: <20161227151315.05fc6f762f0c7870deedaf89@kernel.org> (raw)
In-Reply-To: <148276645016.10706.96498003200762808.stgit@devbox>

Oops, I found I missed an rcu_read_unlock on the fast path...
I'll send v3.

Thanks,


On Tue, 27 Dec 2016 00:34:20 +0900
Masami Hiramatsu <mhiramat@kernel.org> wrote:

> Make __kernel_text_address()/kernel_text_address() returns
> true if the given address is on a kprobe's instruction slot,
> which is generated by kprobes as a trampoline code.
> This can help stacktraces to determine the address is on a
> text area or not.
> 
> To implement this without any sleep in is_kprobe_*_slot(),
> this also modify insn_cache page list as a rcu list. It may
> increase processing deley (not processing time) for garbage
> slot collection, because it requires to wait an additional
> rcu grance period when freeing a page from the list.
> However, since it is not a hot path, we may not take care of it.
> 
> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
> 
> ---
>  V2: Fix build error when CONFIG_KPROBES=n
> 
>  Hi Josh, could check this patch fixes your issue? It will
>  enable unwinder code to validate return address by using
>  __kernel_text_address() again.
> ---
>  include/linux/kprobes.h |   22 ++++++++++++++-
>  kernel/extable.c        |    9 +++++-
>  kernel/kprobes.c        |   70 ++++++++++++++++++++++++++++++++++++-----------
>  3 files changed, 82 insertions(+), 19 deletions(-)
> 
> diff --git a/include/linux/kprobes.h b/include/linux/kprobes.h
> index 8f68490..f0496b0 100644
> --- a/include/linux/kprobes.h
> +++ b/include/linux/kprobes.h
> @@ -281,6 +281,9 @@ struct kprobe_insn_cache {
>  extern kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c);
>  extern void __free_insn_slot(struct kprobe_insn_cache *c,
>  			     kprobe_opcode_t *slot, int dirty);
> +/* sleep-less address checking routine  */
> +extern bool __is_insn_slot_addr(struct kprobe_insn_cache *c,
> +				unsigned long addr);
>  
>  #define DEFINE_INSN_CACHE_OPS(__name)					\
>  extern struct kprobe_insn_cache kprobe_##__name##_slots;		\
> @@ -294,6 +297,11 @@ static inline void free_##__name##_slot(kprobe_opcode_t *slot, int dirty)\
>  {									\
>  	__free_insn_slot(&kprobe_##__name##_slots, slot, dirty);	\
>  }									\
> +									\
> +static inline bool is_kprobe_##__name##_slot(unsigned long addr)	\
> +{									\
> +	return __is_insn_slot_addr(&kprobe_##__name##_slots, addr);	\
> +}
>  
>  DEFINE_INSN_CACHE_OPS(insn);
>  
> @@ -330,7 +338,6 @@ extern int proc_kprobes_optimization_handler(struct ctl_table *table,
>  					     int write, void __user *buffer,
>  					     size_t *length, loff_t *ppos);
>  #endif
> -
>  #endif /* CONFIG_OPTPROBES */
>  #ifdef CONFIG_KPROBES_ON_FTRACE
>  extern void kprobe_ftrace_handler(unsigned long ip, unsigned long parent_ip,
> @@ -481,6 +488,19 @@ static inline int enable_jprobe(struct jprobe *jp)
>  	return enable_kprobe(&jp->kp);
>  }
>  
> +#ifndef CONFIG_KPROBES
> +static inline bool is_kprobe_insn_slot(unsigned long addr)
> +{
> +	return false;
> +}
> +#endif
> +#ifndef CONFIG_OPTPROBES
> +static inline bool is_kprobe_optinsn_slot(unsigned long addr)
> +{
> +	return false;
> +}
> +#endif
> +
>  #ifdef CONFIG_KPROBES
>  /*
>   * Blacklist ganerating macro. Specify functions which is not probed
> diff --git a/kernel/extable.c b/kernel/extable.c
> index e820cce..81c9633 100644
> --- a/kernel/extable.c
> +++ b/kernel/extable.c
> @@ -20,6 +20,7 @@
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  #include <linux/init.h>
> +#include <linux/kprobes.h>
>  
>  #include <asm/sections.h>
>  #include <asm/uaccess.h>
> @@ -104,6 +105,8 @@ int __kernel_text_address(unsigned long addr)
>  		return 1;
>  	if (is_ftrace_trampoline(addr))
>  		return 1;
> +	if (is_kprobe_optinsn_slot(addr) || is_kprobe_insn_slot(addr))
> +		return 1;
>  	/*
>  	 * There might be init symbols in saved stacktraces.
>  	 * Give those symbols a chance to be printed in
> @@ -123,7 +126,11 @@ int kernel_text_address(unsigned long addr)
>  		return 1;
>  	if (is_module_text_address(addr))
>  		return 1;
> -	return is_ftrace_trampoline(addr);
> +	if (is_ftrace_trampoline(addr))
> +		return 1;
> +	if (is_kprobe_optinsn_slot(addr) || is_kprobe_insn_slot(addr))
> +		return 1;
> +	return 0;
>  }
>  
>  /*
> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
> index d630954..1bd1c17 100644
> --- a/kernel/kprobes.c
> +++ b/kernel/kprobes.c
> @@ -149,9 +149,11 @@ kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
>  	struct kprobe_insn_page *kip;
>  	kprobe_opcode_t *slot = NULL;
>  
> +	/* Since the slot array is not protected by rcu, we need a mutex */
>  	mutex_lock(&c->mutex);
>   retry:
> -	list_for_each_entry(kip, &c->pages, list) {
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(kip, &c->pages, list) {
>  		if (kip->nused < slots_per_page(c)) {
>  			int i;
>  			for (i = 0; i < slots_per_page(c); i++) {
> @@ -167,6 +169,7 @@ kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
>  			WARN_ON(1);
>  		}
>  	}
> +	rcu_read_unlock();
>  
>  	/* If there are any garbage slots, collect it and try again. */
>  	if (c->nr_garbage && collect_garbage_slots(c) == 0)
> @@ -193,13 +196,15 @@ kprobe_opcode_t *__get_insn_slot(struct kprobe_insn_cache *c)
>  	kip->nused = 1;
>  	kip->ngarbage = 0;
>  	kip->cache = c;
> -	list_add(&kip->list, &c->pages);
> +	list_add_rcu(&kip->list, &c->pages);
>  	slot = kip->insns;
>  out:
>  	mutex_unlock(&c->mutex);
>  	return slot;
>  }
>  
> +
> +
>  /* Return 1 if all garbages are collected, otherwise 0. */
>  static int collect_one_slot(struct kprobe_insn_page *kip, int idx)
>  {
> @@ -213,7 +218,8 @@ static int collect_one_slot(struct kprobe_insn_page *kip, int idx)
>  		 * next time somebody inserts a probe.
>  		 */
>  		if (!list_is_singular(&kip->list)) {
> -			list_del(&kip->list);
> +			list_del_rcu(&kip->list);
> +			synchronize_rcu();
>  			kip->cache->free(kip->insns);
>  			kfree(kip);
>  		}
> @@ -248,29 +254,59 @@ void __free_insn_slot(struct kprobe_insn_cache *c,
>  		      kprobe_opcode_t *slot, int dirty)
>  {
>  	struct kprobe_insn_page *kip;
> +	long idx;
>  
>  	mutex_lock(&c->mutex);
> -	list_for_each_entry(kip, &c->pages, list) {
> -		long idx = ((long)slot - (long)kip->insns) /
> -				(c->insn_size * sizeof(kprobe_opcode_t));
> -		if (idx >= 0 && idx < slots_per_page(c)) {
> -			WARN_ON(kip->slot_used[idx] != SLOT_USED);
> -			if (dirty) {
> -				kip->slot_used[idx] = SLOT_DIRTY;
> -				kip->ngarbage++;
> -				if (++c->nr_garbage > slots_per_page(c))
> -					collect_garbage_slots(c);
> -			} else
> -				collect_one_slot(kip, idx);
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(kip, &c->pages, list) {
> +		idx = ((long)slot - (long)kip->insns) /
> +			(c->insn_size * sizeof(kprobe_opcode_t));
> +		if (idx >= 0 && idx < slots_per_page(c))
>  			goto out;
> -		}
>  	}
> -	/* Could not free this slot. */
> +	/* Could not find this slot. */
>  	WARN_ON(1);
> +	kip = NULL;
>  out:
> +	rcu_read_unlock();
> +	/* Mark and sweep: this may sleep */
> +	if (kip) {
> +		/* Check double free */
> +		WARN_ON(kip->slot_used[idx] != SLOT_USED);
> +		if (dirty) {
> +			kip->slot_used[idx] = SLOT_DIRTY;
> +			kip->ngarbage++;
> +			if (++c->nr_garbage > slots_per_page(c))
> +				collect_garbage_slots(c);
> +		} else
> +			collect_one_slot(kip, idx);
> +	}
>  	mutex_unlock(&c->mutex);
>  }
>  
> +/*
> + * Check given address is on the page of kprobe instruction slots.
> + * This will be used for checking whether the address on a stack
> + * is on a text area or not.
> + */
> +bool __is_insn_slot_addr(struct kprobe_insn_cache *c, unsigned long addr)
> +{
> +	struct kprobe_insn_page *kip;
> +	bool ret = false;
> +
> +	rcu_read_lock();
> +	list_for_each_entry_rcu(kip, &c->pages, list) {
> +		if (addr >= (unsigned long)kip->insns &&
> +		    addr < (unsigned long)kip->insns + PAGE_SIZE) {
> +			ret = true;
> +			break;
> +		}
> +	}
> +	rcu_read_unlock();
> +
> +	return ret;
> +}
> +
>  #ifdef CONFIG_OPTPROBES
>  /* For optimized_kprobe buffer */
>  struct kprobe_insn_cache kprobe_optinsn_slots = {
> 


-- 
Masami Hiramatsu <mhiramat@kernel.org>

  parent reply	other threads:[~2016-12-27  6:24 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-12-22  6:42 Detecting kprobes generated code addresses Josh Poimboeuf
2016-12-25  3:13 ` Masami Hiramatsu
2016-12-25  6:16   ` Masami Hiramatsu
2016-12-26  4:30     ` Masami Hiramatsu
2016-12-26 14:50       ` [PATCH tip/master] kprobes: extable: Identify kprobes' insn-slots as kernel text area Masami Hiramatsu
2016-12-26 15:34       ` [PATCH tip/master v2] " Masami Hiramatsu
2016-12-26 17:21         ` kbuild test robot
2016-12-26 17:46         ` kbuild test robot
2016-12-27  6:13         ` Masami Hiramatsu [this message]
2016-12-27  6:14       ` [PATCH tip/master v3] " Masami Hiramatsu
2017-01-03 10:54         ` Peter Zijlstra
2017-01-04  5:06           ` Masami Hiramatsu
2017-01-04 10:01             ` Peter Zijlstra
2017-01-08  4:22               ` Masami Hiramatsu
2017-01-08 12:31                 ` Masami Hiramatsu
2017-01-08 14:58               ` [PATCH tip/master v4] " Masami Hiramatsu
2017-01-09 17:36                 ` Josh Poimboeuf
2017-01-10  1:11                   ` Masami Hiramatsu
2017-01-10  8:59                   ` Peter Zijlstra
2017-01-10 21:42                     ` Josh Poimboeuf
2017-01-11  9:57                       ` Masami Hiramatsu
2017-01-14  9:56                 ` [tip:perf/core] kprobes, extable: Identify kprobes trampolines " tip-bot for Masami Hiramatsu
2017-01-04 14:21           ` [PATCH tip/master v3] kprobes: extable: Identify kprobes' insn-slots " Steven Rostedt

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=20161227151315.05fc6f762f0c7870deedaf89@kernel.org \
    --to=mhiramat@kernel.org \
    --cc=ananth@linux.vnet.ibm.com \
    --cc=andreyknvl@google.com \
    --cc=hpa@zytor.com \
    --cc=jpoimboe@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --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).