diff options
author | Jessica Yu <jeyu@kernel.org> | 2018-01-09 18:51:24 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2018-02-16 03:12:58 -0500 |
commit | 297f9233b53a08fd457815e19f1d6f2c3389857b (patch) | |
tree | a69f719f75c4e11a8fea600b1f92ea0ee25d6d0a /kernel/kprobes.c | |
parent | 12310e3437554328bcd75186cf331bc712cb30b2 (diff) |
kprobes: Propagate error from disarm_kprobe_ftrace()
Improve error handling when disarming ftrace-based kprobes. Like with
arm_kprobe_ftrace(), propagate any errors from disarm_kprobe_ftrace() so
that we do not disable/unregister kprobes that are still armed. In other
words, unregister_kprobe() and disable_kprobe() should not report success
if the kprobe could not be disarmed.
disarm_all_kprobes() keeps its current behavior and attempts to
disarm all kprobes. It returns the last encountered error and gives a
warning if not all probes could be disarmed.
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>
Acked-by: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Ananth N Mavinakayanahalli <ananth@linux.vnet.ibm.com>
Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Cc: David S . Miller <davem@davemloft.net>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Joe Lawrence <joe.lawrence@redhat.com>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Miroslav Benes <mbenes@suse.cz>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: live-patching@vger.kernel.org
Link: http://lkml.kernel.org/r/20180109235124.30886-3-jeyu@kernel.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/kprobes.c')
-rw-r--r-- | kernel/kprobes.c | 78 |
1 files changed, 53 insertions, 25 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 2d988141ab85..102160ff5c66 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -1011,23 +1011,27 @@ err_ftrace: | |||
1011 | } | 1011 | } |
1012 | 1012 | ||
1013 | /* Caller must lock kprobe_mutex */ | 1013 | /* Caller must lock kprobe_mutex */ |
1014 | static void disarm_kprobe_ftrace(struct kprobe *p) | 1014 | static int disarm_kprobe_ftrace(struct kprobe *p) |
1015 | { | 1015 | { |
1016 | int ret; | 1016 | int ret = 0; |
1017 | 1017 | ||
1018 | kprobe_ftrace_enabled--; | 1018 | if (kprobe_ftrace_enabled == 1) { |
1019 | if (kprobe_ftrace_enabled == 0) { | ||
1020 | ret = unregister_ftrace_function(&kprobe_ftrace_ops); | 1019 | ret = unregister_ftrace_function(&kprobe_ftrace_ops); |
1021 | WARN(ret < 0, "Failed to init kprobe-ftrace (%d)\n", ret); | 1020 | if (WARN(ret < 0, "Failed to unregister kprobe-ftrace (%d)\n", ret)) |
1021 | return ret; | ||
1022 | } | 1022 | } |
1023 | |||
1024 | kprobe_ftrace_enabled--; | ||
1025 | |||
1023 | ret = ftrace_set_filter_ip(&kprobe_ftrace_ops, | 1026 | ret = ftrace_set_filter_ip(&kprobe_ftrace_ops, |
1024 | (unsigned long)p->addr, 1, 0); | 1027 | (unsigned long)p->addr, 1, 0); |
1025 | WARN(ret < 0, "Failed to disarm kprobe-ftrace at %p (%d)\n", p->addr, ret); | 1028 | WARN(ret < 0, "Failed to disarm kprobe-ftrace at %p (%d)\n", p->addr, ret); |
1029 | return ret; | ||
1026 | } | 1030 | } |
1027 | #else /* !CONFIG_KPROBES_ON_FTRACE */ | 1031 | #else /* !CONFIG_KPROBES_ON_FTRACE */ |
1028 | #define prepare_kprobe(p) arch_prepare_kprobe(p) | 1032 | #define prepare_kprobe(p) arch_prepare_kprobe(p) |
1029 | #define arm_kprobe_ftrace(p) (-ENODEV) | 1033 | #define arm_kprobe_ftrace(p) (-ENODEV) |
1030 | #define disarm_kprobe_ftrace(p) do {} while (0) | 1034 | #define disarm_kprobe_ftrace(p) (-ENODEV) |
1031 | #endif | 1035 | #endif |
1032 | 1036 | ||
1033 | /* Arm a kprobe with text_mutex */ | 1037 | /* Arm a kprobe with text_mutex */ |
@@ -1046,18 +1050,18 @@ static int arm_kprobe(struct kprobe *kp) | |||
1046 | } | 1050 | } |
1047 | 1051 | ||
1048 | /* Disarm a kprobe with text_mutex */ | 1052 | /* Disarm a kprobe with text_mutex */ |
1049 | static void disarm_kprobe(struct kprobe *kp, bool reopt) | 1053 | static int disarm_kprobe(struct kprobe *kp, bool reopt) |
1050 | { | 1054 | { |
1051 | if (unlikely(kprobe_ftrace(kp))) { | 1055 | if (unlikely(kprobe_ftrace(kp))) |
1052 | disarm_kprobe_ftrace(kp); | 1056 | return disarm_kprobe_ftrace(kp); |
1053 | return; | ||
1054 | } | ||
1055 | 1057 | ||
1056 | cpus_read_lock(); | 1058 | cpus_read_lock(); |
1057 | mutex_lock(&text_mutex); | 1059 | mutex_lock(&text_mutex); |
1058 | __disarm_kprobe(kp, reopt); | 1060 | __disarm_kprobe(kp, reopt); |
1059 | mutex_unlock(&text_mutex); | 1061 | mutex_unlock(&text_mutex); |
1060 | cpus_read_unlock(); | 1062 | cpus_read_unlock(); |
1063 | |||
1064 | return 0; | ||
1061 | } | 1065 | } |
1062 | 1066 | ||
1063 | /* | 1067 | /* |
@@ -1639,11 +1643,12 @@ static int aggr_kprobe_disabled(struct kprobe *ap) | |||
1639 | static struct kprobe *__disable_kprobe(struct kprobe *p) | 1643 | static struct kprobe *__disable_kprobe(struct kprobe *p) |
1640 | { | 1644 | { |
1641 | struct kprobe *orig_p; | 1645 | struct kprobe *orig_p; |
1646 | int ret; | ||
1642 | 1647 | ||
1643 | /* Get an original kprobe for return */ | 1648 | /* Get an original kprobe for return */ |
1644 | orig_p = __get_valid_kprobe(p); | 1649 | orig_p = __get_valid_kprobe(p); |
1645 | if (unlikely(orig_p == NULL)) | 1650 | if (unlikely(orig_p == NULL)) |
1646 | return NULL; | 1651 | return ERR_PTR(-EINVAL); |
1647 | 1652 | ||
1648 | if (!kprobe_disabled(p)) { | 1653 | if (!kprobe_disabled(p)) { |
1649 | /* Disable probe if it is a child probe */ | 1654 | /* Disable probe if it is a child probe */ |
@@ -1657,8 +1662,13 @@ static struct kprobe *__disable_kprobe(struct kprobe *p) | |||
1657 | * should have already been disarmed, so | 1662 | * should have already been disarmed, so |
1658 | * skip unneed disarming process. | 1663 | * skip unneed disarming process. |
1659 | */ | 1664 | */ |
1660 | if (!kprobes_all_disarmed) | 1665 | if (!kprobes_all_disarmed) { |
1661 | disarm_kprobe(orig_p, true); | 1666 | ret = disarm_kprobe(orig_p, true); |
1667 | if (ret) { | ||
1668 | p->flags &= ~KPROBE_FLAG_DISABLED; | ||
1669 | return ERR_PTR(ret); | ||
1670 | } | ||
1671 | } | ||
1662 | orig_p->flags |= KPROBE_FLAG_DISABLED; | 1672 | orig_p->flags |= KPROBE_FLAG_DISABLED; |
1663 | } | 1673 | } |
1664 | } | 1674 | } |
@@ -1675,8 +1685,8 @@ static int __unregister_kprobe_top(struct kprobe *p) | |||
1675 | 1685 | ||
1676 | /* Disable kprobe. This will disarm it if needed. */ | 1686 | /* Disable kprobe. This will disarm it if needed. */ |
1677 | ap = __disable_kprobe(p); | 1687 | ap = __disable_kprobe(p); |
1678 | if (ap == NULL) | 1688 | if (IS_ERR(ap)) |
1679 | return -EINVAL; | 1689 | return PTR_ERR(ap); |
1680 | 1690 | ||
1681 | if (ap == p) | 1691 | if (ap == p) |
1682 | /* | 1692 | /* |
@@ -2109,12 +2119,14 @@ static void kill_kprobe(struct kprobe *p) | |||
2109 | int disable_kprobe(struct kprobe *kp) | 2119 | int disable_kprobe(struct kprobe *kp) |
2110 | { | 2120 | { |
2111 | int ret = 0; | 2121 | int ret = 0; |
2122 | struct kprobe *p; | ||
2112 | 2123 | ||
2113 | mutex_lock(&kprobe_mutex); | 2124 | mutex_lock(&kprobe_mutex); |
2114 | 2125 | ||
2115 | /* Disable this kprobe */ | 2126 | /* Disable this kprobe */ |
2116 | if (__disable_kprobe(kp) == NULL) | 2127 | p = __disable_kprobe(kp); |
2117 | ret = -EINVAL; | 2128 | if (IS_ERR(p)) |
2129 | ret = PTR_ERR(p); | ||
2118 | 2130 | ||
2119 | mutex_unlock(&kprobe_mutex); | 2131 | mutex_unlock(&kprobe_mutex); |
2120 | return ret; | 2132 | return ret; |
@@ -2486,34 +2498,50 @@ already_enabled: | |||
2486 | return ret; | 2498 | return ret; |
2487 | } | 2499 | } |
2488 | 2500 | ||
2489 | static void disarm_all_kprobes(void) | 2501 | static int disarm_all_kprobes(void) |
2490 | { | 2502 | { |
2491 | struct hlist_head *head; | 2503 | struct hlist_head *head; |
2492 | struct kprobe *p; | 2504 | struct kprobe *p; |
2493 | unsigned int i; | 2505 | unsigned int i, total = 0, errors = 0; |
2506 | int err, ret = 0; | ||
2494 | 2507 | ||
2495 | mutex_lock(&kprobe_mutex); | 2508 | mutex_lock(&kprobe_mutex); |
2496 | 2509 | ||
2497 | /* If kprobes are already disarmed, just return */ | 2510 | /* If kprobes are already disarmed, just return */ |
2498 | if (kprobes_all_disarmed) { | 2511 | if (kprobes_all_disarmed) { |
2499 | mutex_unlock(&kprobe_mutex); | 2512 | mutex_unlock(&kprobe_mutex); |
2500 | return; | 2513 | return 0; |
2501 | } | 2514 | } |
2502 | 2515 | ||
2503 | kprobes_all_disarmed = true; | 2516 | kprobes_all_disarmed = true; |
2504 | printk(KERN_INFO "Kprobes globally disabled\n"); | ||
2505 | 2517 | ||
2506 | for (i = 0; i < KPROBE_TABLE_SIZE; i++) { | 2518 | for (i = 0; i < KPROBE_TABLE_SIZE; i++) { |
2507 | head = &kprobe_table[i]; | 2519 | head = &kprobe_table[i]; |
2520 | /* Disarm all kprobes on a best-effort basis */ | ||
2508 | hlist_for_each_entry_rcu(p, head, hlist) { | 2521 | hlist_for_each_entry_rcu(p, head, hlist) { |
2509 | if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p)) | 2522 | if (!arch_trampoline_kprobe(p) && !kprobe_disabled(p)) { |
2510 | disarm_kprobe(p, false); | 2523 | err = disarm_kprobe(p, false); |
2524 | if (err) { | ||
2525 | errors++; | ||
2526 | ret = err; | ||
2527 | } | ||
2528 | total++; | ||
2529 | } | ||
2511 | } | 2530 | } |
2512 | } | 2531 | } |
2532 | |||
2533 | if (errors) | ||
2534 | pr_warn("Kprobes globally disabled, but failed to disarm %d out of %d probes\n", | ||
2535 | errors, total); | ||
2536 | else | ||
2537 | pr_info("Kprobes globally disabled\n"); | ||
2538 | |||
2513 | mutex_unlock(&kprobe_mutex); | 2539 | mutex_unlock(&kprobe_mutex); |
2514 | 2540 | ||
2515 | /* Wait for disarming all kprobes by optimizer */ | 2541 | /* Wait for disarming all kprobes by optimizer */ |
2516 | wait_for_kprobe_optimizer(); | 2542 | wait_for_kprobe_optimizer(); |
2543 | |||
2544 | return ret; | ||
2517 | } | 2545 | } |
2518 | 2546 | ||
2519 | /* | 2547 | /* |
@@ -2556,7 +2584,7 @@ static ssize_t write_enabled_file_bool(struct file *file, | |||
2556 | case 'n': | 2584 | case 'n': |
2557 | case 'N': | 2585 | case 'N': |
2558 | case '0': | 2586 | case '0': |
2559 | disarm_all_kprobes(); | 2587 | ret = disarm_all_kprobes(); |
2560 | break; | 2588 | break; |
2561 | default: | 2589 | default: |
2562 | return -EINVAL; | 2590 | return -EINVAL; |