force_sig_fault(TARGET_SIGBUS, TARGET_BUS_ADRALN,
env->gpr[HEX_REG_R31]);
break;
+ case HEX_CAUSE_INVALID_PACKET:
+ force_sig_fault(TARGET_SIGILL, TARGET_ILL_ILLOPC,
+ env->gpr[HEX_REG_PC]);
+ break;
case EXCP_ATOMIC:
cpu_exec_step_atomic(cs);
break;
insn->iclass = iclass_bits(encoding);
return 2;
}
+ /*
+ * Slot0 decode failed after slot1 succeeded. This is an invalid
+ * duplex encoding (both sub-instructions must be valid).
+ */
+ ctx->insn = --insn;
}
- g_assert_not_reached();
+ /* Invalid duplex encoding - return 0 to signal failure */
+ return 0;
}
}
encoding32 = words[words_read];
end_of_packet = is_packet_end(encoding32);
new_insns = decode_insns(ctx, insn, encoding32);
- g_assert(new_insns > 0);
+ if (new_insns == 0) {
+ /* Invalid instruction encoding */
+ return 0;
+ }
/*
* If we saw an extender, mark next word extended so immediate
* decode works
tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], ctx->next_PC);
gen_exception_raw(excp);
ctx->base.is_jmp = DISAS_NORETURN;
+}
+
+/*
+ * Generate exception for decode failures. Unlike gen_exception_end_tb,
+ * this is used when decode fails before ctx->next_PC is initialized.
+ */
+static void gen_exception_decode_fail(DisasContext *ctx, int nwords, int excp)
+{
+ target_ulong fail_pc = ctx->base.pc_next + nwords * sizeof(uint32_t);
+ gen_exec_counters(ctx);
+ tcg_gen_movi_tl(hex_gpr[HEX_REG_PC], fail_pc);
+ gen_exception_raw(excp);
+ ctx->base.is_jmp = DISAS_NORETURN;
+ ctx->base.pc_next = fail_pc;
}
static int read_packet_words(CPUHexagonState *env, DisasContext *ctx,
nwords = read_packet_words(env, ctx, words);
if (!nwords) {
- gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET);
+ gen_exception_decode_fail(ctx, 0, HEX_CAUSE_INVALID_PACKET);
return;
}
gen_commit_packet(ctx);
ctx->base.pc_next += pkt.encod_pkt_size_in_bytes;
} else {
- gen_exception_end_tb(ctx, HEX_CAUSE_INVALID_PACKET);
+ gen_exception_decode_fail(ctx, nwords, HEX_CAUSE_INVALID_PACKET);
}
}
HEX_TESTS += hvx_misc
HEX_TESTS += hvx_histogram
HEX_TESTS += invalid-slots
+HEX_TESTS += invalid-encoding
HEX_TESTS += unaligned_pc
run-and-check-exception = $(call run-test,$2,$3 2>$2.stderr; \
--- /dev/null
+/*
+ * Test that invalid instruction encodings are properly rejected.
+ *
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void *resume_pc;
+
+static void handle_sigill(int sig, siginfo_t *info, void *puc)
+{
+ ucontext_t *uc = (ucontext_t *)puc;
+
+ if (sig != SIGILL) {
+ _exit(EXIT_FAILURE);
+ }
+
+ uc->uc_mcontext.r0 = SIGILL;
+ uc->uc_mcontext.pc = (unsigned long)resume_pc;
+}
+
+/*
+ * Each test function:
+ * - Sets r0 to something other than SIGILL
+ * - Stores the resume address into resume_pc
+ * - Executes the invalid encoding
+ * - The handler sets r0 = SIGILL and resumes after the faulting packet
+ * - Returns the value in r0
+ */
+
+/*
+ * Invalid duplex encoding (issue #3291):
+ * - Word 0: 0x0fff6fff = immext(#0xfffbffc0), parse bits = 01
+ * - Word 1: 0x600237b0 = duplex with:
+ * - slot0 = 0x17b0 (invalid S2 subinstruction encoding)
+ * - slot1 = 0x0002 (valid SA1_addi)
+ * - duplex iclass = 7 (S2 for slot0, A for slot1)
+ *
+ * Since slot0 doesn't decode to any valid S2 subinstruction, this packet
+ * should be rejected and raise SIGILL.
+ */
+static int test_invalid_duplex(void)
+{
+ int sig;
+
+ asm volatile(
+ "r0 = #0\n"
+ "r1 = ##1f\n"
+ "memw(%1) = r1\n"
+ ".word 0x0fff6fff\n" /* immext(#0xfffbffc0), parse=01 */
+ ".word 0x600237b0\n" /* duplex: slot0=0x17b0 (invalid) */
+ "1:\n"
+ "%0 = r0\n"
+ : "=r"(sig)
+ : "r"(&resume_pc)
+ : "r0", "r1", "memory");
+
+ return sig;
+}
+
+int main()
+{
+ struct sigaction act;
+
+ memset(&act, 0, sizeof(act));
+ act.sa_sigaction = handle_sigill;
+ act.sa_flags = SA_SIGINFO;
+ assert(sigaction(SIGILL, &act, NULL) == 0);
+
+ assert(test_invalid_duplex() == SIGILL);
+
+ puts("PASS");
+ return EXIT_SUCCESS;
+}