···5858 /* copy of the original instruction */5959 kprobe_opcode_t *insn;6060 /*6161- * boostable = false: This instruction type is not boostable.6262- * boostable = true: This instruction has been boosted: we have6161+ * boostable = 0: This instruction type is not boostable.6262+ * boostable = 1: This instruction has been boosted: we have6363 * added a relative jump after the instruction copy in insn,6464 * so no single-step and fixup are needed (unless there's6565 * a post_handler).6666 */6767- bool boostable;6868- bool if_modifier;6767+ unsigned boostable:1;6868+ unsigned if_modifier:1;6969+ unsigned is_call:1;7070+ unsigned is_pushf:1;7171+ unsigned is_abs_ip:1;6972 /* Number of bytes of text poked */7073 int tp_len;7174};
+74-94
arch/x86/kernel/kprobes/core.c
···133133NOKPROBE_SYMBOL(synthesize_relcall);134134135135/*136136- * Skip the prefixes of the instruction.137137- */138138-static kprobe_opcode_t *skip_prefixes(kprobe_opcode_t *insn)139139-{140140- insn_attr_t attr;141141-142142- attr = inat_get_opcode_attribute((insn_byte_t)*insn);143143- while (inat_is_legacy_prefix(attr)) {144144- insn++;145145- attr = inat_get_opcode_attribute((insn_byte_t)*insn);146146- }147147-#ifdef CONFIG_X86_64148148- if (inat_is_rex_prefix(attr))149149- insn++;150150-#endif151151- return insn;152152-}153153-NOKPROBE_SYMBOL(skip_prefixes);154154-155155-/*156136 * Returns non-zero if INSN is boostable.157137 * RIP relative instructions are adjusted at copying time in 64 bits mode158138 */···292312}293313294314/*295295- * Returns non-zero if opcode modifies the interrupt flag.296296- */297297-static int is_IF_modifier(kprobe_opcode_t *insn)298298-{299299- /* Skip prefixes */300300- insn = skip_prefixes(insn);301301-302302- switch (*insn) {303303- case 0xfa: /* cli */304304- case 0xfb: /* sti */305305- case 0xcf: /* iret/iretd */306306- case 0x9d: /* popf/popfd */307307- return 1;308308- }309309-310310- return 0;311311-}312312-313313-/*314315 * Copy an instruction with recovering modified instruction by kprobes315316 * and adjust the displacement if the instruction uses the %rip-relative316317 * addressing mode. Note that since @real will be the final place of copied···372411 synthesize_reljump(buf + len, p->ainsn.insn + len,373412 p->addr + insn->length);374413 len += JMP32_INSN_SIZE;375375- p->ainsn.boostable = true;414414+ p->ainsn.boostable = 1;376415 } else {377377- p->ainsn.boostable = false;416416+ p->ainsn.boostable = 0;378417 }379418380419 return len;···411450 module_memfree(page);412451}413452453453+static void set_resume_flags(struct kprobe *p, struct insn *insn)454454+{455455+ insn_byte_t opcode = insn->opcode.bytes[0];456456+457457+ switch (opcode) {458458+ case 0xfa: /* cli */459459+ case 0xfb: /* sti */460460+ case 0x9d: /* popf/popfd */461461+ /* Check whether the instruction modifies Interrupt Flag or not */462462+ p->ainsn.if_modifier = 1;463463+ break;464464+ case 0x9c: /* pushfl */465465+ p->ainsn.is_pushf = 1;466466+ break;467467+ case 0xcf: /* iret */468468+ p->ainsn.if_modifier = 1;469469+ fallthrough;470470+ case 0xc2: /* ret/lret */471471+ case 0xc3:472472+ case 0xca:473473+ case 0xcb:474474+ case 0xea: /* jmp absolute -- ip is correct */475475+ /* ip is already adjusted, no more changes required */476476+ p->ainsn.is_abs_ip = 1;477477+ /* Without resume jump, this is boostable */478478+ p->ainsn.boostable = 1;479479+ break;480480+ case 0xe8: /* call relative - Fix return addr */481481+ p->ainsn.is_call = 1;482482+ break;483483+#ifdef CONFIG_X86_32484484+ case 0x9a: /* call absolute -- same as call absolute, indirect */485485+ p->ainsn.is_call = 1;486486+ p->ainsn.is_abs_ip = 1;487487+ break;488488+#endif489489+ case 0xff:490490+ opcode = insn->opcode.bytes[1];491491+ if ((opcode & 0x30) == 0x10) {492492+ /*493493+ * call absolute, indirect494494+ * Fix return addr; ip is correct.495495+ * But this is not boostable496496+ */497497+ p->ainsn.is_call = 1;498498+ p->ainsn.is_abs_ip = 1;499499+ break;500500+ } else if (((opcode & 0x31) == 0x20) ||501501+ ((opcode & 0x31) == 0x21)) {502502+ /*503503+ * jmp near and far, absolute indirect504504+ * ip is correct.505505+ */506506+ p->ainsn.is_abs_ip = 1;507507+ /* Without resume jump, this is boostable */508508+ p->ainsn.boostable = 1;509509+ }510510+ break;511511+ }512512+}513513+414514static int arch_copy_kprobe(struct kprobe *p)415515{416516 struct insn insn;···489467 */490468 len = prepare_boost(buf, p, &insn);491469492492- /* Check whether the instruction modifies Interrupt Flag or not */493493- p->ainsn.if_modifier = is_IF_modifier(buf);470470+ /* Analyze the opcode and set resume flags */471471+ set_resume_flags(p, &insn);494472495473 /* Also, displacement change doesn't affect the first byte */496474 p->opcode = buf[0];···513491514492 if (!can_probe((unsigned long)p->addr))515493 return -EILSEQ;494494+495495+ memset(&p->ainsn, 0, sizeof(p->ainsn));496496+516497 /* insn: must be on special executable page on x86. */517498 p->ainsn.insn = get_insn_slot();518499 if (!p->ainsn.insn)···831806 * 2) If the single-stepped instruction was a call, the return address832807 * that is atop the stack is the address following the copied instruction.833808 * We need to make it the address following the original instruction.834834- *835835- * If this is the first time we've single-stepped the instruction at836836- * this probepoint, and the instruction is boostable, boost it: add a837837- * jump instruction after the copied instruction, that jumps to the next838838- * instruction after the probepoint.839809 */840810static void resume_execution(struct kprobe *p, struct pt_regs *regs,841811 struct kprobe_ctlblk *kcb)···838818 unsigned long *tos = stack_addr(regs);839819 unsigned long copy_ip = (unsigned long)p->ainsn.insn;840820 unsigned long orig_ip = (unsigned long)p->addr;841841- kprobe_opcode_t *insn = p->ainsn.insn;842842-843843- /* Skip prefixes */844844- insn = skip_prefixes(insn);845821846822 regs->flags &= ~X86_EFLAGS_TF;847847- switch (*insn) {848848- case 0x9c: /* pushfl */823823+824824+ /* Fixup the contents of top of stack */825825+ if (p->ainsn.is_pushf) {849826 *tos &= ~(X86_EFLAGS_TF | X86_EFLAGS_IF);850827 *tos |= kcb->kprobe_old_flags;851851- break;852852- case 0xc2: /* iret/ret/lret */853853- case 0xc3:854854- case 0xca:855855- case 0xcb:856856- case 0xcf:857857- case 0xea: /* jmp absolute -- ip is correct */858858- /* ip is already adjusted, no more changes required */859859- p->ainsn.boostable = true;860860- goto no_change;861861- case 0xe8: /* call relative - Fix return addr */828828+ } else if (p->ainsn.is_call) {862829 *tos = orig_ip + (*tos - copy_ip);863863- break;864864-#ifdef CONFIG_X86_32865865- case 0x9a: /* call absolute -- same as call absolute, indirect */866866- *tos = orig_ip + (*tos - copy_ip);867867- goto no_change;868868-#endif869869- case 0xff:870870- if ((insn[1] & 0x30) == 0x10) {871871- /*872872- * call absolute, indirect873873- * Fix return addr; ip is correct.874874- * But this is not boostable875875- */876876- *tos = orig_ip + (*tos - copy_ip);877877- goto no_change;878878- } else if (((insn[1] & 0x31) == 0x20) ||879879- ((insn[1] & 0x31) == 0x21)) {880880- /*881881- * jmp near and far, absolute indirect882882- * ip is correct. And this is boostable883883- */884884- p->ainsn.boostable = true;885885- goto no_change;886886- }887887- break;888888- default:889889- break;890830 }891831892892- regs->ip += orig_ip - copy_ip;832832+ if (!p->ainsn.is_abs_ip)833833+ regs->ip += orig_ip - copy_ip;893834894894-no_change:895835 restore_btf();896836}897837NOKPROBE_SYMBOL(resume_execution);