/** * insn_get_modrm - collect ModRM byte, if any * @insn: &struct insn containing instruction * * Populates @insn->modrm and updates @insn->next_byte to point past the * ModRM byte, if any. If necessary, first collects the preceding bytes * (prefixes and opcode(s)). No effect if @insn->modrm.got is already 1. */ void insn_get_modrm(struct insn *insn) { struct insn_field *modrm = &insn->modrm; insn_byte_t pfx_id, mod; if (modrm->got) return; if (!insn->opcode.got) insn_get_opcode(insn); if (inat_has_modrm(insn->attr)) { mod = get_next(insn_byte_t, insn); modrm->value = mod; modrm->nbytes = 1; if (inat_is_group(insn->attr)) { pfx_id = insn_last_prefix_id(insn); insn->attr = inat_get_group_attribute(mod, pfx_id, insn->attr); if (insn_is_avx(insn) && !inat_accept_vex(insn->attr)) insn->attr = 0; /* This is bad */ } } if (insn->x86_64 && inat_is_force64(insn->attr)) insn->opnd_bytes = 8; modrm->got = 1; err_out: return; }
/* * Figure out which fixups arch_uprobe_post_xol() will need to perform, and * annotate arch_uprobe->fixups accordingly. To start with, * arch_uprobe->fixups is either zero or it reflects rip-related fixups. */ static void prepare_fixups(struct arch_uprobe *auprobe, struct insn *insn) { bool fix_ip = true, fix_call = false; /* defaults */ int reg; insn_get_opcode(insn); /* should be a nop */ switch (OPCODE1(insn)) { case 0x9d: /* popf */ auprobe->fixups |= UPROBE_FIX_SETF; break; case 0xc3: /* ret/lret */ case 0xcb: case 0xc2: case 0xca: /* ip is correct */ fix_ip = false; break; case 0xe8: /* call relative - Fix return addr */ fix_call = true; break; case 0x9a: /* call absolute - Fix return addr, not ip */ fix_call = true; fix_ip = false; break; case 0xff: insn_get_modrm(insn); reg = MODRM_REG(insn); if (reg == 2 || reg == 3) { /* call or lcall, indirect */ /* Fix return addr; ip is correct. */ fix_call = true; fix_ip = false; } else if (reg == 4 || reg == 5) { /* jmp or ljmp, indirect */ /* ip is correct. */ fix_ip = false; } break; case 0xea: /* jmp absolute -- ip is correct */ fix_ip = false; break; default: break; } if (fix_ip) auprobe->fixups |= UPROBE_FIX_IP; if (fix_call) auprobe->fixups |= UPROBE_FIX_CALL; }
/* * Copy an instruction and adjust the displacement if the instruction * uses the %rip-relative addressing mode. * If it does, Return the address of the 32-bit displacement word. * If not, return null. * Only applicable to 64-bit x86. */ static int __kprobes __copy_instruction(u8 *dest, u8 *src, int recover) { struct insn insn; int ret; kprobe_opcode_t buf[MAX_INSN_SIZE]; kernel_insn_init(&insn, src); if (recover) { insn_get_opcode(&insn); if (insn.opcode.bytes[0] == BREAKPOINT_INSTRUCTION) { ret = recover_probed_instruction(buf, (unsigned long)src); if (ret) return 0; kernel_insn_init(&insn, buf); } } insn_get_length(&insn); memcpy(dest, insn.kaddr, insn.length); #ifdef CONFIG_X86_64 if (insn_rip_relative(&insn)) { s64 newdisp; u8 *disp; kernel_insn_init(&insn, dest); insn_get_displacement(&insn); /* * The copied instruction uses the %rip-relative addressing * mode. Adjust the displacement for the difference between * the original location of this instruction and the location * of the copy that will actually be run. The tricky bit here * is making sure that the sign extension happens correctly in * this calculation, since we need a signed 32-bit result to * be sign-extended to 64 bits when it's added to the %rip * value and yield the same 64-bit result that the sign- * extension of the original signed 32-bit displacement would * have given. */ newdisp = (u8 *) src + (s64) insn.displacement.value - (u8 *) dest; BUG_ON((s64) (s32) newdisp != newdisp); /* Sanity check. */ disp = (u8 *) dest + insn_offset_displacement(&insn); *(s32 *) disp = (s32) newdisp; } #endif return insn.length; }
static int mpx_insn_decode(struct insn *insn, struct pt_regs *regs) { unsigned char buf[MAX_INSN_SIZE]; int x86_64 = !test_thread_flag(TIF_IA32); int not_copied; int nr_copied; not_copied = copy_from_user(buf, (void __user *)regs->ip, sizeof(buf)); nr_copied = sizeof(buf) - not_copied; /* * The decoder _should_ fail nicely if we pass it a short buffer. * But, let's not depend on that implementation detail. If we * did not get anything, just error out now. */ if (!nr_copied) return -EFAULT; insn_init(insn, buf, nr_copied, x86_64); insn_get_length(insn); /* * copy_from_user() tries to get as many bytes as we could see in * the largest possible instruction. If the instruction we are * after is shorter than that _and_ we attempt to copy from * something unreadable, we might get a short read. This is OK * as long as the read did not stop in the middle of the * instruction. Check to see if we got a partial instruction. */ if (nr_copied < insn->length) return -EFAULT; insn_get_opcode(insn); /* * We only _really_ need to decode bndcl/bndcn/bndcu * Error out on anything else. */ if (insn->opcode.bytes[0] != 0x0f) goto bad_opcode; if ((insn->opcode.bytes[1] != 0x1a) && (insn->opcode.bytes[1] != 0x1b)) goto bad_opcode; return 0; bad_opcode: return -EINVAL; }
static int validate_insn_64bits(struct arch_uprobe *auprobe, struct insn *insn) { insn_init(insn, auprobe->insn, true); /* Skip good instruction prefixes; reject "bad" ones. */ insn_get_opcode(insn); if (is_prefix_bad(insn)) return -ENOTSUPP; if (test_bit(OPCODE1(insn), (unsigned long *)good_insns_64)) return 0; if (insn->opcode.nbytes == 2) { if (test_bit(OPCODE2(insn), (unsigned long *)good_2byte_insns)) return 0; } return -ENOTSUPP; }
/* 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(paddr, NULL, &offset, NULL, __dummy_buf)) 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); }
/** * insn_get_modrm - collect ModRM byte, if any * @insn: &struct insn containing instruction * * Populates @insn->modrm and updates @insn->next_byte to point past the * ModRM byte, if any. If necessary, first collects the preceding bytes * (prefixes and opcode(s)). No effect if @insn->modrm.got is already 1. */ void insn_get_modrm(struct insn *insn) { struct insn_field *modrm = &insn->modrm; insn_byte_t pfx, mod; if (modrm->got) return; if (!insn->opcode.got) insn_get_opcode(insn); if (inat_has_modrm(insn->attr)) { mod = get_next(insn_byte_t, insn); modrm->value = mod; modrm->nbytes = 1; if (inat_is_group(insn->attr)) { pfx = insn_last_prefix(insn); insn->attr = inat_get_group_attribute(mod, pfx, insn->attr); } } if (insn->x86_64 && inat_is_force64(insn->attr)) insn->opnd_bytes = 8; modrm->got = 1; }
/* Decode and process the instruction ('c_insn') at * the address 'kaddr' - see the description of do_process_area for details. * * Check if we get past the end of the buffer [kaddr, end_kaddr) * * The function returns the length of the instruction in bytes. * 0 is returned in case of failure. */ static unsigned int do_process_insn(struct insn* c_insn, void* kaddr, void* end_kaddr, void** from_funcs, void** to_funcs, unsigned int nfuncs) { /* ptr to the 32-bit offset argument in the instruction */ u32* offset = NULL; /* address of the function being called */ void* addr = NULL; static const unsigned char op = 0xe8; /* 'call <offset>' */ int i; BUG_ON(from_funcs == NULL || to_funcs == NULL); /* Decode the instruction and populate 'insn' structure */ kernel_insn_init(c_insn, kaddr); insn_get_length(c_insn); if (c_insn->length == 0) { return 0; } if (kaddr + c_insn->length > end_kaddr) { /* Note: it is OK to stop at 'end_kaddr' but no further */ KEDR_MSG(COMPONENT_STRING "instruction decoder stopped past the end of the section.\n"); insn_get_opcode(c_insn); printk(KERN_ALERT COMPONENT_STRING "kaddr=%p, end_kaddr=%p, c_insn->length=%d, opcode=0x%x\n", (void*)kaddr, (void*)end_kaddr, (int)c_insn->length, (unsigned int)c_insn->opcode.value ); WARN_ON(1); } /* This call may be overkill as insn_get_length() probably has to decode * the instruction completely. * Still, to operate safely, we need insn_get_opcode() before we can access * c_insn->opcode. * The call is cheap anyway, no re-decoding is performed. */ insn_get_opcode(c_insn); if (c_insn->opcode.value != op) { /* Not a 'call' instruction, nothing to do. */ return c_insn->length; } /* [NB] For some reason, the decoder stores the argument of 'call' and 'jmp' * as 'immediate' rather than 'displacement' (as Intel manuals name it). * May be it is a bug, may be it is not. * Meanwhile, I'll call this value 'offset' to avoid confusion. */ /* Call this before trying to access c_insn->immediate */ insn_get_immediate(c_insn); if (c_insn->immediate.nbytes != 4) { KEDR_MSG(COMPONENT_STRING "at 0x%p: " "opcode: 0x%x, " "immediate field is %u rather than 32 bits in size; " "insn.length = %u, insn.imm = %u, off_immed = %d\n", kaddr, (unsigned int)c_insn->opcode.value, 8 * (unsigned int)c_insn->immediate.nbytes, c_insn->length, (unsigned int)c_insn->immediate.value, insn_offset_immediate(c_insn)); WARN_ON(1); return c_insn->length; } offset = (u32*)(kaddr + insn_offset_immediate(c_insn)); addr = CALL_ADDR_FROM_OFFSET(kaddr, c_insn->length, *offset); /* Check if one of the functions of interest is called */ for (i = 0; i < nfuncs; ++i) { if (addr == from_funcs[i]) { /* Change the address of the function to be called */ BUG_ON(to_funcs[i] == NULL); KEDR_MSG(COMPONENT_STRING "at 0x%p: changing address 0x%p to 0x%p (displ: 0x%x to 0x%x)\n", kaddr, from_funcs[i], to_funcs[i], (unsigned int)(*offset), (unsigned int)CALL_OFFSET_FROM_ADDR( kaddr, c_insn->length, to_funcs[i]) ); *offset = CALL_OFFSET_FROM_ADDR( kaddr, c_insn->length, to_funcs[i] ); break; } } return c_insn->length; }