All of lore.kernel.org
 help / color / mirror / 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, other threads:[~2020-01-10  6:03 UTC | newest]

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

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.