Blame libipt/src/pt_insn.c

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