All of lore.kernel.org
 help / color / mirror / Atom feed
From: Oleg Nesterov <oleg@redhat.com>
To: Ananth Mavinakayanahalli <ananth@in.ibm.com>,
	Anton Arapov <arapov@gmail.com>,
	David Long <dave.long@linaro.org>,
	Denys Vlasenko <dvlasenk@redhat.com>,
	"Frank Ch. Eigler" <fche@redhat.com>,
	Ingo Molnar <mingo@kernel.org>, Jan Willeke <willeke@de.ibm.com>,
	Jim Keniston <jkenisto@us.ibm.com>,
	Mark Wielaard <mjw@redhat.com>,
	Pratyush Anand <panand@redhat.com>,
	Srikar Dronamraju <srikar@linux.vnet.ibm.com>
Cc: linux-kernel@vger.kernel.org
Subject: [PATCH v2 11/11] uprobes/x86: Make arch_uretprobe_is_alive(RP_CHECK_CALL) more clever
Date: Tue, 7 Jul 2015 03:23:13 +0200	[thread overview]
Message-ID: <20150707012313.GA7557@redhat.com> (raw)
In-Reply-To: <20150707012210.GA7466@redhat.com>

The previous change documents that cleanup_return_instances() can't
always detect the dead frames, the stack can grow. But there is one
special case which imho worth fixing: arch_uretprobe_is_alive() can
return true when the stack didn't actually grow, but the next "call"
insn uses the already invalidated frame.

Test-case:

	#include <stdio.h>
	#include <setjmp.h>

	jmp_buf jmp;
	int nr = 1024;

	void func_2(void)
	{
		if (--nr == 0)
			return;
		longjmp(jmp, 1);
	}

	void func_1(void)
	{
		setjmp(jmp);
		func_2();
	}

	int main(void)
	{
		func_1();
		return 0;
	}

If you ret-probe func_1() and func_2() prepare_uretprobe() hits the
MAX_URETPROBE_DEPTH limit and "return" from func_2() is not reported.

When we know that the new call is not chained, we can do the more
strict check. In this case "sp" points to the new ret-addr, so every
frame which uses the same "sp" must be dead. The only complication is
that arch_uretprobe_is_alive() needs to know was it chained or not, so
we add the new RP_CHECK_CHAIN_CALL enum and change prepare_uretprobe()
to pass RP_CHECK_CALL only if !chained.

Note: arch_uretprobe_is_alive() could also re-read *sp and check if
this word is still trampoline_vaddr. This could obviously improve the
logic, but I would like to avoid another copy_from_user() especially
in the case when we can't avoid the false "alive == T" positives.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
 arch/x86/kernel/uprobes.c |    5 ++++-
 include/linux/uprobes.h   |    1 +
 kernel/events/uprobes.c   |   14 +++++++-------
 3 files changed, 12 insertions(+), 8 deletions(-)

diff --git a/arch/x86/kernel/uprobes.c b/arch/x86/kernel/uprobes.c
index 67eb168..a5c59f2 100644
--- a/arch/x86/kernel/uprobes.c
+++ b/arch/x86/kernel/uprobes.c
@@ -997,5 +997,8 @@ arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs
 bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx,
 				struct pt_regs *regs)
 {
-	return regs->sp <= ret->stack;
+	if (ctx == RP_CHECK_CALL) /* sp was just decremented by "call" insn */
+		return regs->sp < ret->stack;
+	else
+		return regs->sp <= ret->stack;
 }
diff --git a/include/linux/uprobes.h b/include/linux/uprobes.h
index c0a5402..0bdc72f 100644
--- a/include/linux/uprobes.h
+++ b/include/linux/uprobes.h
@@ -104,6 +104,7 @@ struct return_instance {
 
 enum rp_check {
 	RP_CHECK_CALL,
+	RP_CHECK_CHAIN_CALL,
 	RP_CHECK_RET,
 };
 
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index df5661a..0f370ef 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -1511,10 +1511,11 @@ static unsigned long get_trampoline_vaddr(void)
 	return trampoline_vaddr;
 }
 
-static void cleanup_return_instances(struct uprobe_task *utask, struct pt_regs *regs)
+static void cleanup_return_instances(struct uprobe_task *utask, bool chained,
+					struct pt_regs *regs)
 {
 	struct return_instance *ri = utask->return_instances;
-	enum rp_check ctx = RP_CHECK_CALL;
+	enum rp_check ctx = chained ? RP_CHECK_CHAIN_CALL : RP_CHECK_CALL;
 
 	while (ri && !arch_uretprobe_is_alive(ri, ctx, regs)) {
 		ri = free_ret_instance(ri);
@@ -1528,7 +1529,7 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
 	struct return_instance *ri;
 	struct uprobe_task *utask;
 	unsigned long orig_ret_vaddr, trampoline_vaddr;
-	bool chained = false;
+	bool chained;
 
 	if (!get_xol_area())
 		return;
@@ -1554,14 +1555,15 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
 		goto fail;
 
 	/* drop the entries invalidated by longjmp() */
-	cleanup_return_instances(utask, regs);
+	chained = (orig_ret_vaddr == trampoline_vaddr);
+	cleanup_return_instances(utask, chained, regs);
 
 	/*
 	 * We don't want to keep trampoline address in stack, rather keep the
 	 * original return address of first caller thru all the consequent
 	 * instances. This also makes breakpoint unwrapping easier.
 	 */
-	if (orig_ret_vaddr == trampoline_vaddr) {
+	if (chained) {
 		if (!utask->return_instances) {
 			/*
 			 * This situation is not possible. Likely we have an
@@ -1570,8 +1572,6 @@ static void prepare_uretprobe(struct uprobe *uprobe, struct pt_regs *regs)
 			uprobe_warn(current, "handle tail call");
 			goto fail;
 		}
-
-		chained = true;
 		orig_ret_vaddr = utask->return_instances->orig_ret_vaddr;
 	}
 
-- 
1.5.5.1


  parent reply	other threads:[~2015-07-07  1:25 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-07-07  1:22 [PATCH v2 00/11] uprobes: longjmp fixes Oleg Nesterov
2015-07-07  1:22 ` [PATCH v2 01/11] uprobes: Introduce get_uprobe() Oleg Nesterov
2015-07-07 12:44   ` Anton Arapov
2015-07-07  1:22 ` [PATCH v2 02/11] uprobes: Introduce free_ret_instance() Oleg Nesterov
2015-07-07 12:46   ` Anton Arapov
2015-07-07  1:22 ` [PATCH v2 03/11] uprobes: Send SIGILL if handle_trampoline() fails Oleg Nesterov
2015-07-07 12:51   ` Anton Arapov
2015-07-07  1:22 ` [PATCH v2 04/11] uprobes: Change prepare_uretprobe() to use uprobe_warn() Oleg Nesterov
2015-07-07 12:52   ` Anton Arapov
2015-07-07  1:22 ` [PATCH v2 05/11] uprobes: Change handle_trampoline() to find the next chain beforehand Oleg Nesterov
2015-07-07 12:54   ` Anton Arapov
2015-07-07  1:22 ` [PATCH v2 06/11] uprobes: Export struct return_instance, introduce arch_uretprobe_is_alive() Oleg Nesterov
2015-07-07 12:58   ` Anton Arapov
2015-07-10 11:52   ` Srikar Dronamraju
2015-07-07  1:22 ` [PATCH v2 07/11] uprobes/x86: Reimplement arch_uretprobe_is_alive() Oleg Nesterov
2015-07-07 13:02   ` Anton Arapov
2015-07-10 11:53   ` Srikar Dronamraju
2015-07-07  1:23 ` [PATCH v2 08/11] uprobes: Change handle_trampoline() to flush the frames invalidated by longjmp() Oleg Nesterov
2015-07-07 13:05   ` Anton Arapov
2015-07-10 11:55   ` Srikar Dronamraju
2015-07-07  1:23 ` [PATCH v2 09/11] uprobes: Change prepare_uretprobe() to (try to) flush the dead frames Oleg Nesterov
2015-07-07 13:07   ` Anton Arapov
2015-07-10 11:57   ` Srikar Dronamraju
2015-07-07  1:23 ` [PATCH v2 10/11] uprobes: Add the "enum rp_check ctx" arg to arch_uretprobe_is_alive() Oleg Nesterov
2015-07-07 13:08   ` Anton Arapov
2015-07-10 12:06   ` Srikar Dronamraju
2015-07-07  1:23 ` Oleg Nesterov [this message]
2015-07-07 13:11   ` [PATCH v2 11/11] uprobes/x86: Make arch_uretprobe_is_alive(RP_CHECK_CALL) more clever Anton Arapov
2015-07-10 12:07   ` Srikar Dronamraju
2015-07-10 12:01 ` [PATCH v2 00/11] uprobes: longjmp fixes Pratyush Anand

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=20150707012313.GA7557@redhat.com \
    --to=oleg@redhat.com \
    --cc=ananth@in.ibm.com \
    --cc=arapov@gmail.com \
    --cc=dave.long@linaro.org \
    --cc=dvlasenk@redhat.com \
    --cc=fche@redhat.com \
    --cc=jkenisto@us.ibm.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mingo@kernel.org \
    --cc=mjw@redhat.com \
    --cc=panand@redhat.com \
    --cc=srikar@linux.vnet.ibm.com \
    --cc=willeke@de.ibm.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 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.