libipt, block: proceed to event with trace

The block decoder does not use trace when trying to reach an event location.
This works fine for most events but may cause it to fail with -pte_bad_query in
some cases.

For example, a MODE.EXEC binds to the next TIP and takes effect at the TIP IP.
In order to reach that location, we need to proceed past the (far) branch that
generated the TIP.

Change-Id: Ia56c37ea95c5d4e99f723bbfc79e5565c0e909e0
Signed-off-by: Markus Metzger <markus.t.metzger@intel.com>
diff --git a/libipt/src/pt_block_decoder.c b/libipt/src/pt_block_decoder.c
index 8eca947..c4aca8c 100644
--- a/libipt/src/pt_block_decoder.c
+++ b/libipt/src/pt_block_decoder.c
@@ -1248,6 +1248,45 @@
 	}
 }
 
+/* Proceed to a particular IP with trace, if necessary.
+ *
+ * Proceed until we reach @ip or until:
+ *
+ *   - @block is full:               return zero
+ *   - @block would switch sections: return zero
+ *   - we need trace:                return zero
+ *
+ * Update @decoder->ip to point to the last IP that was reached.
+ *
+ * A return of zero ends @block.
+ *
+ * Returns a positive integer if @ip was reached.
+ * Returns zero if no such instruction was reached.
+ * Returns a negative error code otherwise.
+ */
+static int pt_blk_proceed_to_ip_with_trace(struct pt_block_decoder *decoder,
+					   struct pt_block *block,
+					   uint64_t ip)
+{
+	struct pt_insn_ext iext;
+	struct pt_insn insn;
+	int status;
+
+	/* Try to reach @ip without trace.
+	 *
+	 * We're also OK if @block overflowed or we switched sections and we
+	 * have to try again in the next iteration.
+	 */
+	status = pt_blk_proceed_to_ip(decoder, block, &insn, &iext, ip);
+	if (status != -pte_bad_query)
+		return status;
+
+	/* Needing trace is not an error.  We use trace to determine the next
+	 * start IP and end the block.
+	 */
+	return pt_blk_proceed_with_trace(decoder, &insn, &iext);
+}
+
 /* Proceed to the event location for a disabled event.
  *
  * We have a (synchronous) disabled event pending.  Proceed to the event
@@ -1283,6 +1322,81 @@
 					    ev->variant.disabled.ip);
 }
 
+/* Proceed to the event location for an async paging event.
+ *
+ * We have an async paging event pending.  Proceed to the event location and
+ * indicate whether we were able to reach it.  Needing trace in order to proceed
+ * is not an error in this case but ends the block.
+ *
+ * Returns a positive integer if the event location was reached.
+ * Returns zero if the event location was not reached.
+ * Returns a negative error code otherwise.
+ */
+static int pt_blk_proceed_to_async_paging(struct pt_block_decoder *decoder,
+					  struct pt_block *block,
+					  const struct pt_event *ev)
+{
+	if (!ev)
+		return -pte_internal;
+
+	/* Apply the event immediately if we don't have an IP. */
+	if (ev->ip_suppressed)
+		return 1;
+
+	return pt_blk_proceed_to_ip_with_trace(decoder, block,
+					       ev->variant.async_paging.ip);
+}
+
+/* Proceed to the event location for an async vmcs event.
+ *
+ * We have an async vmcs event pending.  Proceed to the event location and
+ * indicate whether we were able to reach it.  Needing trace in order to proceed
+ * is not an error in this case but ends the block.
+ *
+ * Returns a positive integer if the event location was reached.
+ * Returns zero if the event location was not reached.
+ * Returns a negative error code otherwise.
+ */
+static int pt_blk_proceed_to_async_vmcs(struct pt_block_decoder *decoder,
+					struct pt_block *block,
+					const struct pt_event *ev)
+{
+	if (!ev)
+		return -pte_internal;
+
+	/* Apply the event immediately if we don't have an IP. */
+	if (ev->ip_suppressed)
+		return 1;
+
+	return pt_blk_proceed_to_ip_with_trace(decoder, block,
+					       ev->variant.async_vmcs.ip);
+}
+
+/* Proceed to the event location for an exec mode event.
+ *
+ * We have an exec mode event pending.  Proceed to the event location and
+ * indicate whether we were able to reach it.  Needing trace in order to proceed
+ * is not an error in this case but ends the block.
+ *
+ * Returns a positive integer if the event location was reached.
+ * Returns zero if the event location was not reached.
+ * Returns a negative error code otherwise.
+ */
+static int pt_blk_proceed_to_exec_mode(struct pt_block_decoder *decoder,
+				       struct pt_block *block,
+				       const struct pt_event *ev)
+{
+	if (!ev)
+		return -pte_internal;
+
+	/* Apply the event immediately if we don't have an IP. */
+	if (ev->ip_suppressed)
+		return 1;
+
+	return pt_blk_proceed_to_ip_with_trace(decoder, block,
+					       ev->variant.exec_mode.ip);
+}
+
 /* Try to work around erratum SKD022.
  *
  * If we get an asynchronous disable on VMLAUNCH or VMRESUME, the FUP that
@@ -1486,14 +1600,10 @@
 			break;
 
 		case ptev_async_paging:
-			if (!ev->ip_suppressed) {
-				ip = ev->variant.async_paging.ip;
-
-				status = pt_blk_proceed_to_ip(decoder, block,
-							      &insn, &iext, ip);
-				if (status <= 0)
-					return status;
-			}
+			status = pt_blk_proceed_to_async_paging(decoder, block,
+								ev);
+			if (status <= 0)
+				return status;
 
 			status = pt_blk_apply_paging(decoder, block, ev);
 			if (status < 0)
@@ -1538,14 +1648,10 @@
 			break;
 
 		case ptev_async_vmcs:
-			if (!ev->ip_suppressed) {
-				ip = ev->variant.async_vmcs.ip;
-
-				status = pt_blk_proceed_to_ip(decoder, block,
-							      &insn, &iext, ip);
-				if (status <= 0)
-					return status;
-			}
+			status = pt_blk_proceed_to_async_vmcs(decoder, block,
+							      ev);
+			if (status <= 0)
+				return status;
 
 			status = pt_blk_apply_vmcs(decoder, block, ev);
 			if (status < 0)
@@ -1561,14 +1667,10 @@
 			break;
 
 		case ptev_exec_mode:
-			if (!ev->ip_suppressed) {
-				ip = ev->variant.exec_mode.ip;
-
-				status = pt_blk_proceed_to_ip(decoder, block,
-							      &insn, &iext, ip);
-				if (status <= 0)
-					return status;
-			}
+			status = pt_blk_proceed_to_exec_mode(decoder, block,
+							     ev);
+			if (status <= 0)
+				return status;
 
 			status = pt_blk_process_exec_mode(decoder, block, ev);
 			if (status <= 0)
diff --git a/test/src/fup-pip-vmcs-tip.ptt b/test/src/fup-pip-vmcs-tip.ptt
new file mode 100644
index 0000000..70a5cd0
--- /dev/null
+++ b/test/src/fup-pip-vmcs-tip.ptt
@@ -0,0 +1,69 @@
+; Copyright (c) 2016, 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.
+
+; Test a PIP and VMCS binding to an in-flight asynchronous branch.
+;
+
+org 0x1000
+bits 64
+
+; @pt p0: psb()
+; @pt p1: mode.exec(64bit)
+; @pt p2: fup(3: %l0)
+; @pt p3: psbend()
+l0: nop
+
+; @pt p4: fup(1: %l1)
+l1: hlt
+
+; @pt p5: pip(0xcdcdc0)
+; @pt p6: vmcs(0xcdcdc000)
+; @pt p7: tip(3: %l2)
+l2: nop
+
+; @pt p8:fup(1: %l3)
+; @pt p9:tip.pgd(0: %l3)
+l3: hlt
+
+
+; @pt .exp(ptdump)
+;%0p0  psb
+;%0p1  mode.exec  cs.l
+;%0p2  fup        3: %?l0
+;%0p3  psbend
+;%0p4  fup        1: %?l1.2
+;%0p5  pip        cdcdc0        cr3  0000000000cdcdc0
+;%0p6  vmcs       cdcdc000      vmcs 00000000cdcdc000
+;%0p7  tip        3: %?l2
+;%0p8  fup        1: %?l3.2
+;%0p9  tip.pgd    0: %?l3.0
+
+
+; @pt .exp(ptxed)
+;%0l0
+;[interrupt]
+;%0l2
+;[disabled]
diff --git a/test/src/mode_exec-tip.ptt b/test/src/mode_exec-tip.ptt
new file mode 100644
index 0000000..696b3a3
--- /dev/null
+++ b/test/src/mode_exec-tip.ptt
@@ -0,0 +1,66 @@
+; Copyright (c) 2016, 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.
+
+; Test a MODE.EXEC binding to a TIP.
+;
+; The block decoder used to fail on this as it was not able to reach the
+; MODE.EXEC event IP over the far branch that caused the TIP.
+;
+
+org 0x1000
+bits 64
+
+; @pt p0: psb()
+; @pt p1: mode.exec(32bit)
+; @pt p2: fup(3: %l0)
+; @pt p3: psbend()
+l0: jmp far [rax]
+l1: hlt
+
+; @pt p4: mode.exec(64bit)
+; @pt p5: tip(3: %l2)
+l2: nop
+
+; @pt p6:fup(1: %l3)
+; @pt p7:tip.pgd(0: %l3)
+l3: hlt
+
+
+; @pt .exp(ptdump)
+;%0p0  psb
+;%0p1  mode.exec  cs.d
+;%0p2  fup        3: %?l0
+;%0p3  psbend
+;%0p4  mode.exec  cs.l
+;%0p5  tip        3: %?l2
+;%0p6  fup        1: %?l3.2
+;%0p7  tip.pgd    0: %?l3.0
+
+
+; @pt .exp(ptxed)
+;%0l0
+;%0l2
+;[disabled]