Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754181AbbGPHRE (ORCPT ); Thu, 16 Jul 2015 03:17:04 -0400 Received: from mail4.hitachi.co.jp ([133.145.228.5]:47182 "EHLO mail4.hitachi.co.jp" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752133AbbGPHPL (ORCPT ); Thu, 16 Jul 2015 03:15:11 -0400 X-AuditID: 85900ec0-9c3c7b9000001a57-a1-55a759e3c0b8 Subject: [PATCH tip/master 1/3] kprobes: Support blacklist functions in module From: Masami Hiramatsu To: Ingo Molnar Cc: Pratyush Anand , Ananth N Mavinakayanahalli , Rusty Russell , Linux Kernel Mailing List , Ingo Molnar , Rob Landley , "H. Peter Anvin" , Thomas Gleixner , "David S. Miller" Date: Thu, 16 Jul 2015 16:10:55 +0900 Message-ID: <20150716071055.14218.64129.stgit@localhost.localdomain> In-Reply-To: <20150716071053.14218.82072.stgit@localhost.localdomain> References: <20150716071053.14218.82072.stgit@localhost.localdomain> User-Agent: StGit/0.17.1-dirty MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit X-Brightmail-Tracker: AAAAAA== Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 7591 Lines: 237 To blacklist the functions in a module (e.g. user-defined kprobe handler and the functions invoked from it), expand blacklist support for modules. With this change, users can use NOKPROBE_SYMBOL() macro in their own modules. Signed-off-by: Masami Hiramatsu Cc: Ananth N Mavinakayanahalli Cc: "David S. Miller" Cc: Rob Landley Cc: Rusty Russell --- Documentation/kprobes.txt | 8 ++++++ include/linux/module.h | 4 +++ kernel/kprobes.c | 64 ++++++++++++++++++++++++++++++++++++++------- kernel/module.c | 6 ++++ 4 files changed, 72 insertions(+), 10 deletions(-) diff --git a/Documentation/kprobes.txt b/Documentation/kprobes.txt index 1f9b3e2..b5a698f 100644 --- a/Documentation/kprobes.txt +++ b/Documentation/kprobes.txt @@ -513,6 +513,14 @@ int enable_jprobe(struct jprobe *jp); Enables *probe which has been disabled by disable_*probe(). You must specify the probe which has been registered. +4.9 NOKPROBE_SYMBOL() + +#include +NOKPROBE_SYMBOL(FUNCTION); + +Protects given FUNCTION from other kprobes. This is useful for handler +functions and functions called from the handlers. + 5. Kprobes Features and Limitations Kprobes allows multiple probes at the same address. Currently, diff --git a/include/linux/module.h b/include/linux/module.h index d67b193..0827e34 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -352,6 +352,10 @@ struct module { void __percpu *percpu; unsigned int percpu_size; #endif +#ifdef CONFIG_KPROBES + unsigned int num_kprobe_blacklist; + unsigned long *kprobe_blacklist; +#endif #ifdef CONFIG_TRACEPOINTS unsigned int num_tracepoints; diff --git a/kernel/kprobes.c b/kernel/kprobes.c index c90e417..53951c3 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -88,6 +88,7 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) /* Blacklist -- list of struct kprobe_blacklist_entry */ static LIST_HEAD(kprobe_blacklist); +static DEFINE_MUTEX(kprobe_blacklist_mutex); #ifdef __ARCH_WANT_KPROBES_INSN_SLOT /* @@ -1332,22 +1333,27 @@ bool __weak arch_within_kprobe_blacklist(unsigned long addr) addr < (unsigned long)__kprobes_text_end; } -static bool within_kprobe_blacklist(unsigned long addr) +static struct kprobe_blacklist_entry *find_blacklist_entry(unsigned long addr) { struct kprobe_blacklist_entry *ent; + list_for_each_entry(ent, &kprobe_blacklist, list) { + if (addr >= ent->start_addr && addr < ent->end_addr) + return ent; + } + + return NULL; +} + +static bool within_kprobe_blacklist(unsigned long addr) +{ if (arch_within_kprobe_blacklist(addr)) return true; /* * If there exists a kprobe_blacklist, verify and * fail any probe registration in the prohibited area */ - list_for_each_entry(ent, &kprobe_blacklist, list) { - if (addr >= ent->start_addr && addr < ent->end_addr) - return true; - } - - return false; + return !!find_blacklist_entry(addr); } /* @@ -1437,6 +1443,8 @@ static int check_kprobe_address_safe(struct kprobe *p, ret = arch_check_ftrace_location(p); if (ret) return ret; + + mutex_lock(&kprobe_blacklist_mutex); jump_label_lock(); preempt_disable(); @@ -1474,6 +1482,7 @@ static int check_kprobe_address_safe(struct kprobe *p, out: preempt_enable(); jump_label_unlock(); + mutex_unlock(&kprobe_blacklist_mutex); return ret; } @@ -2054,13 +2063,13 @@ NOKPROBE_SYMBOL(dump_kprobe); * since a kprobe need not necessarily be at the beginning * of a function. */ -static int __init populate_kprobe_blacklist(unsigned long *start, - unsigned long *end) +static int populate_kprobe_blacklist(unsigned long *start, unsigned long *end) { unsigned long *iter; struct kprobe_blacklist_entry *ent; unsigned long entry, offset = 0, size = 0; + mutex_lock(&kprobe_blacklist_mutex); for (iter = start; iter < end; iter++) { entry = arch_deref_entry_point((void *)*iter); @@ -2079,9 +2088,28 @@ static int __init populate_kprobe_blacklist(unsigned long *start, INIT_LIST_HEAD(&ent->list); list_add_tail(&ent->list, &kprobe_blacklist); } + mutex_unlock(&kprobe_blacklist_mutex); + return 0; } +/* Shrink the blacklist */ +static void shrink_kprobe_blacklist(unsigned long *start, unsigned long *end) +{ + struct kprobe_blacklist_entry *ent; + unsigned long *iter; + + mutex_lock(&kprobe_blacklist_mutex); + for (iter = start; iter < end; iter++) { + ent = find_blacklist_entry(*iter); + if (!ent) + continue; + list_del(&ent->list); + kfree(ent); + } + mutex_unlock(&kprobe_blacklist_mutex); +} + /* Module notifier call back, checking kprobes on the module */ static int kprobes_module_callback(struct notifier_block *nb, unsigned long val, void *data) @@ -2092,6 +2120,16 @@ static int kprobes_module_callback(struct notifier_block *nb, unsigned int i; int checkcore = (val == MODULE_STATE_GOING); + /* Add/remove module blacklist */ + if (val == MODULE_STATE_COMING) + populate_kprobe_blacklist(mod->kprobe_blacklist, + mod->kprobe_blacklist + + mod->num_kprobe_blacklist); + else if (val == MODULE_STATE_GOING) + shrink_kprobe_blacklist(mod->kprobe_blacklist, + mod->kprobe_blacklist + + mod->num_kprobe_blacklist); + if (val != MODULE_STATE_GOING && val != MODULE_STATE_LIVE) return NOTIFY_DONE; @@ -2278,6 +2316,7 @@ static const struct file_operations debugfs_kprobes_operations = { /* kprobes/blacklist -- shows which functions can not be probed */ static void *kprobe_blacklist_seq_start(struct seq_file *m, loff_t *pos) { + mutex_lock(&kprobe_blacklist_mutex); return seq_list_start(&kprobe_blacklist, *pos); } @@ -2286,6 +2325,11 @@ static void *kprobe_blacklist_seq_next(struct seq_file *m, void *v, loff_t *pos) return seq_list_next(v, &kprobe_blacklist, pos); } +static void kprobe_blacklist_seq_stop(struct seq_file *m, void *v) +{ + mutex_unlock(&kprobe_blacklist_mutex); +} + static int kprobe_blacklist_seq_show(struct seq_file *m, void *v) { struct kprobe_blacklist_entry *ent = @@ -2299,7 +2343,7 @@ static int kprobe_blacklist_seq_show(struct seq_file *m, void *v) static const struct seq_operations kprobe_blacklist_seq_ops = { .start = kprobe_blacklist_seq_start, .next = kprobe_blacklist_seq_next, - .stop = kprobe_seq_stop, /* Reuse void function */ + .stop = kprobe_blacklist_seq_stop, .show = kprobe_blacklist_seq_show, }; diff --git a/kernel/module.c b/kernel/module.c index 4d2b82e..c3a593c 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -2937,6 +2938,11 @@ static int find_module_sections(struct module *mod, struct load_info *info) sizeof(*mod->ftrace_callsites), &mod->num_ftrace_callsites); #endif +#ifdef CONFIG_KPROBES + mod->kprobe_blacklist = section_objs(info, "_kprobe_blacklist", + sizeof(*mod->kprobe_blacklist), + &mod->num_kprobe_blacklist); +#endif mod->extable = section_objs(info, "__ex_table", sizeof(*mod->extable), &mod->num_exentries); -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/