All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jessica Yu <jeyu@kernel.org>
To: Steven Rostedt <rostedt@goodmis.org>
Cc: Masami Hiramatsu <mhiramat@kernel.org>,
	Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>,
	Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>,
	"David S . Miller" <davem@davemloft.net>,
	Ingo Molnar <mingo@kernel.org>, Petr Mladek <pmladek@suse.com>,
	Josh Poimboeuf <jpoimboe@redhat.com>,
	Joe Lawrence <joe.lawrence@redhat.com>,
	Jiri Kosina <jikos@kernel.org>, Miroslav Benes <mbenes@suse.cz>,
	live-patching@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: kprobes: propagate error from arm_kprobe_ftrace()
Date: Wed, 3 Jan 2018 22:00:00 +0100	[thread overview]
Message-ID: <20180103205957.kp4fb4tudahcn6rj@redbean.fios-router.home> (raw)
In-Reply-To: <20180103093303.7356608d@gandalf.local.home>

+++ Steven Rostedt [03/01/18 09:33 -0500]:
>On Wed,  3 Jan 2018 02:40:47 +0100
>Jessica Yu <jeyu@kernel.org> wrote:
>
>> Improve error handling when arming ftrace-based kprobes. Specifically, if
>> we fail to arm a ftrace-based kprobe, register_kprobe()/enable_kprobe()
>> should report an error instead of success. Previously, this has lead to
>> confusing situations where register_kprobe() would return 0 indicating
>> success, but the kprobe would not be functional if ftrace registration
>> during the kprobe arming process had failed. We should therefore take any
>> errors returned by ftrace into account and propagate this error so that we
>> do not register/enable kprobes that cannot be armed. This can happen if,
>> for example, register_ftrace_function() finds an IPMODIFY conflict (since
>> kprobe_ftrace_ops has this flag set) and returns an error. Such a conflict
>> is possible since livepatches also set the IPMODIFY flag for their ftrace_ops.
>>
>> arm_all_kprobes() keeps its current behavior and attempts to arm all
>> kprobes. It returns the last encountered error and gives a warning if
>> not all probes could be armed.
>>
>> This patch is based on Petr Mladek's original patchset (patches 2 and 3)
>> back in 2015, which improved kprobes error handling, found here:
>>
>>    https://lkml.org/lkml/2015/2/26/452
>>
>> However, further work on this had been paused since then and the patches
>> were not upstreamed.
>>
>> Based-on-patches-by: Petr Mladek <pmladek@suse.com>
>> Signed-off-by: Jessica Yu <jeyu@kernel.org>
>> ---
>>  kernel/kprobes.c | 94 +++++++++++++++++++++++++++++++++++++++++---------------
>>  1 file changed, 69 insertions(+), 25 deletions(-)
>>
>> diff --git a/kernel/kprobes.c b/kernel/kprobes.c
>> index b4aab48ad258..ae6b6fe79de3 100644
>> --- a/kernel/kprobes.c
>> +++ b/kernel/kprobes.c
>> @@ -988,18 +988,32 @@ static int prepare_kprobe(struct kprobe *p)
>>  }
>>
>>  /* Caller must lock kprobe_mutex */
>> -static void arm_kprobe_ftrace(struct kprobe *p)
>> +static int arm_kprobe_ftrace(struct kprobe *p)
>>  {
>> -	int ret;
>> +	int ret = 0;
>>
>>  	ret = ftrace_set_filter_ip(&kprobe_ftrace_ops,
>>  				   (unsigned long)p->addr, 0, 0);
>> -	WARN(ret < 0, "Failed to arm kprobe-ftrace at %p (%d)\n", p->addr, ret);
>> -	kprobe_ftrace_enabled++;
>> -	if (kprobe_ftrace_enabled == 1) {
>> +	if (WARN(ret < 0, "Failed to arm kprobe-ftrace at %p (%d)\n", p->addr, ret))
>> +		return ret;
>
>I wonder if we should change this from a WARN to a printk(). No reason
>to do stack dumps here.

Yeah, I was trying to preserve the current behavior. I'll leave it up
to Masami.

>> +
>> +	if (kprobe_ftrace_enabled == 0) {
>>  		ret = register_ftrace_function(&kprobe_ftrace_ops);
>> -		WARN(ret < 0, "Failed to init kprobe-ftrace (%d)\n", ret);
>> +		if (WARN(ret < 0, "Failed to init kprobe-ftrace (%d)\n", ret))
>> +			goto err_ftrace;
>>  	}
>> +
>> +	kprobe_ftrace_enabled++;
>> +	return ret;
>> +
>> +err_ftrace:
>> +	/*
>> +	 * Note: Since kprobe_ftrace_ops has IPMODIFY set, and ftrace requires a
>> +	 * non-empty filter_hash for IPMODIFY ops, we're safe from an accidental
>> +	 * empty filter_hash which would undesirably trace all functions.
>> +	 */
>> +	ftrace_set_filter_ip(&kprobe_ftrace_ops, (unsigned long)p->addr, 1, 0);
>> +	return ret;
>>  }
>>
>>  /* Caller must lock kprobe_mutex */
>> @@ -1018,22 +1032,23 @@ static void disarm_kprobe_ftrace(struct kprobe *p)
>>  }
>>  #else	/* !CONFIG_KPROBES_ON_FTRACE */
>>  #define prepare_kprobe(p)	arch_prepare_kprobe(p)
>> -#define arm_kprobe_ftrace(p)	do {} while (0)
>> +#define arm_kprobe_ftrace(p)	(0)
>
>Hmm. Perhaps we should have arm_kprobe_ftrace() return a failure.
>
>>  #define disarm_kprobe_ftrace(p)	do {} while (0)
>>  #endif
>>
>>  /* Arm a kprobe with text_mutex */
>> -static void arm_kprobe(struct kprobe *kp)
>> +static int arm_kprobe(struct kprobe *kp)
>>  {
>> -	if (unlikely(kprobe_ftrace(kp))) {
>> -		arm_kprobe_ftrace(kp);
>> -		return;
>> -	}
>> +	if (unlikely(kprobe_ftrace(kp)))
>> +		return arm_kprobe_ftrace(kp);
>
>If CONFIG_KPROBES_ON_FTRACE is not defined, this if should always be
>false. But if for some reason in the future, it is not false, we just
>had arm_kprobe_ftrace() return success when it really is a failure.
>
> -ENODEV?

Good point, I will include this change in v4, unless there are
objections.

>> +
>>  	cpus_read_lock();
>>  	mutex_lock(&text_mutex);
>>  	__arm_kprobe(kp);
>>  	mutex_unlock(&text_mutex);
>>  	cpus_read_unlock();
>> +
>> +	return 0;
>>  }
>>
>>  /* Disarm a kprobe with text_mutex */
>> @@ -1372,9 +1387,15 @@ static int register_aggr_kprobe(struct kprobe *orig_p, struct kprobe *p)
>>
>>  	if (ret == 0 && kprobe_disabled(ap) && !kprobe_disabled(p)) {
>>  		ap->flags &= ~KPROBE_FLAG_DISABLED;
>> -		if (!kprobes_all_disarmed)
>> +		if (!kprobes_all_disarmed) {
>>  			/* Arm the breakpoint again. */
>> -			arm_kprobe(ap);
>> +			ret = arm_kprobe(ap);
>> +			if (ret) {
>> +				ap->flags |= KPROBE_FLAG_DISABLED;
>> +				list_del_rcu(&p->list);
>
>Don't we need to hold the mutex to modify the list?

It is unfortunately unclear from this snippet, but we do hold the
kprobe_mutex here. It's held for most of the duration of
register_kprobe(), where register_aggr_kprobe() is called.

>> +				synchronize_sched();
>> +			}
>> +		}
>>  	}
>>  	return ret;
>>  }
>> @@ -1594,8 +1615,14 @@ int register_kprobe(struct kprobe *p)
>>  	hlist_add_head_rcu(&p->hlist,
>>  		       &kprobe_table[hash_ptr(p->addr, KPROBE_HASH_BITS)]);
>>
>> -	if (!kprobes_all_disarmed && !kprobe_disabled(p))
>> -		arm_kprobe(p);
>> +	if (!kprobes_all_disarmed && !kprobe_disabled(p)) {
>> +		ret = arm_kprobe(p);
>> +		if (ret) {
>> +			hlist_del_rcu(&p->hlist);
>
>Same here.

We do hold kprobe_mutex here as well (see above comment).

>> +			synchronize_sched();
>> +			goto out;
>> +		}
>> +	}
>>
>>  	/* Try to optimize kprobe */
>>  	try_to_optimize_kprobe(p);
>> @@ -2137,7 +2164,9 @@ int enable_kprobe(struct kprobe *kp)
>>
>>  	if (!kprobes_all_disarmed && kprobe_disabled(p)) {
>>  		p->flags &= ~KPROBE_FLAG_DISABLED;
>> -		arm_kprobe(p);
>> +		ret = arm_kprobe(p);
>> +		if (ret)
>> +			p->flags |= KPROBE_FLAG_DISABLED;
>>  	}
>>  out:
>>  	mutex_unlock(&kprobe_mutex);
>> @@ -2565,11 +2594,12 @@ static const struct file_operations debugfs_kprobe_ei_ops = {
>>  	.release        = seq_release,
>>  };
>>
>> -static void arm_all_kprobes(void)
>> +static int arm_all_kprobes(void)
>>  {
>>  	struct hlist_head *head;
>>  	struct kprobe *p;
>> -	unsigned int i;
>> +	unsigned int i, errors = 0;
>> +	int err, ret = 0;
>>
>>  	mutex_lock(&kprobe_mutex);
>>
>> @@ -2586,16 +2616,26 @@ static void arm_all_kprobes(void)
>>  	/* Arming kprobes doesn't optimize kprobe itself */
>>  	for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
>>  		head = &kprobe_table[i];
>> -		hlist_for_each_entry_rcu(p, head, hlist)
>> -			if (!kprobe_disabled(p))
>> -				arm_kprobe(p);
>> +		/* Arm all kprobes on a best-effort basis */
>> +		hlist_for_each_entry_rcu(p, head, hlist) {
>> +			if (!kprobe_disabled(p)) {
>> +				err = arm_kprobe(p);
>> +				if (err)  {
>> +					errors++;
>> +					ret = err;
>> +				}
>> +			}
>> +		}
>>  	}
>>
>> -	printk(KERN_INFO "Kprobes globally enabled\n");
>> +	if (errors)
>> +		pr_warn("Kprobes globally enabled, but failed to arm %d probes\n", errors);
>
>Perhaps we should have a count of all kprobes that were tried, and
>write something like:
>
> "Kprobes globally enabled, but failed to arm %d out of %d probes\n",
>   errors, total

Sure, ok.

Thank you for the review!

Jessica

  reply	other threads:[~2018-01-03 21:00 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-01-03  1:40 [PATCH v3 0/2] kprobes: improve error handling when arming/disarming kprobes Jessica Yu
2018-01-03  1:40 ` [PATCH v3 1/2] kprobes: propagate error from arm_kprobe_ftrace() Jessica Yu
2018-01-03 14:33   ` Steven Rostedt
2018-01-03 21:00     ` Jessica Yu [this message]
2018-01-04 14:42       ` Masami Hiramatsu
2018-01-03  1:40 ` [PATCH v3 2/2] kprobes: propagate error from disarm_kprobe_ftrace() Jessica Yu
  -- strict thread matches above, loose matches on Subject: below --
2017-11-02 16:33 [PATCH v2 0/2] kprobes: improve error handling when arming/disarming kprobes Jessica Yu
2017-11-02 16:33 ` [PATCH v2 1/2] kprobes: propagate error from arm_kprobe_ftrace() Jessica Yu
2017-11-03 14:03   ` Steven Rostedt
2017-11-07 17:14     ` Jessica Yu
2017-11-09  0:35       ` Masami Hiramatsu
2017-11-21 14:47         ` Jessica Yu
2017-10-04 19:14 [PATCH 0/2] kprobes: improve error handling when arming/disarming kprobes Jessica Yu
2017-10-04 19:14 ` [PATCH 1/2] kprobes: propagate error from arm_kprobe_ftrace() Jessica Yu
2017-10-05  6:23   ` Masami Hiramatsu
2017-10-07 10:52     ` Jessica Yu

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=20180103205957.kp4fb4tudahcn6rj@redbean.fios-router.home \
    --to=jeyu@kernel.org \
    --cc=ananth@linux.vnet.ibm.com \
    --cc=anil.s.keshavamurthy@intel.com \
    --cc=davem@davemloft.net \
    --cc=jikos@kernel.org \
    --cc=joe.lawrence@redhat.com \
    --cc=jpoimboe@redhat.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=live-patching@vger.kernel.org \
    --cc=mbenes@suse.cz \
    --cc=mhiramat@kernel.org \
    --cc=mingo@kernel.org \
    --cc=pmladek@suse.com \
    --cc=rostedt@goodmis.org \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.