Skip to content

Commit 63fef14

Browse files
mhiramatIngo Molnar
authored andcommitted
kprobes/x86: Make insn buffer always ROX and use text_poke()
Make insn buffer always ROX and use text_poke() to write the copied instructions instead of set_memory_*(). This makes instruction buffer stronger against other kernel subsystems because there is no window time to modify the buffer. Suggested-by: Ingo Molnar <[email protected]> Signed-off-by: Masami Hiramatsu <[email protected]> Cc: Ananth N Mavinakayanahalli <[email protected]> Cc: Anil S Keshavamurthy <[email protected]> Cc: David S . Miller <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Thomas Gleixner <[email protected]> Link: http://lkml.kernel.org/r/150304463032.17009.14195368040691676813.stgit@devbox Signed-off-by: Ingo Molnar <[email protected]>
1 parent 4f56186 commit 63fef14

File tree

4 files changed

+81
-53
lines changed

4 files changed

+81
-53
lines changed

arch/x86/kernel/kprobes/common.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,11 @@ extern unsigned long recover_probed_instruction(kprobe_opcode_t *buf,
7575
* Copy an instruction and adjust the displacement if the instruction
7676
* uses the %rip-relative addressing mode.
7777
*/
78-
extern int __copy_instruction(u8 *dest, u8 *src, struct insn *insn);
78+
extern int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn);
7979

8080
/* Generate a relative-jump/call instruction */
81-
extern void synthesize_reljump(void *from, void *to);
82-
extern void synthesize_relcall(void *from, void *to);
81+
extern void synthesize_reljump(void *dest, void *from, void *to);
82+
extern void synthesize_relcall(void *dest, void *from, void *to);
8383

8484
#ifdef CONFIG_OPTPROBES
8585
extern int setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter);

arch/x86/kernel/kprobes/core.c

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -119,29 +119,29 @@ struct kretprobe_blackpoint kretprobe_blacklist[] = {
119119
const int kretprobe_blacklist_size = ARRAY_SIZE(kretprobe_blacklist);
120120

121121
static nokprobe_inline void
122-
__synthesize_relative_insn(void *from, void *to, u8 op)
122+
__synthesize_relative_insn(void *dest, void *from, void *to, u8 op)
123123
{
124124
struct __arch_relative_insn {
125125
u8 op;
126126
s32 raddr;
127127
} __packed *insn;
128128

129-
insn = (struct __arch_relative_insn *)from;
129+
insn = (struct __arch_relative_insn *)dest;
130130
insn->raddr = (s32)((long)(to) - ((long)(from) + 5));
131131
insn->op = op;
132132
}
133133

134134
/* Insert a jump instruction at address 'from', which jumps to address 'to'.*/
135-
void synthesize_reljump(void *from, void *to)
135+
void synthesize_reljump(void *dest, void *from, void *to)
136136
{
137-
__synthesize_relative_insn(from, to, RELATIVEJUMP_OPCODE);
137+
__synthesize_relative_insn(dest, from, to, RELATIVEJUMP_OPCODE);
138138
}
139139
NOKPROBE_SYMBOL(synthesize_reljump);
140140

141141
/* Insert a call instruction at address 'from', which calls address 'to'.*/
142-
void synthesize_relcall(void *from, void *to)
142+
void synthesize_relcall(void *dest, void *from, void *to)
143143
{
144-
__synthesize_relative_insn(from, to, RELATIVECALL_OPCODE);
144+
__synthesize_relative_insn(dest, from, to, RELATIVECALL_OPCODE);
145145
}
146146
NOKPROBE_SYMBOL(synthesize_relcall);
147147

@@ -346,10 +346,11 @@ static int is_IF_modifier(kprobe_opcode_t *insn)
346346
/*
347347
* Copy an instruction with recovering modified instruction by kprobes
348348
* and adjust the displacement if the instruction uses the %rip-relative
349-
* addressing mode.
349+
* addressing mode. Note that since @real will be the final place of copied
350+
* instruction, displacement must be adjust by @real, not @dest.
350351
* This returns the length of copied instruction, or 0 if it has an error.
351352
*/
352-
int __copy_instruction(u8 *dest, u8 *src, struct insn *insn)
353+
int __copy_instruction(u8 *dest, u8 *src, u8 *real, struct insn *insn)
353354
{
354355
kprobe_opcode_t buf[MAX_INSN_SIZE];
355356
unsigned long recovered_insn =
@@ -387,11 +388,11 @@ int __copy_instruction(u8 *dest, u8 *src, struct insn *insn)
387388
* have given.
388389
*/
389390
newdisp = (u8 *) src + (s64) insn->displacement.value
390-
- (u8 *) dest;
391+
- (u8 *) real;
391392
if ((s64) (s32) newdisp != newdisp) {
392393
pr_err("Kprobes error: new displacement does not fit into s32 (%llx)\n", newdisp);
393394
pr_err("\tSrc: %p, Dest: %p, old disp: %x\n",
394-
src, dest, insn->displacement.value);
395+
src, real, insn->displacement.value);
395396
return 0;
396397
}
397398
disp = (u8 *) dest + insn_offset_displacement(insn);
@@ -402,20 +403,38 @@ int __copy_instruction(u8 *dest, u8 *src, struct insn *insn)
402403
}
403404

404405
/* Prepare reljump right after instruction to boost */
405-
static void prepare_boost(struct kprobe *p, struct insn *insn)
406+
static int prepare_boost(kprobe_opcode_t *buf, struct kprobe *p,
407+
struct insn *insn)
406408
{
409+
int len = insn->length;
410+
407411
if (can_boost(insn, p->addr) &&
408-
MAX_INSN_SIZE - insn->length >= RELATIVEJUMP_SIZE) {
412+
MAX_INSN_SIZE - len >= RELATIVEJUMP_SIZE) {
409413
/*
410414
* These instructions can be executed directly if it
411415
* jumps back to correct address.
412416
*/
413-
synthesize_reljump(p->ainsn.insn + insn->length,
417+
synthesize_reljump(buf + len, p->ainsn.insn + len,
414418
p->addr + insn->length);
419+
len += RELATIVEJUMP_SIZE;
415420
p->ainsn.boostable = true;
416421
} else {
417422
p->ainsn.boostable = false;
418423
}
424+
425+
return len;
426+
}
427+
428+
/* Make page to RO mode when allocate it */
429+
void *alloc_insn_page(void)
430+
{
431+
void *page;
432+
433+
page = module_alloc(PAGE_SIZE);
434+
if (page)
435+
set_memory_ro((unsigned long)page & PAGE_MASK, 1);
436+
437+
return page;
419438
}
420439

421440
/* Recover page to RW mode before releasing it */
@@ -429,28 +448,28 @@ void free_insn_page(void *page)
429448
static int arch_copy_kprobe(struct kprobe *p)
430449
{
431450
struct insn insn;
451+
kprobe_opcode_t buf[MAX_INSN_SIZE];
432452
int len;
433453

434-
set_memory_rw((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
435-
436454
/* Copy an instruction with recovering if other optprobe modifies it.*/
437-
len = __copy_instruction(p->ainsn.insn, p->addr, &insn);
455+
len = __copy_instruction(buf, p->addr, p->ainsn.insn, &insn);
438456
if (!len)
439457
return -EINVAL;
440458

441459
/*
442460
* __copy_instruction can modify the displacement of the instruction,
443461
* but it doesn't affect boostable check.
444462
*/
445-
prepare_boost(p, &insn);
446-
447-
set_memory_ro((unsigned long)p->ainsn.insn & PAGE_MASK, 1);
463+
len = prepare_boost(buf, p, &insn);
448464

449465
/* Check whether the instruction modifies Interrupt Flag or not */
450-
p->ainsn.if_modifier = is_IF_modifier(p->ainsn.insn);
466+
p->ainsn.if_modifier = is_IF_modifier(buf);
451467

452468
/* Also, displacement change doesn't affect the first byte */
453-
p->opcode = p->ainsn.insn[0];
469+
p->opcode = buf[0];
470+
471+
/* OK, write back the instruction(s) into ROX insn buffer */
472+
text_poke(p->ainsn.insn, buf, len);
454473

455474
return 0;
456475
}

arch/x86/kernel/kprobes/opt.c

Lines changed: 37 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,13 @@ optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
184184
}
185185
NOKPROBE_SYMBOL(optimized_callback);
186186

187-
static int copy_optimized_instructions(u8 *dest, u8 *src)
187+
static int copy_optimized_instructions(u8 *dest, u8 *src, u8 *real)
188188
{
189189
struct insn insn;
190190
int len = 0, ret;
191191

192192
while (len < RELATIVEJUMP_SIZE) {
193-
ret = __copy_instruction(dest + len, src + len, &insn);
193+
ret = __copy_instruction(dest + len, src + len, real, &insn);
194194
if (!ret || !can_boost(&insn, src + len))
195195
return -EINVAL;
196196
len += ret;
@@ -343,57 +343,66 @@ void arch_remove_optimized_kprobe(struct optimized_kprobe *op)
343343
int arch_prepare_optimized_kprobe(struct optimized_kprobe *op,
344344
struct kprobe *__unused)
345345
{
346-
u8 *buf;
347-
int ret;
346+
u8 *buf = NULL, *slot;
347+
int ret, len;
348348
long rel;
349349

350350
if (!can_optimize((unsigned long)op->kp.addr))
351351
return -EILSEQ;
352352

353-
op->optinsn.insn = get_optinsn_slot();
354-
if (!op->optinsn.insn)
353+
buf = kzalloc(MAX_OPTINSN_SIZE, GFP_KERNEL);
354+
if (!buf)
355355
return -ENOMEM;
356356

357+
op->optinsn.insn = slot = get_optinsn_slot();
358+
if (!slot) {
359+
ret = -ENOMEM;
360+
goto out;
361+
}
362+
357363
/*
358364
* Verify if the address gap is in 2GB range, because this uses
359365
* a relative jump.
360366
*/
361-
rel = (long)op->optinsn.insn - (long)op->kp.addr + RELATIVEJUMP_SIZE;
367+
rel = (long)slot - (long)op->kp.addr + RELATIVEJUMP_SIZE;
362368
if (abs(rel) > 0x7fffffff) {
363-
__arch_remove_optimized_kprobe(op, 0);
364-
return -ERANGE;
369+
ret = -ERANGE;
370+
goto err;
365371
}
366372

367-
buf = (u8 *)op->optinsn.insn;
368-
set_memory_rw((unsigned long)buf & PAGE_MASK, 1);
373+
/* Copy arch-dep-instance from template */
374+
memcpy(buf, &optprobe_template_entry, TMPL_END_IDX);
369375

370376
/* Copy instructions into the out-of-line buffer */
371-
ret = copy_optimized_instructions(buf + TMPL_END_IDX, op->kp.addr);
372-
if (ret < 0) {
373-
__arch_remove_optimized_kprobe(op, 0);
374-
return ret;
375-
}
377+
ret = copy_optimized_instructions(buf + TMPL_END_IDX, op->kp.addr,
378+
slot + TMPL_END_IDX);
379+
if (ret < 0)
380+
goto err;
376381
op->optinsn.size = ret;
377-
378-
/* Copy arch-dep-instance from template */
379-
memcpy(buf, &optprobe_template_entry, TMPL_END_IDX);
382+
len = TMPL_END_IDX + op->optinsn.size;
380383

381384
/* Set probe information */
382385
synthesize_set_arg1(buf + TMPL_MOVE_IDX, (unsigned long)op);
383386

384387
/* Set probe function call */
385-
synthesize_relcall(buf + TMPL_CALL_IDX, optimized_callback);
388+
synthesize_relcall(buf + TMPL_CALL_IDX,
389+
slot + TMPL_CALL_IDX, optimized_callback);
386390

387391
/* Set returning jmp instruction at the tail of out-of-line buffer */
388-
synthesize_reljump(buf + TMPL_END_IDX + op->optinsn.size,
392+
synthesize_reljump(buf + len, slot + len,
389393
(u8 *)op->kp.addr + op->optinsn.size);
390-
391-
set_memory_ro((unsigned long)buf & PAGE_MASK, 1);
392-
393-
flush_icache_range((unsigned long) buf,
394-
(unsigned long) buf + TMPL_END_IDX +
395-
op->optinsn.size + RELATIVEJUMP_SIZE);
396-
return 0;
394+
len += RELATIVEJUMP_SIZE;
395+
396+
/* We have to use text_poke for instuction buffer because it is RO */
397+
text_poke(slot, buf, len);
398+
ret = 0;
399+
out:
400+
kfree(buf);
401+
return ret;
402+
403+
err:
404+
__arch_remove_optimized_kprobe(op, 0);
405+
goto out;
397406
}
398407

399408
/*

kernel/kprobes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ enum kprobe_slot_state {
117117
SLOT_USED = 2,
118118
};
119119

120-
static void *alloc_insn_page(void)
120+
void __weak *alloc_insn_page(void)
121121
{
122122
return module_alloc(PAGE_SIZE);
123123
}

0 commit comments

Comments
 (0)