/* Check if paddr is at an instruction boundary */ static int __kprobes can_probe(unsigned long paddr) { unsigned long addr, __addr, offset = 0; struct insn insn; kprobe_opcode_t buf[MAX_INSN_SIZE]; if (!kallsyms_lookup_size_offset(paddr, NULL, &offset)) return 0; /* Decode instructions */ addr = paddr - offset; while (addr < paddr) { /* * Check if the instruction has been modified by another * kprobe, in which case we replace the breakpoint by the * original instruction in our buffer. * Also, jump optimization will change the breakpoint to * relative-jump. Since the relative-jump itself is * normally used, we just go through if there is no kprobe. */ __addr = recover_probed_instruction(buf, addr); kernel_insn_init(&insn, (void *)__addr); insn_get_length(&insn); /* * Another debugging subsystem might insert this breakpoint. * In that case, we can't recover it. */ if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) return 0; addr += insn.length; } return (addr == paddr); }
enum kprobe_insn __kprobes arm_kprobe_decode_insn(kprobe_opcode_t *addr, struct arch_specific_insn *asi) { enum kprobe_insn decoded; kprobe_opcode_t insn = le32_to_cpu(*addr); kprobe_opcode_t *scan_end = NULL; unsigned long size = 0, offset = 0; /* * If there's a symbol defined in front of and near enough to * the probe address assume it is the entry point to this * code and use it to further limit how far back we search * when determining if we're in an atomic sequence. If we could * not find any symbol skip the atomic test altogether as we * could otherwise end up searching irrelevant text/literals. * KPROBES depends on KALLSYMS so this last case should never * happen. */ if (kallsyms_lookup_size_offset((unsigned long) addr, &size, &offset)) { if (offset < (MAX_ATOMIC_CONTEXT_SIZE*sizeof(kprobe_opcode_t))) scan_end = addr - (offset / sizeof(kprobe_opcode_t)); else scan_end = addr - MAX_ATOMIC_CONTEXT_SIZE; } decoded = arm_probe_decode_insn(insn, asi); if (decoded != INSN_REJECTED && scan_end) if (is_probed_address_atomic(addr - 1, scan_end)) return INSN_REJECTED; return decoded; }
/* parts of the initialization that is done only when the object is loaded */ static int klp_init_object_loaded(struct klp_patch *patch, struct klp_object *obj) { struct klp_func *func; int ret; module_disable_ro(patch->mod); ret = klp_write_object_relocations(patch->mod, obj); if (ret) { module_enable_ro(patch->mod, true); return ret; } arch_klp_init_object_loaded(patch, obj); module_enable_ro(patch->mod, true); klp_for_each_func(obj, func) { ret = klp_find_object_symbol(obj->name, func->old_name, func->old_sympos, &func->old_addr); if (ret) return ret; ret = kallsyms_lookup_size_offset(func->old_addr, &func->old_size, NULL); if (!ret) { pr_err("kallsyms size lookup failed for '%s'\n", func->old_name); return -ENOENT; } ret = kallsyms_lookup_size_offset((unsigned long)func->new_func, &func->new_size, NULL); if (!ret) { pr_err("kallsyms size lookup failed for '%s' replacement\n", func->old_name); return -ENOENT; } }
static int __init frame_info_init(void) { unsigned long size = 0; #ifdef CONFIG_KALLSYMS unsigned long ofs; kallsyms_lookup_size_offset((unsigned long)schedule, &size, &ofs); #endif schedule_mfi.func = schedule; schedule_mfi.func_size = size; get_frame_info(&schedule_mfi); /* * Without schedule() frame info, result given by * thread_saved_pc() and get_wchan() are not reliable. */ if (schedule_mfi.pc_offset < 0) printk("Can't analyze schedule() prologue at %p\n", schedule); return 0; }
static int __init frame_info_init(void) { unsigned long size = 0; #ifdef CONFIG_KALLSYMS unsigned long ofs; kallsyms_lookup_size_offset((unsigned long)schedule, &size, &ofs); #endif schedule_mfi.func = schedule; schedule_mfi.func_size = size; get_frame_info(&schedule_mfi); /* */ if (schedule_mfi.pc_offset < 0) printk("Can't analyze schedule() prologue at %p\n", schedule); return 0; }
/* Check if paddr is at an instruction boundary */ static int __kprobes can_probe(unsigned long paddr) { int ret; unsigned long addr, offset = 0; struct insn insn; kprobe_opcode_t buf[MAX_INSN_SIZE]; if (!kallsyms_lookup_size_offset(paddr, NULL, &offset)) return 0; /* Decode instructions */ addr = paddr - offset; while (addr < paddr) { kernel_insn_init(&insn, (void *)addr); insn_get_opcode(&insn); /* * Check if the instruction has been modified by another * kprobe, in which case we replace the breakpoint by the * original instruction in our buffer. */ if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) { ret = recover_probed_instruction(buf, addr); if (ret) /* * Another debugging subsystem might insert * this breakpoint. In that case, we can't * recover it. */ return 0; kernel_insn_init(&insn, buf); } insn_get_length(&insn); addr += insn.length; } return (addr == paddr); }
/* generic stack unwinding function */ unsigned long notrace unwind_stack_by_address(unsigned long stack_page, unsigned long *sp, unsigned long pc, unsigned long *ra) { struct mips_frame_info info; unsigned long size, ofs; int leaf; extern void ret_from_irq(void); extern void ret_from_exception(void); if (!stack_page) return 0; /* * If we reached the bottom of interrupt context, * return saved pc in pt_regs. */ if (pc == (unsigned long)ret_from_irq || pc == (unsigned long)ret_from_exception) { struct pt_regs *regs; if (*sp >= stack_page && *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) { regs = (struct pt_regs *)*sp; pc = regs->cp0_epc; if (__kernel_text_address(pc)) { *sp = regs->regs[29]; *ra = regs->regs[31]; return pc; } } return 0; } if (!kallsyms_lookup_size_offset(pc, &size, &ofs)) return 0; /* * Return ra if an exception occured at the first instruction */ if (unlikely(ofs == 0)) { pc = *ra; *ra = 0; return pc; } info.func = (void *)(pc - ofs); info.func_size = ofs; /* analyze from start to ofs */ leaf = get_frame_info(&info); if (leaf < 0) return 0; if (*sp < stack_page || *sp + info.frame_size > stack_page + THREAD_SIZE - 32) return 0; if (leaf) /* * For some extreme cases, get_frame_info() can * consider wrongly a nested function as a leaf * one. In that cases avoid to return always the * same value. */ pc = pc != *ra ? *ra : 0; else pc = ((unsigned long *)(*sp))[info.pc_offset]; *sp += info.frame_size; *ra = 0; return __kernel_text_address(pc) ? pc : 0; }
unsigned long notrace unwind_stack_by_address(unsigned long stack_page, unsigned long *sp, unsigned long pc, unsigned long *ra) { struct mips_frame_info info; unsigned long size, ofs; int leaf; extern void ret_from_irq(void); extern void ret_from_exception(void); if (!stack_page) return 0; /* */ if (pc == (unsigned long)ret_from_irq || pc == (unsigned long)ret_from_exception) { struct pt_regs *regs; if (*sp >= stack_page && *sp + sizeof(*regs) <= stack_page + THREAD_SIZE - 32) { regs = (struct pt_regs *)*sp; pc = regs->cp0_epc; if (__kernel_text_address(pc)) { *sp = regs->regs[29]; *ra = regs->regs[31]; return pc; } } return 0; } if (!kallsyms_lookup_size_offset(pc, &size, &ofs)) return 0; /* */ if (unlikely(ofs == 0)) { pc = *ra; *ra = 0; return pc; } info.func = (void *)(pc - ofs); info.func_size = ofs; /* */ leaf = get_frame_info(&info); if (leaf < 0) return 0; if (*sp < stack_page || *sp + info.frame_size > stack_page + THREAD_SIZE - 32) return 0; if (leaf) /* */ pc = pc != *ra ? *ra : 0; else pc = ((unsigned long *)(*sp))[info.pc_offset]; *sp += info.frame_size; *ra = 0; return __kernel_text_address(pc) ? pc : 0; }