diff options
| author | Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | 2014-04-17 04:17:05 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2014-04-24 04:02:56 -0400 |
| commit | 376e242429bf8539ef39a080ac113c8799840b13 (patch) | |
| tree | 33c871f48d37acd167de0b3bf5c902ce4aaa325c /kernel | |
| parent | be8f274323c26ddc7e6fd6c44254b7abcdbe6389 (diff) | |
kprobes: Introduce NOKPROBE_SYMBOL() macro to maintain kprobes blacklist
Introduce NOKPROBE_SYMBOL() macro which builds a kprobes
blacklist at kernel build time.
The usage of this macro is similar to EXPORT_SYMBOL(),
placed after the function definition:
NOKPROBE_SYMBOL(function);
Since this macro will inhibit inlining of static/inline
functions, this patch also introduces a nokprobe_inline macro
for static/inline functions. In this case, we must use
NOKPROBE_SYMBOL() for the inline function caller.
When CONFIG_KPROBES=y, the macro stores the given function
address in the "_kprobe_blacklist" section.
Since the data structures are not fully initialized by the
macro (because there is no "size" information), those
are re-initialized at boot time by using kallsyms.
Signed-off-by: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Link: http://lkml.kernel.org/r/20140417081705.26341.96719.stgit@ltc230.yrl.intra.hitachi.co.jp
Cc: Alok Kataria <akataria@vmware.com>
Cc: Ananth N Mavinakayanahalli <ananth@in.ibm.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Christopher Li <sparse@chrisli.org>
Cc: Chris Wright <chrisw@sous-sol.org>
Cc: David S. Miller <davem@davemloft.net>
Cc: Jan-Simon Möller <dl9pf@gmx.de>
Cc: Jeremy Fitzhardinge <jeremy@goop.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: linux-arch@vger.kernel.org
Cc: linux-doc@vger.kernel.org
Cc: linux-sparse@vger.kernel.org
Cc: virtualization@lists.linux-foundation.org
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/kprobes.c | 100 | ||||
| -rw-r--r-- | kernel/sched/core.c | 1 |
2 files changed, 53 insertions, 48 deletions
diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 5b5ac76671e7..5ffc6875d2a7 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c | |||
| @@ -86,18 +86,8 @@ static raw_spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) | |||
| 86 | return &(kretprobe_table_locks[hash].lock); | 86 | return &(kretprobe_table_locks[hash].lock); |
| 87 | } | 87 | } |
| 88 | 88 | ||
| 89 | /* | 89 | /* Blacklist -- list of struct kprobe_blacklist_entry */ |
| 90 | * Normally, functions that we'd want to prohibit kprobes in, are marked | 90 | static LIST_HEAD(kprobe_blacklist); |
| 91 | * __kprobes. But, there are cases where such functions already belong to | ||
| 92 | * a different section (__sched for preempt_schedule) | ||
| 93 | * | ||
| 94 | * For such cases, we now have a blacklist | ||
| 95 | */ | ||
| 96 | static struct kprobe_blackpoint kprobe_blacklist[] = { | ||
| 97 | {"preempt_schedule",}, | ||
| 98 | {"native_get_debugreg",}, | ||
| 99 | {NULL} /* Terminator */ | ||
| 100 | }; | ||
| 101 | 91 | ||
| 102 | #ifdef __ARCH_WANT_KPROBES_INSN_SLOT | 92 | #ifdef __ARCH_WANT_KPROBES_INSN_SLOT |
| 103 | /* | 93 | /* |
| @@ -1328,24 +1318,22 @@ bool __weak arch_within_kprobe_blacklist(unsigned long addr) | |||
| 1328 | addr < (unsigned long)__kprobes_text_end; | 1318 | addr < (unsigned long)__kprobes_text_end; |
| 1329 | } | 1319 | } |
| 1330 | 1320 | ||
| 1331 | static int __kprobes in_kprobes_functions(unsigned long addr) | 1321 | static bool __kprobes within_kprobe_blacklist(unsigned long addr) |
| 1332 | { | 1322 | { |
| 1333 | struct kprobe_blackpoint *kb; | 1323 | struct kprobe_blacklist_entry *ent; |
| 1334 | 1324 | ||
| 1335 | if (arch_within_kprobe_blacklist(addr)) | 1325 | if (arch_within_kprobe_blacklist(addr)) |
| 1336 | return -EINVAL; | 1326 | return true; |
| 1337 | /* | 1327 | /* |
| 1338 | * If there exists a kprobe_blacklist, verify and | 1328 | * If there exists a kprobe_blacklist, verify and |
| 1339 | * fail any probe registration in the prohibited area | 1329 | * fail any probe registration in the prohibited area |
| 1340 | */ | 1330 | */ |
| 1341 | for (kb = kprobe_blacklist; kb->name != NULL; kb++) { | 1331 | list_for_each_entry(ent, &kprobe_blacklist, list) { |
| 1342 | if (kb->start_addr) { | 1332 | if (addr >= ent->start_addr && addr < ent->end_addr) |
| 1343 | if (addr >= kb->start_addr && | 1333 | return true; |
| 1344 | addr < (kb->start_addr + kb->range)) | ||
| 1345 | return -EINVAL; | ||
| 1346 | } | ||
| 1347 | } | 1334 | } |
| 1348 | return 0; | 1335 | |
| 1336 | return false; | ||
| 1349 | } | 1337 | } |
| 1350 | 1338 | ||
| 1351 | /* | 1339 | /* |
| @@ -1436,7 +1424,7 @@ static __kprobes int check_kprobe_address_safe(struct kprobe *p, | |||
| 1436 | 1424 | ||
| 1437 | /* Ensure it is not in reserved area nor out of text */ | 1425 | /* Ensure it is not in reserved area nor out of text */ |
| 1438 | if (!kernel_text_address((unsigned long) p->addr) || | 1426 | if (!kernel_text_address((unsigned long) p->addr) || |
| 1439 | in_kprobes_functions((unsigned long) p->addr) || | 1427 | within_kprobe_blacklist((unsigned long) p->addr) || |
| 1440 | jump_label_text_reserved(p->addr, p->addr)) { | 1428 | jump_label_text_reserved(p->addr, p->addr)) { |
| 1441 | ret = -EINVAL; | 1429 | ret = -EINVAL; |
| 1442 | goto out; | 1430 | goto out; |
| @@ -2022,6 +2010,38 @@ void __kprobes dump_kprobe(struct kprobe *kp) | |||
| 2022 | kp->symbol_name, kp->addr, kp->offset); | 2010 | kp->symbol_name, kp->addr, kp->offset); |
| 2023 | } | 2011 | } |
| 2024 | 2012 | ||
| 2013 | /* | ||
| 2014 | * Lookup and populate the kprobe_blacklist. | ||
| 2015 | * | ||
| 2016 | * Unlike the kretprobe blacklist, we'll need to determine | ||
| 2017 | * the range of addresses that belong to the said functions, | ||
| 2018 | * since a kprobe need not necessarily be at the beginning | ||
| 2019 | * of a function. | ||
| 2020 | */ | ||
| 2021 | static int __init populate_kprobe_blacklist(unsigned long *start, | ||
| 2022 | unsigned long *end) | ||
| 2023 | { | ||
| 2024 | unsigned long *iter; | ||
| 2025 | struct kprobe_blacklist_entry *ent; | ||
| 2026 | unsigned long offset = 0, size = 0; | ||
| 2027 | |||
| 2028 | for (iter = start; iter < end; iter++) { | ||
| 2029 | if (!kallsyms_lookup_size_offset(*iter, &size, &offset)) { | ||
| 2030 | pr_err("Failed to find blacklist %p\n", (void *)*iter); | ||
| 2031 | continue; | ||
| 2032 | } | ||
| 2033 | |||
| 2034 | ent = kmalloc(sizeof(*ent), GFP_KERNEL); | ||
| 2035 | if (!ent) | ||
| 2036 | return -ENOMEM; | ||
| 2037 | ent->start_addr = *iter; | ||
| 2038 | ent->end_addr = *iter + size; | ||
| 2039 | INIT_LIST_HEAD(&ent->list); | ||
| 2040 | list_add_tail(&ent->list, &kprobe_blacklist); | ||
| 2041 | } | ||
| 2042 | return 0; | ||
| 2043 | } | ||
| 2044 | |||
| 2025 | /* Module notifier call back, checking kprobes on the module */ | 2045 | /* Module notifier call back, checking kprobes on the module */ |
| 2026 | static int __kprobes kprobes_module_callback(struct notifier_block *nb, | 2046 | static int __kprobes kprobes_module_callback(struct notifier_block *nb, |
| 2027 | unsigned long val, void *data) | 2047 | unsigned long val, void *data) |
| @@ -2065,14 +2085,13 @@ static struct notifier_block kprobe_module_nb = { | |||
| 2065 | .priority = 0 | 2085 | .priority = 0 |
| 2066 | }; | 2086 | }; |
| 2067 | 2087 | ||
| 2088 | /* Markers of _kprobe_blacklist section */ | ||
| 2089 | extern unsigned long __start_kprobe_blacklist[]; | ||
| 2090 | extern unsigned long __stop_kprobe_blacklist[]; | ||
| 2091 | |||
| 2068 | static int __init init_kprobes(void) | 2092 | static int __init init_kprobes(void) |
| 2069 | { | 2093 | { |
| 2070 | int i, err = 0; | 2094 | int i, err = 0; |
| 2071 | unsigned long offset = 0, size = 0; | ||
| 2072 | char *modname, namebuf[KSYM_NAME_LEN]; | ||
| 2073 | const char *symbol_name; | ||
| 2074 | void *addr; | ||
| 2075 | struct kprobe_blackpoint *kb; | ||
| 2076 | 2095 | ||
| 2077 | /* FIXME allocate the probe table, currently defined statically */ | 2096 | /* FIXME allocate the probe table, currently defined statically */ |
| 2078 | /* initialize all list heads */ | 2097 | /* initialize all list heads */ |
| @@ -2082,26 +2101,11 @@ static int __init init_kprobes(void) | |||
| 2082 | raw_spin_lock_init(&(kretprobe_table_locks[i].lock)); | 2101 | raw_spin_lock_init(&(kretprobe_table_locks[i].lock)); |
| 2083 | } | 2102 | } |
| 2084 | 2103 | ||
| 2085 | /* | 2104 | err = populate_kprobe_blacklist(__start_kprobe_blacklist, |
| 2086 | * Lookup and populate the kprobe_blacklist. | 2105 | __stop_kprobe_blacklist); |
| 2087 | * | 2106 | if (err) { |
| 2088 | * Unlike the kretprobe blacklist, we'll need to determine | 2107 | pr_err("kprobes: failed to populate blacklist: %d\n", err); |
| 2089 | * the range of addresses that belong to the said functions, | 2108 | pr_err("Please take care of using kprobes.\n"); |
| 2090 | * since a kprobe need not necessarily be at the beginning | ||
| 2091 | * of a function. | ||
| 2092 | */ | ||
| 2093 | for (kb = kprobe_blacklist; kb->name != NULL; kb++) { | ||
| 2094 | kprobe_lookup_name(kb->name, addr); | ||
| 2095 | if (!addr) | ||
| 2096 | continue; | ||
| 2097 | |||
| 2098 | kb->start_addr = (unsigned long)addr; | ||
| 2099 | symbol_name = kallsyms_lookup(kb->start_addr, | ||
| 2100 | &size, &offset, &modname, namebuf); | ||
| 2101 | if (!symbol_name) | ||
| 2102 | kb->range = 0; | ||
| 2103 | else | ||
| 2104 | kb->range = size; | ||
| 2105 | } | 2109 | } |
| 2106 | 2110 | ||
| 2107 | if (kretprobe_blacklist_size) { | 2111 | if (kretprobe_blacklist_size) { |
diff --git a/kernel/sched/core.c b/kernel/sched/core.c index 268a45ea238c..6863631e8cd0 100644 --- a/kernel/sched/core.c +++ b/kernel/sched/core.c | |||
| @@ -2804,6 +2804,7 @@ asmlinkage void __sched notrace preempt_schedule(void) | |||
| 2804 | barrier(); | 2804 | barrier(); |
| 2805 | } while (need_resched()); | 2805 | } while (need_resched()); |
| 2806 | } | 2806 | } |
| 2807 | NOKPROBE_SYMBOL(preempt_schedule); | ||
| 2807 | EXPORT_SYMBOL(preempt_schedule); | 2808 | EXPORT_SYMBOL(preempt_schedule); |
| 2808 | #endif /* CONFIG_PREEMPT */ | 2809 | #endif /* CONFIG_PREEMPT */ |
| 2809 | 2810 | ||
