s390/kprobes: make insn pages read-only
authorHeiko Carstens <hca@linux.ibm.com>
Thu, 10 Sep 2020 14:48:35 +0000 (16:48 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Mon, 14 Sep 2020 09:38:35 +0000 (11:38 +0200)
Make sure that kprobe insn pages are not writable anymore.

Tested-by: Vasily Gorbik <gor@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/kernel/kprobes.c

index d2a71d8..b34fa4e 100644 (file)
@@ -7,6 +7,7 @@
  * s390 port, used ppc64 as template. Mike Grundy <grundym@us.ibm.com>
  */
 
+#include <linux/moduleloader.h>
 #include <linux/kprobes.h>
 #include <linux/ptrace.h>
 #include <linux/preempt.h>
@@ -32,17 +33,33 @@ 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)
+{
+       void *page;
+
+       page = module_alloc(PAGE_SIZE);
+       if (!page)
+               return NULL;
+       __set_memory((unsigned long) page, 1, SET_MEMORY_RO | SET_MEMORY_X);
+       return page;
+}
+
+void free_insn_page(void *page)
+{
+       module_memfree(page);
+}
+
 static void *alloc_s390_insn_page(void)
 {
        if (xchg(&insn_page_in_use, 1) == 1)
                return NULL;
-       set_memory_x((unsigned long) &insn_page, 1);
+       __set_memory((unsigned long) &insn_page, 1, SET_MEMORY_RO | SET_MEMORY_X);
        return &insn_page;
 }
 
 static void free_s390_insn_page(void *page)
 {
-       set_memory_nx((unsigned long) page, 1);
+       __set_memory((unsigned long) page, 1, SET_MEMORY_RW | SET_MEMORY_NX);
        xchg(&insn_page_in_use, 0);
 }
 
@@ -56,25 +73,29 @@ struct kprobe_insn_cache kprobe_s390_insn_slots = {
 
 static void copy_instruction(struct kprobe *p)
 {
+       kprobe_opcode_t insn[MAX_INSN_SIZE];
        s64 disp, new_disp;
        u64 addr, new_addr;
+       unsigned int len;
 
-       memcpy(p->ainsn.insn, p->addr, insn_length(*p->addr >> 8));
-       p->opcode = p->ainsn.insn[0];
-       if (!probe_is_insn_relative_long(p->ainsn.insn))
-               return;
-       /*
-        * For pc-relative instructions in RIL-b or RIL-c format patch the
-        * RI2 displacement field. We have already made sure that the insn
-        * slot for the patched instruction is within the same 2GB area
-        * as the original instruction (either kernel image or module area).
-        * Therefore the new displacement will always fit.
-        */
-       disp = *(s32 *)&p->ainsn.insn[1];
-       addr = (u64)(unsigned long)p->addr;
-       new_addr = (u64)(unsigned long)p->ainsn.insn;
-       new_disp = ((addr + (disp * 2)) - new_addr) / 2;
-       *(s32 *)&p->ainsn.insn[1] = new_disp;
+       len = insn_length(*p->addr >> 8);
+       memcpy(&insn, p->addr, len);
+       p->opcode = insn[0];
+       if (probe_is_insn_relative_long(&insn[0])) {
+               /*
+                * For pc-relative instructions in RIL-b or RIL-c format patch
+                * the RI2 displacement field. We have already made sure that
+                * the insn slot for the patched instruction is within the same
+                * 2GB area as the original instruction (either kernel image or
+                * module area). Therefore the new displacement will always fit.
+                */
+               disp = *(s32 *)&insn[1];
+               addr = (u64)(unsigned long)p->addr;
+               new_addr = (u64)(unsigned long)p->ainsn.insn;
+               new_disp = ((addr + (disp * 2)) - new_addr) / 2;
+               *(s32 *)&insn[1] = new_disp;
+       }
+       s390_kernel_write(p->ainsn.insn, &insn, len);
 }
 NOKPROBE_SYMBOL(copy_instruction);