Set the pages which is used for kprobes' singlestep buffer
and optprobe's trampoline instruction buffer to readonly.
This can prevent unexpected (or unintended) instruction
modification.

This also passes rodata_test as below.

Without this patch, rodata_test shows a warning:

[   10.041310] ------------[ cut here ]------------
[   10.041717] WARNING: CPU: 0 PID: 1 at arch/x86/mm/dump_pagetables.c:235 
note_page+0x7a9/0xa20
[   10.042469] x86/mm: Found insecure W+X mapping at address 
ffffffffa0000000/0xffffffffa0000000
[   10.043073] Modules linked in:
[   10.043317] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G    B           
4.11.0-rc1-00282-gc4bf2f7 #6
[   10.043935] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 
rel-1.10.1-0-g8891697-prebuilt.qemu-project.org 04/01/2014
[   10.044721] Call Trace:
[   10.044898]  dump_stack+0x63/0x8d
[   10.045131]  __warn+0x107/0x130
[   10.045352]  warn_slowpath_fmt+0x92/0xb0
[   10.045623]  ? __warn+0x130/0x130
[   10.045856]  ? 0xffffffffa0000000
[   10.046104]  ? 0xffffffffa0000000
[   10.046337]  note_page+0x7a9/0xa20
[   10.046577]  ptdump_walk_pgd_level_core+0x511/0x540
[   10.046914]  ? note_page+0xa20/0xa20
[   10.047163]  ? do_raw_spin_unlock+0x92/0x120
[   10.047458]  ? 0xffffffffa0000000
[   10.047691]  ? 0xffffffff81000000
[   10.047924]  ptdump_walk_pgd_level_checkwx+0x12/0x20
[   10.048266]  mark_rodata_ro+0x10e/0x120
[   10.048533]  ? rest_init+0xe0/0xe0
[   10.048771]  kernel_init+0x2a/0x120
[   10.049016]  ? rest_init+0xe0/0xe0
[   10.049254]  ret_from_fork+0x2c/0x40
[   10.049503] ---[ end trace 1e4cda1ee3314caa ]---
[   10.049861] x86/mm: Checked W+X mappings: FAILED, 2 W+X pages found.
[   10.050349] rodata_test: all tests were successful

With this fix, no W+X pages found:

[   10.130919] x86/mm: Checked W+X mappings: passed, no W+X pages found.
[   10.131624] rodata_test: all tests were successful

Signed-off-by: Masami Hiramatsu <mhira...@kernel.org>
Reported-by: Andrey Ryabinin <aryabi...@virtuozzo.com>
---
 arch/x86/kernel/kprobes/core.c |    4 ++++
 arch/x86/kernel/kprobes/opt.c  |    3 +++
 2 files changed, 7 insertions(+)

diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
index 3f084a0..0dc24e6 100644
--- a/arch/x86/kernel/kprobes/core.c
+++ b/arch/x86/kernel/kprobes/core.c
@@ -419,6 +419,8 @@ static int arch_copy_kprobe(struct kprobe *p)
 {
        int len;
 
+       set_memory_rw((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
+
        /* Copy an instruction with recovering if other optprobe modifies it.*/
        len = __copy_instruction(p->ainsn.insn, p->addr);
        if (!len)
@@ -430,6 +432,8 @@ static int arch_copy_kprobe(struct kprobe *p)
         */
        prepare_boost(p, len);
 
+       set_memory_ro((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
+
        /* Check whether the instruction modifies Interrupt Flag or not */
        p->ainsn.if_modifier = is_IF_modifier(p->ainsn.insn);
 
diff --git a/arch/x86/kernel/kprobes/opt.c b/arch/x86/kernel/kprobes/opt.c
index 3e7c6e5..b121037 100644
--- a/arch/x86/kernel/kprobes/opt.c
+++ b/arch/x86/kernel/kprobes/opt.c
@@ -350,6 +350,7 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe 
*op,
        }
 
        buf = (u8 *)op->optinsn.insn;
+       set_memory_rw((unsigned long)buf & PAGE_MASK, 1);
 
        /* Copy instructions into the out-of-line buffer */
        ret = copy_optimized_instructions(buf + TMPL_END_IDX, op->kp.addr);
@@ -372,6 +373,8 @@ int arch_prepare_optimized_kprobe(struct optimized_kprobe 
*op,
        synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size,
                           (u8 *)op->kp.addr + op->optinsn.size);
 
+       set_memory_ro((unsigned long)buf & PAGE_MASK, 1);
+
        flush_icache_range((unsigned long) buf,
                           (unsigned long) buf + TMPL_END_IDX +
                           op->optinsn.size + RELATIVEJUMP_SIZE);

Reply via email to