Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755753AbbGPLfL (ORCPT ); Thu, 16 Jul 2015 07:35:11 -0400 Received: from ozlabs.org ([103.22.144.67]:43506 "EHLO ozlabs.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1755730AbbGPLfI (ORCPT ); Thu, 16 Jul 2015 07:35:08 -0400 From: Rusty Russell To: Masami Hiramatsu , Ingo Molnar Cc: Pratyush Anand , Ananth N Mavinakayanahalli , Linux Kernel Mailing List , Ingo Molnar , Rob Landley , "H. Peter Anvin" , Thomas Gleixner , "David S. Miller" Subject: Re: [PATCH tip/master 1/3] kprobes: Support blacklist functions in module In-Reply-To: <20150716071055.14218.64129.stgit@localhost.localdomain> References: <20150716071053.14218.82072.stgit@localhost.localdomain> <20150716071055.14218.64129.stgit@localhost.localdomain> User-Agent: Notmuch/0.17 (http://notmuchmail.org) Emacs/24.4.1 (x86_64-pc-linux-gnu) Date: Thu, 16 Jul 2015 21:04:29 +0930 Message-ID: <87k2u0mj3u.fsf@rustcorp.com.au> MIME-Version: 1.0 Content-Type: text/plain Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 8436 Lines: 247 Masami Hiramatsu writes: > 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. Looks great, thanks! Acked-by: Rusty Russell > 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/ -- 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/