unsignedlong __recover_optprobed_insn(kprobe_opcode_t *buf, unsignedlong addr)
{ struct optimized_kprobe *op; struct kprobe *kp; long offs; int i;
for (i = 0; i < JMP32_INSN_SIZE; i++) {
kp = get_kprobe((void *)addr - i); /* This function only handles jump-optimized kprobe */ if (kp && kprobe_optimized(kp)) {
op = container_of(kp, struct optimized_kprobe, kp); /* If op is optimized or under unoptimizing */ if (list_empty(&op->list) || optprobe_queued_unopt(op)) goto found;
}
}
return addr;
found: /* * If the kprobe can be optimized, original bytes which can be * overwritten by jump destination address. In this case, original * bytes must be recovered from op->optinsn.copied_insn buffer.
*/ if (copy_from_kernel_nofault(buf, (void *)addr,
MAX_INSN_SIZE * sizeof(kprobe_opcode_t))) return 0UL;
staticvoid synthesize_clac(kprobe_opcode_t *addr)
{ /* * Can't be static_cpu_has() due to how objtool treats this feature bit. * This isn't a fast path anyway.
*/ if (!boot_cpu_has(X86_FEATURE_SMAP)) return;
/* Replace the NOP3 with CLAC */
addr[0] = 0x0f;
addr[1] = 0x01;
addr[2] = 0xca;
}
/* Insert a move instruction which sets a pointer to eax/rdi (1st arg). */ staticvoid synthesize_set_arg1(kprobe_opcode_t *addr, unsignedlong val)
{ #ifdef CONFIG_X86_64
*addr++ = 0x48;
*addr++ = 0xbf; #else
*addr++ = 0xb8; #endif
*(unsignedlong *)addr = val;
}
asm ( ".pushsection .rodata\n" "optprobe_template_func:\n" ".global optprobe_template_entry\n" "optprobe_template_entry:\n" #ifdef CONFIG_X86_64 " pushq $" __stringify(__KERNEL_DS) "\n" /* Save the 'sp - 8', this will be fixed later. */ " pushq %rsp\n" " pushfq\n" ".global optprobe_template_clac\n" "optprobe_template_clac:\n"
ASM_NOP3
SAVE_REGS_STRING " movq %rsp, %rsi\n" ".global optprobe_template_val\n" "optprobe_template_val:\n"
ASM_NOP5
ASM_NOP5 ".global optprobe_template_call\n" "optprobe_template_call:\n"
ASM_NOP5 /* Copy 'regs->flags' into 'regs->ss'. */ " movq 18*8(%rsp), %rdx\n" " movq %rdx, 20*8(%rsp)\n"
RESTORE_REGS_STRING /* Skip 'regs->flags' and 'regs->sp'. */ " addq $16, %rsp\n" /* And pop flags register from 'regs->ss'. */ " popfq\n" #else/* CONFIG_X86_32 */ " pushl %ss\n" /* Save the 'sp - 4', this will be fixed later. */ " pushl %esp\n" " pushfl\n" ".global optprobe_template_clac\n" "optprobe_template_clac:\n"
ASM_NOP3
SAVE_REGS_STRING " movl %esp, %edx\n" ".global optprobe_template_val\n" "optprobe_template_val:\n"
ASM_NOP5 ".global optprobe_template_call\n" "optprobe_template_call:\n"
ASM_NOP5 /* Copy 'regs->flags' into 'regs->ss'. */ " movl 14*4(%esp), %edx\n" " movl %edx, 16*4(%esp)\n"
RESTORE_REGS_STRING /* Skip 'regs->flags' and 'regs->sp'. */ " addl $8, %esp\n" /* And pop flags register from 'regs->ss'. */ " popfl\n" #endif ".global optprobe_template_end\n" "optprobe_template_end:\n" ".popsection\n");
/* Optimized kprobe call back function: called from optinsn */ staticvoid
optimized_callback(struct optimized_kprobe *op, struct pt_regs *regs)
{ /* This is possible if op is under delayed unoptimizing */ if (kprobe_disabled(&op->kp)) return;
/* Decode whole function to ensure any instructions don't jump into target */ staticint can_optimize(unsignedlong paddr)
{ unsignedlong addr, size = 0, offset = 0; struct insn insn;
kprobe_opcode_t buf[MAX_INSN_SIZE];
/* Lookup symbol including addr */ if (!kallsyms_lookup_size_offset(paddr, &size, &offset)) return 0;
/* * Do not optimize in the entry code due to the unstable * stack handling and registers setup.
*/ if (((paddr >= (unsignedlong)__entry_text_start) &&
(paddr < (unsignedlong)__entry_text_end))) return 0;
/* Check there is enough space for a relative jump. */ if (size - offset < JMP32_INSN_SIZE) return 0;
/* Decode instructions */
addr = paddr - offset; while (addr < paddr - offset + size) { /* Decode until function end */ unsignedlong recovered_insn; int ret;
if (search_exception_tables(addr)) /* * Since some fixup code will jumps into this function, * we can't optimize kprobe in this function.
*/ return 0;
recovered_insn = recover_probed_instruction(buf, addr); if (!recovered_insn) return 0;
ret = insn_decode_kernel(&insn, (void *)recovered_insn); if (ret < 0) return 0; #ifdef CONFIG_KGDB /* * If there is a dynamically installed kgdb sw breakpoint, * this function should not be probed.
*/ if (insn.opcode.bytes[0] == INT3_INSN_OPCODE &&
kgdb_has_hit_break(addr)) return 0; #endif /* Recover address */
insn.kaddr = (void *)addr;
insn.next_byte = (void *)(addr + insn.length); /* * Check any instructions don't jump into target, indirectly or * directly. * * The indirect case is present to handle a code with jump * tables. When the kernel uses retpolines, the check should in * theory additionally look for jumps to indirect thunks. * However, the kernel built with retpolines or IBT has jump * tables disabled so the check can be skipped altogether.
*/ if (!IS_ENABLED(CONFIG_MITIGATION_RETPOLINE) &&
!IS_ENABLED(CONFIG_X86_KERNEL_IBT) &&
insn_is_indirect_jump(&insn)) return 0; if (insn_jump_into_range(&insn, paddr + INT3_INSN_SIZE,
DISP32_SIZE)) return 0;
addr += insn.length;
}
return 1;
}
/* Check optimized_kprobe can actually be optimized. */ int arch_check_optimized_kprobe(struct optimized_kprobe *op)
{ int i; struct kprobe *p;
for (i = 1; i < op->optinsn.size; i++) {
p = get_kprobe(op->kp.addr + i); if (p && !kprobe_disarmed(p)) return -EEXIST;
}
return 0;
}
/* Check the addr is within the optimized instructions. */ int arch_within_optimized_kprobe(struct optimized_kprobe *op,
kprobe_opcode_t *addr)
{ return (op->kp.addr <= addr &&
op->kp.addr + op->optinsn.size > addr);
}
/* Free optimized instruction slot */ static void __arch_remove_optimized_kprobe(struct optimized_kprobe *op, int dirty)
{
u8 *slot = op->optinsn.insn; if (slot) { int len = TMPL_END_IDX + op->optinsn.size + JMP32_INSN_SIZE;
/* Record the perf event before freeing the slot */ if (dirty)
perf_event_text_poke(slot, slot, len, NULL, 0);
/* * Copy replacing target instructions * Target instructions MUST be relocatable (checked inside) * This is called when new aggr(opt)probe is allocated or reused.
*/ int arch_prepare_optimized_kprobe(struct optimized_kprobe *op, struct kprobe *__unused)
{
u8 *buf = NULL, *slot; int ret, len; long rel;
if (!can_optimize((unsignedlong)op->kp.addr)) return -EILSEQ;
buf = kzalloc(MAX_OPTINSN_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM;
op->optinsn.insn = slot = get_optinsn_slot(); if (!slot) {
ret = -ENOMEM; goto out;
}
/* * Verify if the address gap is in 2GB range, because this uses * a relative jump.
*/
rel = (long)slot - (long)op->kp.addr + JMP32_INSN_SIZE; if (abs(rel) > 0x7fffffff) {
ret = -ERANGE; goto err;
}
/* Copy arch-dep-instance from template */
memcpy(buf, optprobe_template_entry, TMPL_END_IDX);
/* Copy instructions into the out-of-line buffer */
ret = copy_optimized_instructions(buf + TMPL_END_IDX, op->kp.addr,
slot + TMPL_END_IDX); if (ret < 0) goto err;
op->optinsn.size = ret;
len = TMPL_END_IDX + op->optinsn.size;
synthesize_clac(buf + TMPL_CLAC_IDX);
/* Set probe information */
synthesize_set_arg1(buf + TMPL_MOVE_IDX, (unsignedlong)op);
/* Set probe function call */
synthesize_relcall(buf + TMPL_CALL_IDX,
slot + TMPL_CALL_IDX, optimized_callback);
/* Set returning jmp instruction at the tail of out-of-line buffer */
synthesize_reljump(buf + len, slot + len,
(u8 *)op->kp.addr + op->optinsn.size);
len += JMP32_INSN_SIZE;
/* * Note len = TMPL_END_IDX + op->optinsn.size + JMP32_INSN_SIZE is also * used in __arch_remove_optimized_kprobe().
*/
/* We have to use text_poke() for instruction buffer because it is RO */
perf_event_text_poke(slot, NULL, 0, buf, len);
text_poke(slot, buf, len);
/* * Replace breakpoints (INT3) with relative jumps (JMP.d32). * Caller must call with locking kprobe_mutex and text_mutex. * * The caller will have installed a regular kprobe and after that issued * syncrhonize_rcu_tasks(), this ensures that the instruction(s) that live in * the 4 bytes after the INT3 are unused and can now be overwritten.
*/ void arch_optimize_kprobes(struct list_head *oplist)
{ struct optimized_kprobe *op, *tmp;
u8 insn_buff[JMP32_INSN_SIZE];
/* * Replace a relative jump (JMP.d32) with a breakpoint (INT3). * * After that, we can restore the 4 bytes after the INT3 to undo what * arch_optimize_kprobes() scribbled. This is safe since those bytes will be * unused once the INT3 lands.
*/ void arch_unoptimize_kprobe(struct optimized_kprobe *op)
{
u8 new[JMP32_INSN_SIZE] = { INT3_INSN_OPCODE, };
u8 old[JMP32_INSN_SIZE];
u8 *addr = op->kp.addr;
int setup_detour_execution(struct kprobe *p, struct pt_regs *regs, int reenter)
{ struct optimized_kprobe *op;
if (p->flags & KPROBE_FLAG_OPTIMIZED) { /* This kprobe is really able to run optimized path. */
op = container_of(p, struct optimized_kprobe, kp); /* Detour through copied instructions */
regs->ip = (unsignedlong)op->optinsn.insn + TMPL_END_IDX; if (!reenter)
reset_current_kprobe(); return 1;
} return 0;
}
NOKPROBE_SYMBOL(setup_detour_execution);
Messung V0.5
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.