mirror of
https://git.proxmox.com/git/mirror_ubuntu-kernels.git
synced 2026-01-27 15:44:19 +00:00
When arch_prepare_optimized_kprobe calculating jump destination address,
it copies original instructions from jmp-optimized kprobe (see
__recover_optprobed_insn), and calculated based on length of original
instruction.
arch_check_optimized_kprobe does not check KPROBE_FLAG_OPTIMATED when
checking whether jmp-optimized kprobe exists.
As a result, setup_detour_execution may jump to a range that has been
overwritten by jump destination address, resulting in an inval opcode error.
For example, assume that register two kprobes whose addresses are
<func+9> and <func+11> in "func" function.
The original code of "func" function is as follows:
0xffffffff816cb5e9 <+9>: push %r12
0xffffffff816cb5eb <+11>: xor %r12d,%r12d
0xffffffff816cb5ee <+14>: test %rdi,%rdi
0xffffffff816cb5f1 <+17>: setne %r12b
0xffffffff816cb5f5 <+21>: push %rbp
1.Register the kprobe for <func+11>, assume that is kp1, corresponding optimized_kprobe is op1.
After the optimization, "func" code changes to:
0xffffffff816cc079 <+9>: push %r12
0xffffffff816cc07b <+11>: jmp 0xffffffffa0210000
0xffffffff816cc080 <+16>: incl 0xf(%rcx)
0xffffffff816cc083 <+19>: xchg %eax,%ebp
0xffffffff816cc084 <+20>: (bad)
0xffffffff816cc085 <+21>: push %rbp
Now op1->flags == KPROBE_FLAG_OPTIMATED;
2. Register the kprobe for <func+9>, assume that is kp2, corresponding optimized_kprobe is op2.
register_kprobe(kp2)
register_aggr_kprobe
alloc_aggr_kprobe
__prepare_optimized_kprobe
arch_prepare_optimized_kprobe
__recover_optprobed_insn // copy original bytes from kp1->optinsn.copied_insn,
// jump address = <func+14>
3. disable kp1:
disable_kprobe(kp1)
__disable_kprobe
...
if (p == orig_p || aggr_kprobe_disabled(orig_p)) {
ret = disarm_kprobe(orig_p, true) // add op1 in unoptimizing_list, not unoptimized
orig_p->flags |= KPROBE_FLAG_DISABLED; // op1->flags == KPROBE_FLAG_OPTIMATED | KPROBE_FLAG_DISABLED
...
4. unregister kp2
__unregister_kprobe_top
...
if (!kprobe_disabled(ap) && !kprobes_all_disarmed) {
optimize_kprobe(op)
...
if (arch_check_optimized_kprobe(op) < 0) // because op1 has KPROBE_FLAG_DISABLED, here not return
return;
p->kp.flags |= KPROBE_FLAG_OPTIMIZED; // now op2 has KPROBE_FLAG_OPTIMIZED
}
"func" code now is:
0xffffffff816cc079 <+9>: int3
0xffffffff816cc07a <+10>: push %rsp
0xffffffff816cc07b <+11>: jmp 0xffffffffa0210000
0xffffffff816cc080 <+16>: incl 0xf(%rcx)
0xffffffff816cc083 <+19>: xchg %eax,%ebp
0xffffffff816cc084 <+20>: (bad)
0xffffffff816cc085 <+21>: push %rbp
5. if call "func", int3 handler call setup_detour_execution:
if (p->flags & KPROBE_FLAG_OPTIMIZED) {
...
regs->ip = (unsigned long)op->optinsn.insn + TMPL_END_IDX;
...
}
The code for the destination address is
0xffffffffa021072c: push %r12
0xffffffffa021072e: xor %r12d,%r12d
0xffffffffa0210731: jmp 0xffffffff816cb5ee <func+14>
However, <func+14> is not a valid start instruction address. As a result, an error occurs.
Link: https://lore.kernel.org/all/20230216034247.32348-3-yangjihong1@huawei.com/
Fixes:
|
||
|---|---|---|
| .. | ||
| acpi | ||
| apic | ||
| cpu | ||
| fpu | ||
| kprobes | ||
| .gitignore | ||
| alternative.c | ||
| amd_gart_64.c | ||
| amd_nb.c | ||
| aperture_64.c | ||
| apm_32.c | ||
| asm-offsets_32.c | ||
| asm-offsets_64.c | ||
| asm-offsets.c | ||
| audit_64.c | ||
| bootflag.c | ||
| callthunks.c | ||
| cfi.c | ||
| check.c | ||
| cpuid.c | ||
| crash_core_32.c | ||
| crash_core_64.c | ||
| crash_dump_32.c | ||
| crash_dump_64.c | ||
| crash.c | ||
| devicetree.c | ||
| doublefault_32.c | ||
| dumpstack_32.c | ||
| dumpstack_64.c | ||
| dumpstack.c | ||
| e820.c | ||
| early_printk.c | ||
| early-quirks.c | ||
| ebda.c | ||
| eisa.c | ||
| espfix_64.c | ||
| ftrace_32.S | ||
| ftrace_64.S | ||
| ftrace.c | ||
| head32.c | ||
| head64.c | ||
| head_32.S | ||
| head_64.S | ||
| hpet.c | ||
| hw_breakpoint.c | ||
| i8237.c | ||
| i8253.c | ||
| i8259.c | ||
| idt.c | ||
| io_delay.c | ||
| ioport.c | ||
| irq_32.c | ||
| irq_64.c | ||
| irq_work.c | ||
| irq.c | ||
| irqflags.S | ||
| irqinit.c | ||
| itmt.c | ||
| jailhouse.c | ||
| jump_label.c | ||
| kdebugfs.c | ||
| kexec-bzimage64.c | ||
| kgdb.c | ||
| ksysfs.c | ||
| kvm.c | ||
| kvmclock.c | ||
| ldt.c | ||
| machine_kexec_32.c | ||
| machine_kexec_64.c | ||
| Makefile | ||
| mmconf-fam10h_64.c | ||
| module.c | ||
| mpparse.c | ||
| msr.c | ||
| nmi_selftest.c | ||
| nmi.c | ||
| paravirt-spinlocks.c | ||
| paravirt.c | ||
| pci-dma.c | ||
| pcspeaker.c | ||
| perf_regs.c | ||
| platform-quirks.c | ||
| pmem.c | ||
| probe_roms.c | ||
| process_32.c | ||
| process_64.c | ||
| process.c | ||
| process.h | ||
| ptrace.c | ||
| pvclock.c | ||
| quirks.c | ||
| reboot_fixups_32.c | ||
| reboot.c | ||
| relocate_kernel_32.S | ||
| relocate_kernel_64.S | ||
| resource.c | ||
| rethook.c | ||
| rtc.c | ||
| setup_percpu.c | ||
| setup.c | ||
| sev_verify_cbit.S | ||
| sev-shared.c | ||
| sev.c | ||
| signal_32.c | ||
| signal_64.c | ||
| signal_compat.c | ||
| signal.c | ||
| smp.c | ||
| smpboot.c | ||
| stacktrace.c | ||
| static_call.c | ||
| step.c | ||
| sys_ia32.c | ||
| sys_x86_64.c | ||
| tboot.c | ||
| time.c | ||
| tls.c | ||
| tls.h | ||
| topology.c | ||
| trace_clock.c | ||
| trace.c | ||
| tracepoint.c | ||
| traps.c | ||
| tsc_msr.c | ||
| tsc_sync.c | ||
| tsc.c | ||
| umip.c | ||
| unwind_frame.c | ||
| unwind_guess.c | ||
| unwind_orc.c | ||
| uprobes.c | ||
| verify_cpu.S | ||
| vm86_32.c | ||
| vmlinux.lds.S | ||
| vsmp_64.c | ||
| x86_init.c | ||