Skip to content

Commit b46b3d7

Browse files
Masami Hiramatsufweisbec
authored andcommitted
kprobes: Checks probe address is instruction boudary on x86
Ensure safeness of inserting kprobes by checking whether the specified address is at the first byte of an instruction on x86. This is done by decoding probed function from its head to the probe point. Signed-off-by: Masami Hiramatsu <[email protected]> Acked-by: Ananth N Mavinakayanahalli <[email protected]> Cc: Avi Kivity <[email protected]> Cc: Andi Kleen <[email protected]> Cc: Christoph Hellwig <[email protected]> Cc: Frank Ch. Eigler <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Ingo Molnar <[email protected]> Cc: Jason Baron <[email protected]> Cc: Jim Keniston <[email protected]> Cc: K.Prasad <[email protected]> Cc: Lai Jiangshan <[email protected]> Cc: Li Zefan <[email protected]> Cc: Przemysław Pawełczyk <[email protected]> Cc: Roland McGrath <[email protected]> Cc: Sam Ravnborg <[email protected]> Cc: Srikar Dronamraju <[email protected]> Cc: Steven Rostedt <[email protected]> Cc: Tom Zanussi <[email protected]> Cc: Vegard Nossum <[email protected]> LKML-Reference: <[email protected]> Signed-off-by: Frederic Weisbecker <[email protected]>
1 parent ca0e9ba commit b46b3d7

File tree

1 file changed

+73
-0
lines changed

1 file changed

+73
-0
lines changed

arch/x86/kernel/kprobes.c

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,14 @@
4848
#include <linux/preempt.h>
4949
#include <linux/module.h>
5050
#include <linux/kdebug.h>
51+
#include <linux/kallsyms.h>
5152

5253
#include <asm/cacheflush.h>
5354
#include <asm/desc.h>
5455
#include <asm/pgtable.h>
5556
#include <asm/uaccess.h>
5657
#include <asm/alternative.h>
58+
#include <asm/insn.h>
5759

5860
void jprobe_return_end(void);
5961

@@ -244,6 +246,75 @@ static int __kprobes can_boost(kprobe_opcode_t *opcodes)
244246
}
245247
}
246248

249+
/* Recover the probed instruction at addr for further analysis. */
250+
static int recover_probed_instruction(kprobe_opcode_t *buf, unsigned long addr)
251+
{
252+
struct kprobe *kp;
253+
kp = get_kprobe((void *)addr);
254+
if (!kp)
255+
return -EINVAL;
256+
257+
/*
258+
* Basically, kp->ainsn.insn has an original instruction.
259+
* However, RIP-relative instruction can not do single-stepping
260+
* at different place, fix_riprel() tweaks the displacement of
261+
* that instruction. In that case, we can't recover the instruction
262+
* from the kp->ainsn.insn.
263+
*
264+
* On the other hand, kp->opcode has a copy of the first byte of
265+
* the probed instruction, which is overwritten by int3. And
266+
* the instruction at kp->addr is not modified by kprobes except
267+
* for the first byte, we can recover the original instruction
268+
* from it and kp->opcode.
269+
*/
270+
memcpy(buf, kp->addr, MAX_INSN_SIZE * sizeof(kprobe_opcode_t));
271+
buf[0] = kp->opcode;
272+
return 0;
273+
}
274+
275+
/* Dummy buffers for kallsyms_lookup */
276+
static char __dummy_buf[KSYM_NAME_LEN];
277+
278+
/* Check if paddr is at an instruction boundary */
279+
static int __kprobes can_probe(unsigned long paddr)
280+
{
281+
int ret;
282+
unsigned long addr, offset = 0;
283+
struct insn insn;
284+
kprobe_opcode_t buf[MAX_INSN_SIZE];
285+
286+
if (!kallsyms_lookup(paddr, NULL, &offset, NULL, __dummy_buf))
287+
return 0;
288+
289+
/* Decode instructions */
290+
addr = paddr - offset;
291+
while (addr < paddr) {
292+
kernel_insn_init(&insn, (void *)addr);
293+
insn_get_opcode(&insn);
294+
295+
/*
296+
* Check if the instruction has been modified by another
297+
* kprobe, in which case we replace the breakpoint by the
298+
* original instruction in our buffer.
299+
*/
300+
if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) {
301+
ret = recover_probed_instruction(buf, addr);
302+
if (ret)
303+
/*
304+
* Another debugging subsystem might insert
305+
* this breakpoint. In that case, we can't
306+
* recover it.
307+
*/
308+
return 0;
309+
kernel_insn_init(&insn, buf);
310+
}
311+
insn_get_length(&insn);
312+
addr += insn.length;
313+
}
314+
315+
return (addr == paddr);
316+
}
317+
247318
/*
248319
* Returns non-zero if opcode modifies the interrupt flag.
249320
*/
@@ -359,6 +430,8 @@ static void __kprobes arch_copy_kprobe(struct kprobe *p)
359430

360431
int __kprobes arch_prepare_kprobe(struct kprobe *p)
361432
{
433+
if (!can_probe((unsigned long)p->addr))
434+
return -EILSEQ;
362435
/* insn: must be on special executable page on x86. */
363436
p->ainsn.insn = get_insn_slot();
364437
if (!p->ainsn.insn)

0 commit comments

Comments
 (0)