LKML Archive on lore.kernel.org
 help / color / Atom feed
* [BUGFIX PATCH] kprobes: Fix to cancel optimizing/unoptimizing kprobes correctly
@ 2020-01-07 14:42 Masami Hiramatsu
  2020-01-07 23:39 ` Steven Rostedt
  2020-01-10  6:03 ` [tip: core/kprobes] kprobes: Fix optimize_kprobe()/unoptimize_kprobe() cancellation logic tip-bot2 for Masami Hiramatsu
  0 siblings, 2 replies; 4+ messages in thread
From: Masami Hiramatsu @ 2020-01-07 14:42 UTC (permalink / raw)
  To: Ingo Molnar
  Cc: Alexei Starovoitov, Andy Lutomirski, Borislav Petkov,
	Linus Torvalds, Peter Zijlstra, Steven Rostedt, Thomas Gleixner,
	bristot, Naveen N . Rao, Anil S Keshavamurthy, David Miller,
	Linux Kernel Mailing List

optimize_kprobe() and unoptimize_kprobe() cancels if given kprobe
is on the optimizing_list or unoptimizing_list. However, since
commit f66c0447cca1 ("kprobes: Set unoptimized flag after
unoptimizing code") modified the update timing of the
KPROBE_FLAG_OPTIMIZED, it doesn't work as expected anymore.

The optimized_kprobe could be following states.

- [optimizing]: Before inserting jump instruction
  op.kp->flags has KPROBE_FLAG_OPTIMIZED and
  op->list is not empty.

- [optimized]: jump inserted
  op.kp->flags has KPROBE_FLAG_OPTIMIZED and
  op->list is empty.

- [unoptimizing]: Before removing jump instruction (including unused
  optprobe)
  op.kp->flags has KPROBE_FLAG_OPTIMIZED and
  op->list is not empty.

- [unoptimized]: jump removed
  op.kp->flags doesn't have KPROBE_FLAG_OPTIMIZED and
  op->list is empty.

Current code mis-expects [unoptimizing] state doesn't have
KPROBE_FLAG_OPTIMIZED, and that can cause wrong results.

This introduces optprobe_queued_unopt() to distinguish [optimizing]
and [unoptimizing] states and fixes logics in optimize_kprobe() and
unoptimize_kprobe().

Fixes: f66c0447cca1 ("kprobes: Set unoptimized flag after unoptimizing code")
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
---
 kernel/kprobes.c |   65 ++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 41 insertions(+), 24 deletions(-)

diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 34e28b236d68..d898b633f1d6 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -612,6 +612,16 @@ void wait_for_kprobe_optimizer(void)
 	mutex_unlock(&kprobe_mutex);
 }
 
+static bool optprobe_queued_unopt(struct optimized_kprobe *op)
+{
+	struct optimized_kprobe *_op;
+
+	list_for_each_entry(_op, &unoptimizing_list, list)
+		if (op == _op)
+			return true;
+	return false;
+}
+
 /* Optimize kprobe if p is ready to be optimized */
 static void optimize_kprobe(struct kprobe *p)
 {
@@ -633,17 +643,21 @@ static void optimize_kprobe(struct kprobe *p)
 		return;
 
 	/* Check if it is already optimized. */
-	if (op->kp.flags & KPROBE_FLAG_OPTIMIZED)
+	if (op->kp.flags & KPROBE_FLAG_OPTIMIZED) {
+		if (optprobe_queued_unopt(op)) {
+			/* This is under unoptimizing. Just dequeue the probe */
+			list_del_init(&op->list);
+		}
 		return;
+	}
 	op->kp.flags |= KPROBE_FLAG_OPTIMIZED;
 
-	if (!list_empty(&op->list))
-		/* This is under unoptimizing. Just dequeue the probe */
-		list_del_init(&op->list);
-	else {
-		list_add(&op->list, &optimizing_list);
-		kick_kprobe_optimizer();
-	}
+	/* On unoptimizing/optimizing_list, op must have OPTIMIZED flag */
+	if (WARN_ON_ONCE(!list_empty(&op->list)))
+		return;
+
+	list_add(&op->list, &optimizing_list);
+	kick_kprobe_optimizer();
 }
 
 /* Short cut to direct unoptimizing */
@@ -665,30 +679,33 @@ static void unoptimize_kprobe(struct kprobe *p, bool force)
 		return; /* This is not an optprobe nor optimized */
 
 	op = container_of(p, struct optimized_kprobe, kp);
-	if (!kprobe_optimized(p)) {
-		/* Unoptimized or unoptimizing case */
-		if (force && !list_empty(&op->list)) {
-			/*
-			 * Only if this is unoptimizing kprobe and forced,
-			 * forcibly unoptimize it. (No need to unoptimize
-			 * unoptimized kprobe again :)
-			 */
-			list_del_init(&op->list);
-			force_unoptimize_kprobe(op);
-		}
+	if (!kprobe_optimized(p))
 		return;
-	}
 
 	if (!list_empty(&op->list)) {
-		/* Dequeue from the optimization queue */
-		list_del_init(&op->list);
+		if (optprobe_queued_unopt(op)) {
+			/* Queued in unoptimizing queue */
+			if (force) {
+				/*
+				 * Forcibly unoptimize probe here, and queue it
+				 * in freeing list for release probe afterwards.
+				 */
+				force_unoptimize_kprobe(op);
+				list_move(&op->list, &freeing_list);
+			}
+		} else {
+			/* Dequeue from the optimizing queue */
+			list_del_init(&op->list);
+			op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+		}
 		return;
 	}
+
 	/* Optimized kprobe case */
-	if (force)
+	if (force) {
 		/* Forcibly update the code: this is a special case */
 		force_unoptimize_kprobe(op);
-	else {
+	} else {
 		list_add(&op->list, &unoptimizing_list);
 		kick_kprobe_optimizer();
 	}


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [BUGFIX PATCH] kprobes: Fix to cancel optimizing/unoptimizing kprobes correctly
  2020-01-07 14:42 [BUGFIX PATCH] kprobes: Fix to cancel optimizing/unoptimizing kprobes correctly Masami Hiramatsu
@ 2020-01-07 23:39 ` Steven Rostedt
  2020-01-08  3:02   ` Masami Hiramatsu
  2020-01-10  6:03 ` [tip: core/kprobes] kprobes: Fix optimize_kprobe()/unoptimize_kprobe() cancellation logic tip-bot2 for Masami Hiramatsu
  1 sibling, 1 reply; 4+ messages in thread
From: Steven Rostedt @ 2020-01-07 23:39 UTC (permalink / raw)
  To: Masami Hiramatsu
  Cc: Ingo Molnar, Alexei Starovoitov, Andy Lutomirski,
	Borislav Petkov, Linus Torvalds, Peter Zijlstra, Thomas Gleixner,
	bristot, Naveen N . Rao, Anil S Keshavamurthy, David Miller,
	Linux Kernel Mailing List

On Tue,  7 Jan 2020 23:42:24 +0900
Masami Hiramatsu <mhiramat@kernel.org> wrote:

> optimize_kprobe() and unoptimize_kprobe() cancels if given kprobe
> is on the optimizing_list or unoptimizing_list. However, since
> commit f66c0447cca1 ("kprobes: Set unoptimized flag after
> unoptimizing code") modified the update timing of the
> KPROBE_FLAG_OPTIMIZED, it doesn't work as expected anymore.
> 
> The optimized_kprobe could be following states.
> 
> - [optimizing]: Before inserting jump instruction
>   op.kp->flags has KPROBE_FLAG_OPTIMIZED and
>   op->list is not empty.
> 
> - [optimized]: jump inserted
>   op.kp->flags has KPROBE_FLAG_OPTIMIZED and
>   op->list is empty.
> 
> - [unoptimizing]: Before removing jump instruction (including unused
>   optprobe)
>   op.kp->flags has KPROBE_FLAG_OPTIMIZED and
>   op->list is not empty.
> 
> - [unoptimized]: jump removed
>   op.kp->flags doesn't have KPROBE_FLAG_OPTIMIZED and
>   op->list is empty.
> 
> Current code mis-expects [unoptimizing] state doesn't have
> KPROBE_FLAG_OPTIMIZED, and that can cause wrong results.
> 
> This introduces optprobe_queued_unopt() to distinguish [optimizing]
> and [unoptimizing] states and fixes logics in optimize_kprobe() and
> unoptimize_kprobe().
> 
> Fixes: f66c0447cca1 ("kprobes: Set unoptimized flag after unoptimizing code")
> Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>

Looks good.

Reviewed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>


>  		return;
>  	}
> +
>  	/* Optimized kprobe case */
> -	if (force)
> +	if (force) {
>  		/* Forcibly update the code: this is a special case */
>  		force_unoptimize_kprobe(op);
> -	else {
> +	} else {
>  		list_add(&op->list, &unoptimizing_list);
>  		kick_kprobe_optimizer();
>  	}

I see you added some clean up to this patch.

-- Steve

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [BUGFIX PATCH] kprobes: Fix to cancel optimizing/unoptimizing kprobes correctly
  2020-01-07 23:39 ` Steven Rostedt
@ 2020-01-08  3:02   ` Masami Hiramatsu
  0 siblings, 0 replies; 4+ messages in thread
From: Masami Hiramatsu @ 2020-01-08  3:02 UTC (permalink / raw)
  To: Steven Rostedt
  Cc: Ingo Molnar, Alexei Starovoitov, Andy Lutomirski,
	Borislav Petkov, Linus Torvalds, Peter Zijlstra, Thomas Gleixner,
	bristot, Naveen N . Rao, Anil S Keshavamurthy, David Miller,
	Linux Kernel Mailing List

On Tue, 7 Jan 2020 18:39:07 -0500
Steven Rostedt <rostedt@goodmis.org> wrote:

> On Tue,  7 Jan 2020 23:42:24 +0900
> Masami Hiramatsu <mhiramat@kernel.org> wrote:
> 
> > optimize_kprobe() and unoptimize_kprobe() cancels if given kprobe
> > is on the optimizing_list or unoptimizing_list. However, since
> > commit f66c0447cca1 ("kprobes: Set unoptimized flag after
> > unoptimizing code") modified the update timing of the
> > KPROBE_FLAG_OPTIMIZED, it doesn't work as expected anymore.
> > 
> > The optimized_kprobe could be following states.
> > 
> > - [optimizing]: Before inserting jump instruction
> >   op.kp->flags has KPROBE_FLAG_OPTIMIZED and
> >   op->list is not empty.
> > 
> > - [optimized]: jump inserted
> >   op.kp->flags has KPROBE_FLAG_OPTIMIZED and
> >   op->list is empty.
> > 
> > - [unoptimizing]: Before removing jump instruction (including unused
> >   optprobe)
> >   op.kp->flags has KPROBE_FLAG_OPTIMIZED and
> >   op->list is not empty.
> > 
> > - [unoptimized]: jump removed
> >   op.kp->flags doesn't have KPROBE_FLAG_OPTIMIZED and
> >   op->list is empty.
> > 
> > Current code mis-expects [unoptimizing] state doesn't have
> > KPROBE_FLAG_OPTIMIZED, and that can cause wrong results.
> > 
> > This introduces optprobe_queued_unopt() to distinguish [optimizing]
> > and [unoptimizing] states and fixes logics in optimize_kprobe() and
> > unoptimize_kprobe().
> > 
> > Fixes: f66c0447cca1 ("kprobes: Set unoptimized flag after unoptimizing code")
> > Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
> 
> Looks good.
> 
> Reviewed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>

Thank you!

> 
> 
> >  		return;
> >  	}
> > +
> >  	/* Optimized kprobe case */
> > -	if (force)
> > +	if (force) {
> >  		/* Forcibly update the code: this is a special case */
> >  		force_unoptimize_kprobe(op);
> > -	else {
> > +	} else {
> >  		list_add(&op->list, &unoptimizing_list);
> >  		kick_kprobe_optimizer();
> >  	}
> 
> I see you added some clean up to this patch.

Yeah, I felt somewhat uncomfortable for that. 

> 
> -- Steve


-- 
Masami Hiramatsu <mhiramat@kernel.org>

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [tip: core/kprobes] kprobes: Fix optimize_kprobe()/unoptimize_kprobe() cancellation logic
  2020-01-07 14:42 [BUGFIX PATCH] kprobes: Fix to cancel optimizing/unoptimizing kprobes correctly Masami Hiramatsu
  2020-01-07 23:39 ` Steven Rostedt
@ 2020-01-10  6:03 ` tip-bot2 for Masami Hiramatsu
  1 sibling, 0 replies; 4+ messages in thread
From: tip-bot2 for Masami Hiramatsu @ 2020-01-10  6:03 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Masami Hiramatsu, Steven Rostedt (VMware),
	Alexei Starovoitov, Peter Zijlstra, Thomas Gleixner, bristot,
	Ingo Molnar, x86, LKML

The following commit has been merged into the core/kprobes branch of tip:

Commit-ID:     e4add247789e4ba5e08ad8256183ce2e211877d4
Gitweb:        https://git.kernel.org/tip/e4add247789e4ba5e08ad8256183ce2e211877d4
Author:        Masami Hiramatsu <mhiramat@kernel.org>
AuthorDate:    Tue, 07 Jan 2020 23:42:24 +09:00
Committer:     Ingo Molnar <mingo@kernel.org>
CommitterDate: Thu, 09 Jan 2020 12:40:13 +01:00

kprobes: Fix optimize_kprobe()/unoptimize_kprobe() cancellation logic

optimize_kprobe() and unoptimize_kprobe() cancels if a given kprobe
is on the optimizing_list or unoptimizing_list already. However, since
the following commit:

  f66c0447cca1 ("kprobes: Set unoptimized flag after unoptimizing code")

modified the update timing of the KPROBE_FLAG_OPTIMIZED, it doesn't
work as expected anymore.

The optimized_kprobe could be in the following states:

- [optimizing]: Before inserting jump instruction
  op.kp->flags has KPROBE_FLAG_OPTIMIZED and
  op->list is not empty.

- [optimized]: jump inserted
  op.kp->flags has KPROBE_FLAG_OPTIMIZED and
  op->list is empty.

- [unoptimizing]: Before removing jump instruction (including unused
  optprobe)
  op.kp->flags has KPROBE_FLAG_OPTIMIZED and
  op->list is not empty.

- [unoptimized]: jump removed
  op.kp->flags doesn't have KPROBE_FLAG_OPTIMIZED and
  op->list is empty.

Current code mis-expects [unoptimizing] state doesn't have
KPROBE_FLAG_OPTIMIZED, and that can cause incorrect results.

To fix this, introduce optprobe_queued_unopt() to distinguish [optimizing]
and [unoptimizing] states and fixes the logic in optimize_kprobe() and
unoptimize_kprobe().

[ mingo: Cleaned up the changelog and the code a bit. ]

Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Reviewed-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: bristot@redhat.com
Fixes: f66c0447cca1 ("kprobes: Set unoptimized flag after unoptimizing code")
Link: https://lkml.kernel.org/r/157840814418.7181.13478003006386303481.stgit@devnote2
Signed-off-by: Ingo Molnar <mingo@kernel.org>
---
 kernel/kprobes.c | 67 ++++++++++++++++++++++++++++++-----------------
 1 file changed, 43 insertions(+), 24 deletions(-)

diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index 34e28b2..2625c24 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -612,6 +612,18 @@ void wait_for_kprobe_optimizer(void)
 	mutex_unlock(&kprobe_mutex);
 }
 
+static bool optprobe_queued_unopt(struct optimized_kprobe *op)
+{
+	struct optimized_kprobe *_op;
+
+	list_for_each_entry(_op, &unoptimizing_list, list) {
+		if (op == _op)
+			return true;
+	}
+
+	return false;
+}
+
 /* Optimize kprobe if p is ready to be optimized */
 static void optimize_kprobe(struct kprobe *p)
 {
@@ -633,17 +645,21 @@ static void optimize_kprobe(struct kprobe *p)
 		return;
 
 	/* Check if it is already optimized. */
-	if (op->kp.flags & KPROBE_FLAG_OPTIMIZED)
+	if (op->kp.flags & KPROBE_FLAG_OPTIMIZED) {
+		if (optprobe_queued_unopt(op)) {
+			/* This is under unoptimizing. Just dequeue the probe */
+			list_del_init(&op->list);
+		}
 		return;
+	}
 	op->kp.flags |= KPROBE_FLAG_OPTIMIZED;
 
-	if (!list_empty(&op->list))
-		/* This is under unoptimizing. Just dequeue the probe */
-		list_del_init(&op->list);
-	else {
-		list_add(&op->list, &optimizing_list);
-		kick_kprobe_optimizer();
-	}
+	/* On unoptimizing/optimizing_list, op must have OPTIMIZED flag */
+	if (WARN_ON_ONCE(!list_empty(&op->list)))
+		return;
+
+	list_add(&op->list, &optimizing_list);
+	kick_kprobe_optimizer();
 }
 
 /* Short cut to direct unoptimizing */
@@ -665,30 +681,33 @@ static void unoptimize_kprobe(struct kprobe *p, bool force)
 		return; /* This is not an optprobe nor optimized */
 
 	op = container_of(p, struct optimized_kprobe, kp);
-	if (!kprobe_optimized(p)) {
-		/* Unoptimized or unoptimizing case */
-		if (force && !list_empty(&op->list)) {
-			/*
-			 * Only if this is unoptimizing kprobe and forced,
-			 * forcibly unoptimize it. (No need to unoptimize
-			 * unoptimized kprobe again :)
-			 */
-			list_del_init(&op->list);
-			force_unoptimize_kprobe(op);
-		}
+	if (!kprobe_optimized(p))
 		return;
-	}
 
 	if (!list_empty(&op->list)) {
-		/* Dequeue from the optimization queue */
-		list_del_init(&op->list);
+		if (optprobe_queued_unopt(op)) {
+			/* Queued in unoptimizing queue */
+			if (force) {
+				/*
+				 * Forcibly unoptimize the kprobe here, and queue it
+				 * in the freeing list for release afterwards.
+				 */
+				force_unoptimize_kprobe(op);
+				list_move(&op->list, &freeing_list);
+			}
+		} else {
+			/* Dequeue from the optimizing queue */
+			list_del_init(&op->list);
+			op->kp.flags &= ~KPROBE_FLAG_OPTIMIZED;
+		}
 		return;
 	}
+
 	/* Optimized kprobe case */
-	if (force)
+	if (force) {
 		/* Forcibly update the code: this is a special case */
 		force_unoptimize_kprobe(op);
-	else {
+	} else {
 		list_add(&op->list, &unoptimizing_list);
 		kick_kprobe_optimizer();
 	}

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, back to index

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-07 14:42 [BUGFIX PATCH] kprobes: Fix to cancel optimizing/unoptimizing kprobes correctly Masami Hiramatsu
2020-01-07 23:39 ` Steven Rostedt
2020-01-08  3:02   ` Masami Hiramatsu
2020-01-10  6:03 ` [tip: core/kprobes] kprobes: Fix optimize_kprobe()/unoptimize_kprobe() cancellation logic tip-bot2 for Masami Hiramatsu

LKML Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/lkml/0 lkml/git/0.git
	git clone --mirror https://lore.kernel.org/lkml/1 lkml/git/1.git
	git clone --mirror https://lore.kernel.org/lkml/2 lkml/git/2.git
	git clone --mirror https://lore.kernel.org/lkml/3 lkml/git/3.git
	git clone --mirror https://lore.kernel.org/lkml/4 lkml/git/4.git
	git clone --mirror https://lore.kernel.org/lkml/5 lkml/git/5.git
	git clone --mirror https://lore.kernel.org/lkml/6 lkml/git/6.git
	git clone --mirror https://lore.kernel.org/lkml/7 lkml/git/7.git
	git clone --mirror https://lore.kernel.org/lkml/8 lkml/git/8.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 lkml lkml/ https://lore.kernel.org/lkml \
		linux-kernel@vger.kernel.org
	public-inbox-index lkml

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-kernel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git