A duplex encoding like 0x00000000 decodes as two loads that both write r0.

Add a check in decode_insns() after both sub-instructions decode
successfully to verify they don't write the same destination register.

Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2696
Signed-off-by: Brian Cain <[email protected]>
---
 target/hexagon/decode.c           | 12 ++++++++++++
 tests/tcg/hexagon/invalid-dups.c  | 23 +++++++++++++++++++++++
 tests/tcg/hexagon/Makefile.target |  6 ++++++
 3 files changed, 41 insertions(+)
 create mode 100644 tests/tcg/hexagon/invalid-dups.c

diff --git a/target/hexagon/decode.c b/target/hexagon/decode.c
index 69ba1ec96c..90499fc320 100644
--- a/target/hexagon/decode.c
+++ b/target/hexagon/decode.c
@@ -501,12 +501,24 @@ decode_insns(DisasContext *ctx, Insn *insn, uint32_t 
encoding)
 
         /* The slot1 subinsn needs to be in the packet first */
         if (decode_slot1_subinsn(ctx, slot1_subinsn)) {
+            Insn *slot1_insn = insn;
             insn->generate = opcode_genptr[insn->opcode];
             insn->iclass = iclass_bits(encoding);
             ctx->insn = ++insn;
             if (decode_slot0_subinsn(ctx, slot0_subinsn)) {
                 insn->generate = opcode_genptr[insn->opcode];
                 insn->iclass = iclass_bits(encoding);
+                /*
+                 * Check that the two sub-instructions don't write the same
+                 * destination register (e.g., encoding 0x0 decodes as two
+                 * loads both writing R0, which is an invalid packet).
+                 */
+                if (insn->dest_idx >= 0 && slot1_insn->dest_idx >= 0 &&
+                    insn->regno[insn->dest_idx] ==
+                        slot1_insn->regno[slot1_insn->dest_idx]) {
+                    ctx->insn = --insn;
+                    return 0;
+                }
                 return 2;
             }
             /*
diff --git a/tests/tcg/hexagon/invalid-dups.c b/tests/tcg/hexagon/invalid-dups.c
new file mode 100644
index 0000000000..cb37ef7066
--- /dev/null
+++ b/tests/tcg/hexagon/invalid-dups.c
@@ -0,0 +1,23 @@
+/*
+ * Test that duplex encodings with duplicate destination registers are 
rejected.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+/*
+ * The encoding 0x00000000 decodes as a duplex with parse bits [15:14] = 0b00:
+ *   slot1: SL1_loadri_io R0 = memw(R0+#0x0)
+ *   slot0: SL1_loadri_io R0 = memw(R0+#0x0)
+ *
+ * Both sub-instructions write R0, which is an invalid packet (duplicate
+ * destination register).  This should raise SIGILL.
+ */
+
+int main()
+{
+    asm volatile(
+        ".word 0x00000000\n"
+        : : : "r0", "memory");
+    return 0;
+}
diff --git a/tests/tcg/hexagon/Makefile.target 
b/tests/tcg/hexagon/Makefile.target
index b0e20139c2..7199e29a30 100644
--- a/tests/tcg/hexagon/Makefile.target
+++ b/tests/tcg/hexagon/Makefile.target
@@ -52,6 +52,7 @@ HEX_TESTS += hvx_misc
 HEX_TESTS += hvx_histogram
 HEX_TESTS += invalid-slots
 HEX_TESTS += invalid-duplex
+HEX_TESTS += invalid-dups
 HEX_TESTS += unaligned_pc
 
 run-and-check-exception = $(call run-test,$2,$3 2>$2.stderr; \
@@ -68,6 +69,11 @@ run-invalid-duplex: invalid-duplex
                $(QEMU) $(QEMU_OPTS) $< ; test $$? -eq 132, \
                TEST, invalid-duplex on $(TARGET_NAME))
 
+run-invalid-dups: invalid-dups
+       $(call quiet-command, \
+               $(QEMU) $(QEMU_OPTS) $< ; test $$? -eq 132, \
+               TEST, invalid-dups on $(TARGET_NAME))
+
 HEX_TESTS += test_abs
 HEX_TESTS += test_bitcnt
 HEX_TESTS += test_bitsplit
-- 
2.34.1

Reply via email to