forked from Minki/linux
s390/kprobes: make insn pages read-only
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>
This commit is contained in:
parent
c3b2c9064e
commit
6c6687a444
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user