mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Petr Mladek <pmladek@suse.cz>
To: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>,
	"David S. Miller" <davem@davemloft.net>,
	Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>,
	Ananth N Mavinakayanahalli <ananth@in.ibm.com>,
	Frederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>,
	Steven Rostedt <rostedt@goodmis.org>,
	Jiri Kosina <jkosina@suse.cz>,
	linux-kernel@vger.kernel.org, Petr Mladek <pmladek@suse.cz>
Subject: [PATCH 4/7] kprobes: Keep consistent state of kprobes_all_disarmed
Date: Thu, 26 Feb 2015 17:13:49 +0100	[thread overview]
Message-ID: <1424967232-2923-5-git-send-email-pmladek@suse.cz> (raw)
In-Reply-To: <1424967232-2923-1-git-send-email-pmladek@suse.cz>

kprobes_all_disarmed global flag says that Kprobes are disarmed even
when the Kprobe-specific KPROBE_FLAG_DISABLED is not set.

The global flag is currently set by arm_all_probes() and disarm_all_probes()
functions even when they were not able to switch all Kprobes. It might result
in further errors.

This patch tries to restore the consistent state when some particular
Kprobe cannot be (dis)armed. In this case, it reverts the already switched
Kprobes to the previous state.

The implementation splits the cycle modifying all the probes into
separate functions, so that they could be reused to restore the
original state.

The kprobes_all_disarmed flag is modified only when all Kprobes were
successfully switched.

In case of error, we call wait_for_kprobe_optimizer() also in
arm_all_kprobes() to be on the safe side.

Signed-off-by: Petr Mladek <pmladek@suse.cz>
---
 kernel/kprobes.c | 124 ++++++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 95 insertions(+), 29 deletions(-)

diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index ba57147bd52c..1fcb19095b43 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -2353,44 +2353,117 @@ static const struct file_operations debugfs_kprobe_blacklist_ops = {
 	.release        = seq_release,
 };
 
-static int arm_all_kprobes(void)
+static int __disarm_all_kprobes(unsigned int last_table,
+				struct kprobe *last_kprobe);
+
+/*
+ * Arm all enabled Kprobes until you reach the one given by parameters.
+ * It tries to restore the original state on error.
+ *
+ * All Kprobes are handled when @last_table == KPROBE_TABLE_SIZE - 1
+ * and last_kprobe == NULL.
+ *
+ * This function need to be called under kprobe_mutex
+ */
+static int __arm_all_kprobes(unsigned int last_table,
+			     struct kprobe *last_kprobe)
 {
 	struct hlist_head *head;
 	struct kprobe *p;
 	unsigned int i;
 	int err, ret = 0;
 
-	mutex_lock(&kprobe_mutex);
-
-	/* If kprobes are armed, just return */
-	if (!kprobes_all_disarmed)
-		goto already_enabled;
-
 	/* Arming kprobes doesn't optimize kprobe itself */
-	for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
+	for (i = 0; i <= last_table ; i++) {
 		head = &kprobe_table[i];
-		hlist_for_each_entry_rcu(p, head, hlist)
+		hlist_for_each_entry_rcu(p, head, hlist) {
+			if (i == last_table && p == last_kprobe)
+				return 0;
 			if (!kprobe_disabled(p)) {
 				err = arm_kprobe(p);
-				if (err)
-					ret = err;
+				if (!err)
+					continue;
+				/*
+				 * Try to restore the original consistent state.
+				 * But only when all Kprobes are proceed here
+				 * to avoid an infinite loop.
+				 */
+				if (!last_kprobe)
+					WARN_ON(__disarm_all_kprobes(i, p));
+				return ret;
 			}
+		}
 	}
 
-	kprobes_all_disarmed = false;
-	printk(KERN_INFO "Kprobes globally enabled\n");
+	return 0;
+}
+
+static int arm_all_kprobes(void)
+{
+	int ret = 0;
+
+	mutex_lock(&kprobe_mutex);
+
+	/* If kprobes are armed, just return */
+	if (!kprobes_all_disarmed) {
+		mutex_unlock(&kprobe_mutex);
+		return 0;
+	}
+
+	ret = __arm_all_kprobes(KPROBE_TABLE_SIZE - 1, NULL);
+	if (!ret) {
+		kprobes_all_disarmed = false;
+		pr_info("Kprobes globally enabled\n");
+	}
 
-already_enabled:
 	mutex_unlock(&kprobe_mutex);
+
+	/*
+	 * On error, some Kprobes were armed and disarmed again. Be on the safe
+	 * side and wait for disarming all kprobes by optimizer in this case.
+	 */
+	if (ret)
+		wait_for_kprobe_optimizer();
+
 	return ret;
 }
 
-static int disarm_all_kprobes(void)
+/* Reverse operation for __arm_all_kprobes(), see above for details */
+static int __disarm_all_kprobes(unsigned int last_table,
+				struct kprobe *last_kprobe)
 {
 	struct hlist_head *head;
 	struct kprobe *p;
 	unsigned int i;
-	int err, ret = 0;
+	int err;
+
+	for (i = 0; i <= last_table; i++) {
+		head = &kprobe_table[i];
+		hlist_for_each_entry_rcu(p, head, hlist) {
+			if (i == last_table && p == last_kprobe)
+				return 0;
+			if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p)) {
+				err = disarm_kprobe(p, false);
+				if (!err)
+					continue;
+				/*
+				 * Try to restore the original consistent state.
+				 * But only when all Kprobes are proceed here
+				 * to avoid an infinite loop.
+				 */
+				if (!last_kprobe)
+					WARN_ON(__arm_all_kprobes(i, p));
+				return err;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int disarm_all_kprobes(void)
+{
+	int ret;
 
 	mutex_lock(&kprobe_mutex);
 
@@ -2400,24 +2473,17 @@ static int disarm_all_kprobes(void)
 		return 0;
 	}
 
-	for (i = 0; i < KPROBE_TABLE_SIZE; i++) {
-		head = &kprobe_table[i];
-		hlist_for_each_entry_rcu(p, head, hlist) {
-			if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p)) {
-				err = disarm_kprobe(p, false);
-				if (err)
-					ret = err;
-			}
-		}
+	ret = __disarm_all_kprobes(KPROBE_TABLE_SIZE - 1, NULL);
+	if (!ret) {
+		kprobes_all_disarmed = true;
+		pr_info("Kprobes globally disabled\n");
 	}
 
-	kprobes_all_disarmed = true;
-	pr_info("Kprobes globally disabled\n");
-
 	mutex_unlock(&kprobe_mutex);
 
 	/* Wait for disarming all kprobes by optimizer */
-	wait_for_kprobe_optimizer();
+	if (!ret)
+		wait_for_kprobe_optimizer();
 
 	return ret;
 }
-- 
1.8.5.6


  parent reply	other threads:[~2015-02-26 16:13 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-26 16:13 [PATCH 0/7] kprobe: Handle error when Kprobe ftrace arming fails Petr Mladek
2015-02-26 16:13 ` [PATCH 1/7] kprobes: Disable Kprobe when " Petr Mladek
2015-02-27  6:26   ` Masami Hiramatsu
2015-02-26 16:13 ` [PATCH 2/7] kprobes: Propagate error from arm_kprobe_ftrace() Petr Mladek
2015-02-27  7:35   ` Masami Hiramatsu
2015-02-26 16:13 ` [PATCH 3/7] kprobes: Propagate error from disarm_kprobe_ftrace() Petr Mladek
2015-02-27  8:01   ` Masami Hiramatsu
2015-02-26 16:13 ` Petr Mladek [this message]
2015-02-26 16:13 ` [PATCH 5/7] kprobes: Do not try to disarm already disarmed Kprobe Petr Mladek
2015-02-26 16:13 ` [PATCH 6/7] kprobes: Check kprobes_all_disarmed in kprobe_disarmed() Petr Mladek
2015-02-26 16:13 ` [PATCH 7/7] kprobes: Mark globally disabled Kprobes in debugfs interface Petr Mladek
2015-02-27  7:32 ` [PATCH 0/7] kprobe: Handle error when Kprobe ftrace arming fails Masami Hiramatsu
2015-03-12 16:33   ` Petr Mladek
2015-03-13 12:36     ` Masami Hiramatsu

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=1424967232-2923-5-git-send-email-pmladek@suse.cz \
    --to=pmladek@suse.cz \
    --cc=ananth@in.ibm.com \
    --cc=anil.s.keshavamurthy@intel.com \
    --cc=davem@davemloft.net \
    --cc=fweisbec@gmail.com \
    --cc=jkosina@suse.cz \
    --cc=linux-kernel@vger.kernel.org \
    --cc=masami.hiramatsu.pt@hitachi.com \
    --cc=mingo@redhat.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®