Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'perf/kprobes' into perf/core, to pick up finished branch

Signed-off-by: Ingo Molnar <mingo@kernel.org>

+81 -98
+7 -4
arch/x86/include/asm/kprobes.h
··· 58 58 /* copy of the original instruction */ 59 59 kprobe_opcode_t *insn; 60 60 /* 61 - * boostable = false: This instruction type is not boostable. 62 - * boostable = true: This instruction has been boosted: we have 61 + * boostable = 0: This instruction type is not boostable. 62 + * boostable = 1: This instruction has been boosted: we have 63 63 * added a relative jump after the instruction copy in insn, 64 64 * so no single-step and fixup are needed (unless there's 65 65 * a post_handler). 66 66 */ 67 - bool boostable; 68 - bool if_modifier; 67 + unsigned boostable:1; 68 + unsigned if_modifier:1; 69 + unsigned is_call:1; 70 + unsigned is_pushf:1; 71 + unsigned is_abs_ip:1; 69 72 /* Number of bytes of text poked */ 70 73 int tp_len; 71 74 };
+74 -94
arch/x86/kernel/kprobes/core.c
··· 133 133 NOKPROBE_SYMBOL(synthesize_relcall); 134 134 135 135 /* 136 - * Skip the prefixes of the instruction. 137 - */ 138 - static kprobe_opcode_t *skip_prefixes(kprobe_opcode_t *insn) 139 - { 140 - insn_attr_t attr; 141 - 142 - attr = inat_get_opcode_attribute((insn_byte_t)*insn); 143 - while (inat_is_legacy_prefix(attr)) { 144 - insn++; 145 - attr = inat_get_opcode_attribute((insn_byte_t)*insn); 146 - } 147 - #ifdef CONFIG_X86_64 148 - if (inat_is_rex_prefix(attr)) 149 - insn++; 150 - #endif 151 - return insn; 152 - } 153 - NOKPROBE_SYMBOL(skip_prefixes); 154 - 155 - /* 156 136 * Returns non-zero if INSN is boostable. 157 137 * RIP relative instructions are adjusted at copying time in 64 bits mode 158 138 */ ··· 292 312 } 293 313 294 314 /* 295 - * Returns non-zero if opcode modifies the interrupt flag. 296 - */ 297 - static int is_IF_modifier(kprobe_opcode_t *insn) 298 - { 299 - /* Skip prefixes */ 300 - insn = skip_prefixes(insn); 301 - 302 - switch (*insn) { 303 - case 0xfa: /* cli */ 304 - case 0xfb: /* sti */ 305 - case 0xcf: /* iret/iretd */ 306 - case 0x9d: /* popf/popfd */ 307 - return 1; 308 - } 309 - 310 - return 0; 311 - } 312 - 313 - /* 314 315 * Copy an instruction with recovering modified instruction by kprobes 315 316 * and adjust the displacement if the instruction uses the %rip-relative 316 317 * addressing mode. Note that since @real will be the final place of copied ··· 372 411 synthesize_reljump(buf + len, p->ainsn.insn + len, 373 412 p->addr + insn->length); 374 413 len += JMP32_INSN_SIZE; 375 - p->ainsn.boostable = true; 414 + p->ainsn.boostable = 1; 376 415 } else { 377 - p->ainsn.boostable = false; 416 + p->ainsn.boostable = 0; 378 417 } 379 418 380 419 return len; ··· 411 450 module_memfree(page); 412 451 } 413 452 453 + static void set_resume_flags(struct kprobe *p, struct insn *insn) 454 + { 455 + insn_byte_t opcode = insn->opcode.bytes[0]; 456 + 457 + switch (opcode) { 458 + case 0xfa: /* cli */ 459 + case 0xfb: /* sti */ 460 + case 0x9d: /* popf/popfd */ 461 + /* Check whether the instruction modifies Interrupt Flag or not */ 462 + p->ainsn.if_modifier = 1; 463 + break; 464 + case 0x9c: /* pushfl */ 465 + p->ainsn.is_pushf = 1; 466 + break; 467 + case 0xcf: /* iret */ 468 + p->ainsn.if_modifier = 1; 469 + fallthrough; 470 + case 0xc2: /* ret/lret */ 471 + case 0xc3: 472 + case 0xca: 473 + case 0xcb: 474 + case 0xea: /* jmp absolute -- ip is correct */ 475 + /* ip is already adjusted, no more changes required */ 476 + p->ainsn.is_abs_ip = 1; 477 + /* Without resume jump, this is boostable */ 478 + p->ainsn.boostable = 1; 479 + break; 480 + case 0xe8: /* call relative - Fix return addr */ 481 + p->ainsn.is_call = 1; 482 + break; 483 + #ifdef CONFIG_X86_32 484 + case 0x9a: /* call absolute -- same as call absolute, indirect */ 485 + p->ainsn.is_call = 1; 486 + p->ainsn.is_abs_ip = 1; 487 + break; 488 + #endif 489 + case 0xff: 490 + opcode = insn->opcode.bytes[1]; 491 + if ((opcode & 0x30) == 0x10) { 492 + /* 493 + * call absolute, indirect 494 + * Fix return addr; ip is correct. 495 + * But this is not boostable 496 + */ 497 + p->ainsn.is_call = 1; 498 + p->ainsn.is_abs_ip = 1; 499 + break; 500 + } else if (((opcode & 0x31) == 0x20) || 501 + ((opcode & 0x31) == 0x21)) { 502 + /* 503 + * jmp near and far, absolute indirect 504 + * ip is correct. 505 + */ 506 + p->ainsn.is_abs_ip = 1; 507 + /* Without resume jump, this is boostable */ 508 + p->ainsn.boostable = 1; 509 + } 510 + break; 511 + } 512 + } 513 + 414 514 static int arch_copy_kprobe(struct kprobe *p) 415 515 { 416 516 struct insn insn; ··· 489 467 */ 490 468 len = prepare_boost(buf, p, &insn); 491 469 492 - /* Check whether the instruction modifies Interrupt Flag or not */ 493 - p->ainsn.if_modifier = is_IF_modifier(buf); 470 + /* Analyze the opcode and set resume flags */ 471 + set_resume_flags(p, &insn); 494 472 495 473 /* Also, displacement change doesn't affect the first byte */ 496 474 p->opcode = buf[0]; ··· 513 491 514 492 if (!can_probe((unsigned long)p->addr)) 515 493 return -EILSEQ; 494 + 495 + memset(&p->ainsn, 0, sizeof(p->ainsn)); 496 + 516 497 /* insn: must be on special executable page on x86. */ 517 498 p->ainsn.insn = get_insn_slot(); 518 499 if (!p->ainsn.insn) ··· 831 806 * 2) If the single-stepped instruction was a call, the return address 832 807 * that is atop the stack is the address following the copied instruction. 833 808 * We need to make it the address following the original instruction. 834 - * 835 - * If this is the first time we've single-stepped the instruction at 836 - * this probepoint, and the instruction is boostable, boost it: add a 837 - * jump instruction after the copied instruction, that jumps to the next 838 - * instruction after the probepoint. 839 809 */ 840 810 static void resume_execution(struct kprobe *p, struct pt_regs *regs, 841 811 struct kprobe_ctlblk *kcb) ··· 838 818 unsigned long *tos = stack_addr(regs); 839 819 unsigned long copy_ip = (unsigned long)p->ainsn.insn; 840 820 unsigned long orig_ip = (unsigned long)p->addr; 841 - kprobe_opcode_t *insn = p->ainsn.insn; 842 - 843 - /* Skip prefixes */ 844 - insn = skip_prefixes(insn); 845 821 846 822 regs->flags &= ~X86_EFLAGS_TF; 847 - switch (*insn) { 848 - case 0x9c: /* pushfl */ 823 + 824 + /* Fixup the contents of top of stack */ 825 + if (p->ainsn.is_pushf) { 849 826 *tos &= ~(X86_EFLAGS_TF | X86_EFLAGS_IF); 850 827 *tos |= kcb->kprobe_old_flags; 851 - break; 852 - case 0xc2: /* iret/ret/lret */ 853 - case 0xc3: 854 - case 0xca: 855 - case 0xcb: 856 - case 0xcf: 857 - case 0xea: /* jmp absolute -- ip is correct */ 858 - /* ip is already adjusted, no more changes required */ 859 - p->ainsn.boostable = true; 860 - goto no_change; 861 - case 0xe8: /* call relative - Fix return addr */ 828 + } else if (p->ainsn.is_call) { 862 829 *tos = orig_ip + (*tos - copy_ip); 863 - break; 864 - #ifdef CONFIG_X86_32 865 - case 0x9a: /* call absolute -- same as call absolute, indirect */ 866 - *tos = orig_ip + (*tos - copy_ip); 867 - goto no_change; 868 - #endif 869 - case 0xff: 870 - if ((insn[1] & 0x30) == 0x10) { 871 - /* 872 - * call absolute, indirect 873 - * Fix return addr; ip is correct. 874 - * But this is not boostable 875 - */ 876 - *tos = orig_ip + (*tos - copy_ip); 877 - goto no_change; 878 - } else if (((insn[1] & 0x31) == 0x20) || 879 - ((insn[1] & 0x31) == 0x21)) { 880 - /* 881 - * jmp near and far, absolute indirect 882 - * ip is correct. And this is boostable 883 - */ 884 - p->ainsn.boostable = true; 885 - goto no_change; 886 - } 887 - break; 888 - default: 889 - break; 890 830 } 891 831 892 - regs->ip += orig_ip - copy_ip; 832 + if (!p->ainsn.is_abs_ip) 833 + regs->ip += orig_ip - copy_ip; 893 834 894 - no_change: 895 835 restore_btf(); 896 836 } 897 837 NOKPROBE_SYMBOL(resume_execution);