[patch 35/38] kprobes: Add callthunk blacklisting

Thomas Gleixner
2022-07-16T23:19:57Z

From: Peter Zijlstra <peterz@infradead.org>

Callthunks are not safe for probing. Add them to the kprobes black listed
areas.

Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
---
arch/x86/kernel/callthunks.c | 5 ++++
kernel/kprobes.c | 52 +++++++++++++++++++++++++++----------------
2 files changed, 38 insertions(+), 19 deletions(-)

--- a/arch/x86/kernel/callthunks.c
+++ b/arch/x86/kernel/callthunks.c
@@ -6,6 +6,7 @@
#include <linux/debugfs.h>
#include <linux/kallsyms.h>
#include <linux/memory.h>
+#include <linux/kprobes.h>
#include <linux/moduleloader.h>
#include <linux/set_memory.h>
#include <linux/static_call.h>
@@ -476,6 +477,7 @@ static __init_or_module int callthunks_s

static __init noinline void callthunks_init(struct callthunk_sites *cs)
{
+ unsigned long base, size;
int ret;

if (cpu_feature_enabled(X86_FEATURE_CALL_DEPTH)) {
@@ -494,6 +496,9 @@ static __init noinline void callthunks_i
if (WARN_ON_ONCE(ret))
return;

+ base = (unsigned long)builtin_layout.base;
+ size = builtin_layout.size;
+ kprobe_add_area_blacklist(base, base + size);
static_call_force_reinit();
thunks_initialized = true;
}
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -2439,40 +2439,38 @@ void dump_kprobe(struct kprobe *kp)
}
NOKPROBE_SYMBOL(dump_kprobe);

-int kprobe_add_ksym_blacklist(unsigned long entry)
+static int __kprobe_add_ksym_blacklist(unsigned long start, unsigned long end)
{
struct kprobe_blacklist_entry *ent;
- unsigned long offset = 0, size = 0;
-
- if (!kernel_text_address(entry) ||
- !kallsyms_lookup_size_offset(entry, &size, &offset))
- return -EINVAL;

ent = kmalloc(sizeof(*ent), GFP_KERNEL);
if (!ent)
return -ENOMEM;
- ent->start_addr = entry;
- ent->end_addr = entry + size;
+ ent->start_addr = start;
+ ent->end_addr = end;
INIT_LIST_HEAD(&ent->list);
list_add_tail(&ent->list, &kprobe_blacklist);

- return (int)size;
+ return (int)(end - start);
+}
+
+int kprobe_add_ksym_blacklist(unsigned long entry)
+{
+ unsigned long offset = 0, size = 0;
+
+ if (!kernel_text_address(entry) ||
+ !kallsyms_lookup_size_offset(entry, &size, &offset))
+ return -EINVAL;
+
+ return __kprobe_add_ksym_blacklist(entry, entry + size);
}

/* Add all symbols in given area into kprobe blacklist */
int kprobe_add_area_blacklist(unsigned long start, unsigned long end)
{
- unsigned long entry;
- int ret = 0;
+ int ret = __kprobe_add_ksym_blacklist(start, end);

- for (entry = start; entry < end; entry += ret) {
- ret = kprobe_add_ksym_blacklist(entry);
- if (ret < 0)
- return ret;
- if (ret == 0) /* In case of alias symbol */
- ret = 1;
- }
- return 0;
+ return ret < 0 ? ret : 0;
}

/* Remove all symbols in given area from kprobe blacklist */
@@ -2578,6 +2576,14 @@ static void add_module_kprobe_blacklist(
end = start + mod->noinstr_text_size;
kprobe_add_area_blacklist(start, end);
}
+
+#ifdef CONFIG_CALL_THUNKS
+ start = (unsigned long)mod->thunk_layout.base;
+ if (start) {
+ end = start + mod->thunk_layout.size;
+ kprobe_remove_area_blacklist(start, end);
+ }
+#endif
}

static void remove_module_kprobe_blacklist(struct module *mod)
@@ -2601,6 +2607,14 @@ static void remove_module_kprobe_blackli
end = start + mod->noinstr_text_size;
kprobe_remove_area_blacklist(start, end);
}
+
+#ifdef CONFIG_CALL_THUNKS
+ start = (unsigned long)mod->thunk_layout.base;
+ if (start) {
+ end = start + mod->thunk_layout.size;
+ kprobe_remove_area_blacklist(start, end);
+ }
+#endif
}

/* Module notifier call back, checking kprobes on the module */