/* * Copyright (c) 2016-2017, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "pt_insn.h" #include "pt_ild.h" #include "pt_image.h" #include "intel-pt.h" int pt_insn_changes_cpl(const struct pt_insn *insn, const struct pt_insn_ext *iext) { (void) insn; if (!iext) return 0; switch (iext->iclass) { default: return 0; case PTI_INST_INT: case PTI_INST_INT3: case PTI_INST_INT1: case PTI_INST_INTO: case PTI_INST_IRET: case PTI_INST_SYSCALL: case PTI_INST_SYSENTER: case PTI_INST_SYSEXIT: case PTI_INST_SYSRET: return 1; } } int pt_insn_changes_cr3(const struct pt_insn *insn, const struct pt_insn_ext *iext) { (void) insn; if (!iext) return 0; switch (iext->iclass) { default: return 0; case PTI_INST_MOV_CR3: return 1; } } int pt_insn_is_branch(const struct pt_insn *insn, const struct pt_insn_ext *iext) { (void) iext; if (!insn) return 0; switch (insn->iclass) { default: return 0; case ptic_call: case ptic_return: case ptic_jump: case ptic_cond_jump: case ptic_far_call: case ptic_far_return: case ptic_far_jump: return 1; } } int pt_insn_is_far_branch(const struct pt_insn *insn, const struct pt_insn_ext *iext) { (void) iext; if (!insn) return 0; switch (insn->iclass) { default: return 0; case ptic_far_call: case ptic_far_return: case ptic_far_jump: return 1; } } int pt_insn_binds_to_pip(const struct pt_insn *insn, const struct pt_insn_ext *iext) { if (!iext) return 0; switch (iext->iclass) { default: return pt_insn_is_far_branch(insn, iext); case PTI_INST_MOV_CR3: case PTI_INST_VMLAUNCH: case PTI_INST_VMRESUME: return 1; } } int pt_insn_binds_to_vmcs(const struct pt_insn *insn, const struct pt_insn_ext *iext) { if (!iext) return 0; switch (iext->iclass) { default: return pt_insn_is_far_branch(insn, iext); case PTI_INST_VMPTRLD: case PTI_INST_VMLAUNCH: case PTI_INST_VMRESUME: return 1; } } int pt_insn_next_ip(uint64_t *pip, const struct pt_insn *insn, const struct pt_insn_ext *iext) { uint64_t ip; if (!insn || !iext) return -pte_internal; ip = insn->ip + insn->size; switch (insn->iclass) { case ptic_other: break; case ptic_call: case ptic_jump: if (iext->variant.branch.is_direct) { ip += iext->variant.branch.displacement; break; } /* Fall through. */ default: return -pte_bad_query; case ptic_error: return -pte_bad_insn; } if (pip) *pip = ip; return 0; } /* Retry decoding an instruction after a preceding decode error. * * Instruction length decode typically fails due to 'not enough * bytes'. * * This may be caused by partial updates of text sections * represented via new image sections overlapping the original * text section's image section. We stop reading memory at the * end of the section so we do not read the full instruction if * parts of it have been overwritten by the update. * * Try to read the remaining bytes and decode the instruction again. If we * succeed, set @insn->truncated to indicate that the instruction is truncated * in @insn->isid. * * Returns zero on success, a negative error code otherwise. * Returns -pte_bad_insn if the instruction could not be decoded. */ static int pt_insn_decode_retry(struct pt_insn *insn, struct pt_insn_ext *iext, struct pt_image *image, const struct pt_asid *asid) { int size, errcode, isid; uint8_t isize, remaining; if (!insn) return -pte_internal; isize = insn->size; remaining = sizeof(insn->raw) - isize; /* We failed for real if we already read the maximum number of bytes for * an instruction. */ if (!remaining) return -pte_bad_insn; /* Read the remaining bytes from the image. */ size = pt_image_read(image, &isid, &insn->raw[isize], remaining, asid, insn->ip + isize); if (size <= 0) { /* We should have gotten an error if we were not able to read at * least one byte. Check this to guarantee termination. */ if (!size) return -pte_internal; return size; } /* Add the newly read bytes to the instruction's size. */ insn->size += (uint8_t) size; /* Store the new size to avoid infinite recursion in case instruction * decode fails after length decode, which would set @insn->size to the * actual length. */ size = insn->size; /* Try to decode the instruction again. * * If we fail again, we recursively retry again until we either fail to * read more bytes or reach the maximum number of bytes for an * instruction. */ errcode = pt_ild_decode(insn, iext); if (errcode < 0) { if (errcode != -pte_bad_insn) return errcode; /* If instruction length decode already determined the size, * there's no point in reading more bytes. */ if (insn->size != (uint8_t) size) return errcode; return pt_insn_decode_retry(insn, iext, image, asid); } /* We succeeded this time, so the instruction crosses image section * boundaries. * * This poses the question which isid to use for the instruction. * * To reconstruct exactly this instruction at a later time, we'd need to * store all isids involved together with the number of bytes read for * each isid. Since @insn already provides the exact bytes for this * instruction, we assume that the isid will be used solely for source * correlation. In this case, it should refer to the first byte of the * instruction - as it already does. */ insn->truncated = 1; return errcode; } int pt_insn_decode(struct pt_insn *insn, struct pt_insn_ext *iext, struct pt_image *image, const struct pt_asid *asid) { int size, errcode; if (!insn) return -pte_internal; /* Read the memory at the current IP in the current address space. */ size = pt_image_read(image, &insn->isid, insn->raw, sizeof(insn->raw), asid, insn->ip); if (size < 0) return size; /* We initialize @insn->size to the maximal possible size. It will be * set to the actual size during instruction decode. */ insn->size = (uint8_t) size; errcode = pt_ild_decode(insn, iext); if (errcode < 0) { if (errcode != -pte_bad_insn) return errcode; /* If instruction length decode already determined the size, * there's no point in reading more bytes. */ if (insn->size != (uint8_t) size) return errcode; return pt_insn_decode_retry(insn, iext, image, asid); } return errcode; } int pt_insn_range_is_contiguous(uint64_t begin, uint64_t end, enum pt_exec_mode mode, struct pt_image *image, const struct pt_asid *asid, size_t steps) { struct pt_insn_ext iext; struct pt_insn insn; memset(&insn, 0, sizeof(insn)); insn.mode = mode; insn.ip = begin; while (insn.ip != end) { int errcode; if (!steps--) return 0; errcode = pt_insn_decode(&insn, &iext, image, asid); if (errcode < 0) return errcode; errcode = pt_insn_next_ip(&insn.ip, &insn, &iext); if (errcode < 0) return errcode; } return 1; }