struct kretprobe_blackpoint kretprobe_blacklist[] = {
{"__switch_to", }, /* This function switches only current task, but
doesn't switch kernel stack.*/
{NULL, NULL} /* Terminator */
};
/* This movl/addl is used for decoding CFI. */ if (is_cfi_trap(addr + offset)) returnfalse;
}
out: returntrue;
}
/* If x86 supports IBT (ENDBR) it must be skipped. */
kprobe_opcode_t *arch_adjust_kprobe_addr(unsignedlong addr, unsignedlong offset, bool *on_func_entry)
{ if (is_endbr((u32 *)addr)) {
*on_func_entry = !offset || offset == 4; if (*on_func_entry)
offset = 4;
/* This can access kernel text if given address is not recovered */ if (copy_from_kernel_nofault(dest, (void *)recovered_insn,
MAX_INSN_SIZE)) return0;
ret = insn_decode_kernel(insn, dest); if (ret < 0) return0;
/* We can not probe force emulate prefixed instruction */ if (insn_has_emulate_prefix(insn)) return0;
/* Another subsystem puts a breakpoint, failed to recover */ if (insn->opcode.bytes[0] == INT3_INSN_OPCODE) return0;
/* We should not singlestep on the exception masking instructions */ if (insn_masking_exception(insn)) return0;
#ifdef CONFIG_X86_64 /* Only x86_64 has RIP relative instructions */ if (insn_rip_relative(insn)) {
s64 newdisp;
u8 *disp; /* *Thecopiedinstructionusesthe%rip-relativeaddressing *mode.Adjustthedisplacementforthedifferencebetween *theoriginallocationofthisinstructionandthelocation *ofthecopythatwillactuallyberun.Thetrickybithere *ismakingsurethatthesignextensionhappenscorrectlyin *thiscalculation,sinceweneedasigned32-bitresultto *besign-extendedto64bitswhenit'saddedtothe%rip *valueandyieldthesame64-bitresultthatthesign- *extensionoftheoriginalsigned32-bitdisplacementwould *havegiven.
*/
newdisp = (u8 *) src + (s64) insn->displacement.value
- (u8 *) real; if ((s64) (s32) newdisp != newdisp) {
pr_err("Kprobes error: new displacement does not fit into s32 (%llx)\n", newdisp); return0;
}
disp = (u8 *) dest + insn_offset_displacement(insn);
*(s32 *) disp = (s32) newdisp;
} #endif return insn->length;
}
/* Prepare reljump or int3 right after instruction */ staticint prepare_singlestep(kprobe_opcode_t *buf, struct kprobe *p, struct insn *insn)
{ int len = insn->length;
if (!IS_ENABLED(CONFIG_PREEMPTION) &&
!p->post_handler && can_boost(insn, p->addr) &&
MAX_INSN_SIZE - len >= JMP32_INSN_SIZE) { /* *Theseinstructionscanbeexecuteddirectlyifit *jumpsbacktocorrectaddress.
*/
synthesize_reljump(buf + len, p->ainsn.insn + len,
p->addr + insn->length);
len += JMP32_INSN_SIZE;
p->ainsn.boostable = 1;
} else { /* Otherwise, put an int3 for trapping singlestep */ if (MAX_INSN_SIZE - len < INT3_INSN_SIZE) return -ENOSPC;
buf[len] = INT3_INSN_OPCODE;
len += INT3_INSN_SIZE;
}
return len;
}
/* Kprobe x86 instruction emulation - only regs->ip or IF flag modifiers */
if (insn->addr_bytes != sizeof(unsignedlong)) return -EOPNOTSUPP; /* Don't support different size */ if (X86_MODRM_MOD(opcode) != 3) return -EOPNOTSUPP; /* TODO: support memory addressing */
/* Copy an instruction with recovering if other optprobe modifies it.*/
len = __copy_instruction(buf, p->addr, p->ainsn.insn, &insn); if (!len) return -EINVAL;
/* Analyze the opcode and setup emulate functions */
ret = prepare_emulation(p, &insn); if (ret < 0) return ret;
/* Add int3 for single-step or booster jmp */
len = prepare_singlestep(buf, p, &insn); if (len < 0) return len;
/* Also, displacement change doesn't affect the first byte */
p->opcode = buf[0];
staticvoid kprobe_post_process(struct kprobe *cur, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
{ /* Restore back the original saved kprobes variables and continue. */ if (kcb->kprobe_status == KPROBE_REENTER) { /* This will restore both kcb and current_kprobe */
restore_previous_kprobe(kcb);
} else { /* *Alwaysupdatethekcbstatusbecause *reset_curent_kprobe()doesn'tupdatekcb.
*/
kcb->kprobe_status = KPROBE_HIT_SSDONE; if (cur->post_handler)
cur->post_handler(cur, regs, 0);
reset_current_kprobe();
}
}
NOKPROBE_SYMBOL(kprobe_post_process);
staticvoid setup_singlestep(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb, int reenter)
{ if (setup_detour_execution(p, regs, reenter)) return;
#if !defined(CONFIG_PREEMPTION) if (p->ainsn.boostable) { /* Boost up -- we can execute copied instructions directly */ if (!reenter)
reset_current_kprobe(); /* *Reenteringboostedprobedoesn'tresetcurrent_kprobe, *norsetcurrent_kprobe,becauseitdoesn'tusesingle *stepping.
*/
regs->ip = (unsignedlong)p->ainsn.insn; return;
} #endif if (reenter) {
save_previous_kprobe(kcb);
set_current_kprobe(p, regs, kcb);
kcb->kprobe_status = KPROBE_REENTER;
} else
kcb->kprobe_status = KPROBE_HIT_SS;
if (p->ainsn.emulate_op) {
p->ainsn.emulate_op(p, regs);
kprobe_post_process(p, regs, kcb); return;
}
/* Disable interrupt, and set ip register on trampoline */
regs->flags &= ~X86_EFLAGS_IF;
regs->ip = (unsignedlong)p->ainsn.insn;
}
NOKPROBE_SYMBOL(setup_singlestep);
/* Restore saved interrupt flag and ip register */
regs->flags |= kcb->kprobe_saved_flags; /* Note that regs->ip is executed int3 so must be a step back */
regs->ip += (orig_ip - copy_ip) - INT3_INSN_SIZE;
}
NOKPROBE_SYMBOL(resume_singlestep);
/* *Wehavereenteredthekprobe_handler(),sinceanotherprobewashitwhile *withinthehandler.Wesavetheoriginalkprobesvariablesandjustsingle *stepontheinstructionofthenewprobewithoutcallinganyuserhandlers.
*/ staticint reenter_kprobe(struct kprobe *p, struct pt_regs *regs, struct kprobe_ctlblk *kcb)
{ switch (kcb->kprobe_status) { case KPROBE_HIT_SSDONE: case KPROBE_HIT_ACTIVE: case KPROBE_HIT_SS:
kprobes_inc_nmissed_count(p);
setup_singlestep(p, regs, kcb, 1); break; case KPROBE_REENTER: /* A probe has been hit in the codepath leading up to, or just *after,single-steppingofaprobedinstruction.Thisentire *codepathshouldstrictlyresidein.kprobes.textsection. *RaiseaBUGorwe'llcontinueinanendlessreenteringloop *andeventuallyastackoverflow.
*/
pr_err("Unrecoverable kprobe detected.\n");
dump_kprobe(p);
BUG(); default: /* impossible cases */
WARN_ON(1); return0;
}
if (p) { if (kprobe_running()) { if (reenter_kprobe(p, regs, kcb)) return1;
} else {
set_current_kprobe(p, regs, kcb);
kcb->kprobe_status = KPROBE_HIT_ACTIVE;
/* *Ifwehavenopre-handleroritreturned0,we *continuewithnormalprocessing.Ifwehavea *pre-handleranditreturnednon-zero,thatmeans *userhandlersetupregisterstoexittoanother *instruction,wemustskipthesinglestepping.
*/ if (!p->pre_handler || !p->pre_handler(p, regs))
setup_singlestep(p, regs, kcb, 0); else
reset_current_kprobe(); return1;
}
} elseif (kprobe_is_ss(kcb)) {
p = kprobe_running(); if ((unsignedlong)p->ainsn.insn < regs->ip &&
(unsignedlong)p->ainsn.insn + MAX_INSN_SIZE > regs->ip) { /* Most provably this is the second int3 for singlestep */
resume_singlestep(p, regs, kcb);
kprobe_post_process(p, regs, kcb); return1;
}
} /* else: not a kprobe fault; let the kernel handle it */
return0;
}
NOKPROBE_SYMBOL(kprobe_int3_handler);
int kprobe_fault_handler(struct pt_regs *regs, int trapnr)
{ struct kprobe *cur = kprobe_running(); struct kprobe_ctlblk *kcb = get_kprobe_ctlblk();
if (unlikely(regs->ip == (unsignedlong)cur->ainsn.insn)) { /* This must happen on single-stepping */
WARN_ON(kcb->kprobe_status != KPROBE_HIT_SS &&
kcb->kprobe_status != KPROBE_REENTER); /* *Weareherebecausetheinstructionbeingsingle *steppedcausedapagefault.Weresetthecurrent *kprobeandtheippointsbacktotheprobeaddress *andallowthepagefaulthandlertocontinueasa *normalpagefault.
*/
regs->ip = (unsignedlong)cur->addr;
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.