diff options
author | Masami Hiramatsu <mhiramat@kernel.org> | 2019-07-25 02:24:37 -0400 |
---|---|---|
committer | Steven Rostedt (VMware) <rostedt@goodmis.org> | 2019-08-31 12:19:10 -0400 |
commit | 0bc11ed5ab60c135aa764a62c02cd5ea68289de4 (patch) | |
tree | 503be167bf756a0f32c4fbe11da3c41895697049 | |
parent | c68c9ec1c52e5bcd221eb09bc5344ad4f407b204 (diff) |
kprobes: Allow kprobes coexist with livepatch
Allow kprobes which do not modify regs->ip, coexist with livepatch
by dropping FTRACE_OPS_FL_IPMODIFY from ftrace_ops.
User who wants to modify regs->ip (e.g. function fault injection)
must set a dummy post_handler to its kprobes when registering.
However, if such regs->ip modifying kprobes is set on a function,
that function can not be livepatched.
Link: http://lkml.kernel.org/r/156403587671.30117.5233558741694155985.stgit@devnote2
Acked-by: Joe Lawrence <joe.lawrence@redhat.com>
Signed-off-by: Masami Hiramatsu <mhiramat@kernel.org>
Signed-off-by: Steven Rostedt (VMware) <rostedt@goodmis.org>
-rw-r--r-- | kernel/kprobes.c | 56 |
1 files changed, 40 insertions, 16 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index d9770a5393c8..f57deec96ba1 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
@@ -962,8 +962,15 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p) | |||
962 | #ifdef CONFIG_KPROBES_ON_FTRACE | 962 | #ifdef CONFIG_KPROBES_ON_FTRACE |
963 | static struct ftrace_ops kprobe_ftrace_ops __read_mostly = { | 963 | static struct ftrace_ops kprobe_ftrace_ops __read_mostly = { |
964 | .func = kprobe_ftrace_handler, | 964 | .func = kprobe_ftrace_handler, |
965 | .flags = FTRACE_OPS_FL_SAVE_REGS, | ||
966 | }; | ||
967 | |||
968 | static struct ftrace_ops kprobe_ipmodify_ops __read_mostly = { | ||
969 | .func = kprobe_ftrace_handler, | ||
965 | .flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY, | 970 | .flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY, |
966 | }; | 971 | }; |
972 | |||
973 | static int kprobe_ipmodify_enabled; | ||
967 | static int kprobe_ftrace_enabled; | 974 | static int kprobe_ftrace_enabled; |
968 | 975 | ||
969 | /* Must ensure p->addr is really on ftrace */ | 976 | /* Must ensure p->addr is really on ftrace */ |
@@ -976,58 +983,75 @@ static int prepare_kprobe(struct kprobe *p) | |||
976 | } | 983 | } |
977 | 984 | ||
978 | /* Caller must lock kprobe_mutex */ | 985 | /* Caller must lock kprobe_mutex */ |
979 | static int arm_kprobe_ftrace(struct kprobe *p) | 986 | static int __arm_kprobe_ftrace(struct kprobe *p, struct ftrace_ops *ops, |
987 | int *cnt) | ||
980 | { | 988 | { |
981 | int ret = 0; | 989 | int ret = 0; |
982 | 990 | ||
983 | ret = ftrace_set_filter_ip(&kprobe_ftrace_ops, | 991 | ret = ftrace_set_filter_ip(ops, (unsigned long)p->addr, 0, 0); |
984 | (unsigned long)p->addr, 0, 0); | ||
985 | if (ret) { | 992 | if (ret) { |
986 | pr_debug("Failed to arm kprobe-ftrace at %pS (%d)\n", | 993 | pr_debug("Failed to arm kprobe-ftrace at %pS (%d)\n", |
987 | p->addr, ret); | 994 | p->addr, ret); |
988 | return ret; | 995 | return ret; |
989 | } | 996 | } |
990 | 997 | ||
991 | if (kprobe_ftrace_enabled == 0) { | 998 | if (*cnt == 0) { |
992 | ret = register_ftrace_function(&kprobe_ftrace_ops); | 999 | ret = register_ftrace_function(ops); |
993 | if (ret) { | 1000 | if (ret) { |
994 | pr_debug("Failed to init kprobe-ftrace (%d)\n", ret); | 1001 | pr_debug("Failed to init kprobe-ftrace (%d)\n", ret); |
995 | goto err_ftrace; | 1002 | goto err_ftrace; |
996 | } | 1003 | } |
997 | } | 1004 | } |
998 | 1005 | ||
999 | kprobe_ftrace_enabled++; | 1006 | (*cnt)++; |
1000 | return ret; | 1007 | return ret; |
1001 | 1008 | ||
1002 | err_ftrace: | 1009 | err_ftrace: |
1003 | /* | 1010 | /* |
1004 | * Note: Since kprobe_ftrace_ops has IPMODIFY set, and ftrace requires a | 1011 | * At this point, sinec ops is not registered, we should be sefe from |
1005 | * non-empty filter_hash for IPMODIFY ops, we're safe from an accidental | 1012 | * registering empty filter. |
1006 | * empty filter_hash which would undesirably trace all functions. | ||
1007 | */ | 1013 | */ |
1008 | ftrace_set_filter_ip(&kprobe_ftrace_ops, (unsigned long)p->addr, 1, 0); | 1014 | ftrace_set_filter_ip(ops, (unsigned long)p->addr, 1, 0); |
1009 | return ret; | 1015 | return ret; |
1010 | } | 1016 | } |
1011 | 1017 | ||
1018 | static int arm_kprobe_ftrace(struct kprobe *p) | ||
1019 | { | ||
1020 | bool ipmodify = (p->post_handler != NULL); | ||
1021 | |||
1022 | return __arm_kprobe_ftrace(p, | ||
1023 | ipmodify ? &kprobe_ipmodify_ops : &kprobe_ftrace_ops, | ||
1024 | ipmodify ? &kprobe_ipmodify_enabled : &kprobe_ftrace_enabled); | ||
1025 | } | ||
1026 | |||
1012 | /* Caller must lock kprobe_mutex */ | 1027 | /* Caller must lock kprobe_mutex */ |
1013 | static int disarm_kprobe_ftrace(struct kprobe *p) | 1028 | static int __disarm_kprobe_ftrace(struct kprobe *p, struct ftrace_ops *ops, |
1029 | int *cnt) | ||
1014 | { | 1030 | { |
1015 | int ret = 0; | 1031 | int ret = 0; |
1016 | 1032 | ||
1017 | if (kprobe_ftrace_enabled == 1) { | 1033 | if (*cnt == 1) { |
1018 | ret = unregister_ftrace_function(&kprobe_ftrace_ops); | 1034 | ret = unregister_ftrace_function(ops); |
1019 | if (WARN(ret < 0, "Failed to unregister kprobe-ftrace (%d)\n", ret)) | 1035 | if (WARN(ret < 0, "Failed to unregister kprobe-ftrace (%d)\n", ret)) |
1020 | return ret; | 1036 | return ret; |
1021 | } | 1037 | } |
1022 | 1038 | ||
1023 | kprobe_ftrace_enabled--; | 1039 | (*cnt)--; |
1024 | 1040 | ||
1025 | ret = ftrace_set_filter_ip(&kprobe_ftrace_ops, | 1041 | ret = ftrace_set_filter_ip(ops, (unsigned long)p->addr, 1, 0); |
1026 | (unsigned long)p->addr, 1, 0); | ||
1027 | WARN_ONCE(ret < 0, "Failed to disarm kprobe-ftrace at %pS (%d)\n", | 1042 | WARN_ONCE(ret < 0, "Failed to disarm kprobe-ftrace at %pS (%d)\n", |
1028 | p->addr, ret); | 1043 | p->addr, ret); |
1029 | return ret; | 1044 | return ret; |
1030 | } | 1045 | } |
1046 | |||
1047 | static int disarm_kprobe_ftrace(struct kprobe *p) | ||
1048 | { | ||
1049 | bool ipmodify = (p->post_handler != NULL); | ||
1050 | |||
1051 | return __disarm_kprobe_ftrace(p, | ||
1052 | ipmodify ? &kprobe_ipmodify_ops : &kprobe_ftrace_ops, | ||
1053 | ipmodify ? &kprobe_ipmodify_enabled : &kprobe_ftrace_enabled); | ||
1054 | } | ||
1031 | #else /* !CONFIG_KPROBES_ON_FTRACE */ | 1055 | #else /* !CONFIG_KPROBES_ON_FTRACE */ |
1032 | #define prepare_kprobe(p) arch_prepare_kprobe(p) | 1056 | #define prepare_kprobe(p) arch_prepare_kprobe(p) |
1033 | #define arm_kprobe_ftrace(p) (-ENODEV) | 1057 | #define arm_kprobe_ftrace(p) (-ENODEV) |