Commit 6c6687a4 authored by Heiko Carstens's avatar Heiko Carstens Committed by Vasily Gorbik

s390/kprobes: make insn pages read-only

Make sure that kprobe insn pages are not writable anymore.
Tested-by: default avatarVasily Gorbik <gor@linux.ibm.com>
Signed-off-by: default avatarHeiko Carstens <hca@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent c3b2c906
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
* s390 port, used ppc64 as template. Mike Grundy <grundym@us.ibm.com> * s390 port, used ppc64 as template. Mike Grundy <grundym@us.ibm.com>
*/ */
#include <linux/moduleloader.h>
#include <linux/kprobes.h> #include <linux/kprobes.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
#include <linux/preempt.h> #include <linux/preempt.h>
...@@ -32,17 +33,33 @@ DEFINE_INSN_CACHE_OPS(s390_insn); ...@@ -32,17 +33,33 @@ DEFINE_INSN_CACHE_OPS(s390_insn);
static int insn_page_in_use; static int insn_page_in_use;
static char insn_page[PAGE_SIZE] __aligned(PAGE_SIZE); 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) static void *alloc_s390_insn_page(void)
{ {
if (xchg(&insn_page_in_use, 1) == 1) if (xchg(&insn_page_in_use, 1) == 1)
return NULL; 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; return &insn_page;
} }
static void free_s390_insn_page(void *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); xchg(&insn_page_in_use, 0);
} }
...@@ -56,25 +73,29 @@ struct kprobe_insn_cache kprobe_s390_insn_slots = { ...@@ -56,25 +73,29 @@ struct kprobe_insn_cache kprobe_s390_insn_slots = {
static void copy_instruction(struct kprobe *p) static void copy_instruction(struct kprobe *p)
{ {
kprobe_opcode_t insn[MAX_INSN_SIZE];
s64 disp, new_disp; s64 disp, new_disp;
u64 addr, new_addr; u64 addr, new_addr;
unsigned int len;
memcpy(p->ainsn.insn, p->addr, insn_length(*p->addr >> 8)); len = insn_length(*p->addr >> 8);
p->opcode = p->ainsn.insn[0]; memcpy(&insn, p->addr, len);
if (!probe_is_insn_relative_long(p->ainsn.insn)) p->opcode = insn[0];
return; if (probe_is_insn_relative_long(&insn[0])) {
/* /*
* For pc-relative instructions in RIL-b or RIL-c format patch the * For pc-relative instructions in RIL-b or RIL-c format patch
* RI2 displacement field. We have already made sure that the insn * the RI2 displacement field. We have already made sure that
* slot for the patched instruction is within the same 2GB area * the insn slot for the patched instruction is within the same
* as the original instruction (either kernel image or module area). * 2GB area as the original instruction (either kernel image or
* Therefore the new displacement will always fit. * module area). Therefore the new displacement will always fit.
*/ */
disp = *(s32 *)&p->ainsn.insn[1]; disp = *(s32 *)&insn[1];
addr = (u64)(unsigned long)p->addr; addr = (u64)(unsigned long)p->addr;
new_addr = (u64)(unsigned long)p->ainsn.insn; new_addr = (u64)(unsigned long)p->ainsn.insn;
new_disp = ((addr + (disp * 2)) - new_addr) / 2; new_disp = ((addr + (disp * 2)) - new_addr) / 2;
*(s32 *)&p->ainsn.insn[1] = new_disp; *(s32 *)&insn[1] = new_disp;
}
s390_kernel_write(p->ainsn.insn, &insn, len);
} }
NOKPROBE_SYMBOL(copy_instruction); NOKPROBE_SYMBOL(copy_instruction);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment