s390/kprobes: move insn_page to text segment
authorHeiko Carstens <hca@linux.ibm.com>
Fri, 18 Sep 2020 08:26:19 +0000 (10:26 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Fri, 9 Oct 2020 21:45:30 +0000 (23:45 +0200)
Move the in-kernel kprobes insn page to text segment. Rationale:
having that page in rw data segment is suboptimal, since as soon as a
kprobe is set, this will split the 1:1 kernel mapping for a single
page which get new permissions.

Note: there is always at least one kprobe present for the kretprobe
trampoline; so the mapping will always be split into smaller 4k
mappings because of this.

Moving the kprobes insn page into text segment makes sure that the
page is mapped RO/X in any case, and avoids that the 1:1 mapping is
split.

The kprobe insn_page is defined as a dummy function which is filled
with "br %r14" instructions.

Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/kernel/Makefile
arch/s390/kernel/entry.h
arch/s390/kernel/kprobes.c
arch/s390/kernel/kprobes_insn_page.S [new file with mode: 0644]

index efca709..dd73b7f 100644 (file)
@@ -57,6 +57,7 @@ obj-$(CONFIG_COMPAT)          += $(compat-obj-y)
 obj-$(CONFIG_EARLY_PRINTK)     += early_printk.o
 obj-$(CONFIG_STACKTRACE)       += stacktrace.o
 obj-$(CONFIG_KPROBES)          += kprobes.o
+obj-$(CONFIG_KPROBES)          += kprobes_insn_page.o
 obj-$(CONFIG_FUNCTION_TRACER)  += mcount.o ftrace.o
 obj-$(CONFIG_CRASH_DUMP)       += crash_dump.o
 obj-$(CONFIG_UPROBES)          += uprobes.o
index 6475a88..0f7e4e9 100644 (file)
@@ -87,4 +87,6 @@ void set_fs_fixup(void);
 unsigned long stack_alloc(void);
 void stack_free(unsigned long stack);
 
+extern char kprobes_insn_page[];
+
 #endif /* _ENTRY_H */
index b34fa4e..6574774 100644 (file)
@@ -22,6 +22,7 @@
 #include <asm/set_memory.h>
 #include <asm/sections.h>
 #include <asm/dis.h>
+#include "entry.h"
 
 DEFINE_PER_CPU(struct kprobe *, current_kprobe);
 DEFINE_PER_CPU(struct kprobe_ctlblk, kprobe_ctlblk);
@@ -31,7 +32,6 @@ struct kretprobe_blackpoint kretprobe_blacklist[] = { };
 DEFINE_INSN_CACHE_OPS(s390_insn);
 
 static int insn_page_in_use;
-static char insn_page[PAGE_SIZE] __aligned(PAGE_SIZE);
 
 void *alloc_insn_page(void)
 {
@@ -53,13 +53,11 @@ static void *alloc_s390_insn_page(void)
 {
        if (xchg(&insn_page_in_use, 1) == 1)
                return NULL;
-       __set_memory((unsigned long) &insn_page, 1, SET_MEMORY_RO | SET_MEMORY_X);
-       return &insn_page;
+       return &kprobes_insn_page;
 }
 
 static void free_s390_insn_page(void *page)
 {
-       __set_memory((unsigned long) page, 1, SET_MEMORY_RW | SET_MEMORY_NX);
        xchg(&insn_page_in_use, 0);
 }
 
diff --git a/arch/s390/kernel/kprobes_insn_page.S b/arch/s390/kernel/kprobes_insn_page.S
new file mode 100644 (file)
index 0000000..f6cb022
--- /dev/null
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/linkage.h>
+
+/*
+ * insn_page is a special 4k aligned dummy function for kprobes.
+ * It will contain all kprobed instructions that are out-of-line executed.
+ * The page must be within the kernel image to guarantee that the
+ * out-of-line instructions are within 2GB distance of their original
+ * location. Using a dummy function ensures that the insn_page is within
+ * the text section of the kernel and mapped read-only/executable from
+ * the beginning on, thus avoiding to split large mappings if the page
+ * would be in the data section instead.
+ */
+       .section .kprobes.text, "ax"
+       .align 4096
+ENTRY(kprobes_insn_page)
+       .rept 2048
+       .word 0x07fe
+       .endr
+ENDPROC(kprobes_insn_page)
+       .previous