diff --git a/asm/assembler_fuzz_test.go b/asm/assembler_fuzz_test.go index d0a2e05..cf6b5d7 100644 --- a/asm/assembler_fuzz_test.go +++ b/asm/assembler_fuzz_test.go @@ -232,6 +232,11 @@ func FuzzAssembleRISCV64(f *testing.F) { f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tCSRRW $0x1000, X5, X6\n\tRET\n") f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tSLLI $64, X5, X6\n\tRET\n") f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tVLE8V (X10), V32\n\tRET\n") + // Operand-starved spellings that used to panic the layout and encode + // passes; each must come back as a diagnostic. + f.Add("TEXT ·f(SB), $0\n\tJALR\n\tRET\n") + f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tROR $3\n\tRET\n") + f.Add("TEXT ·f(SB), NOSPLIT, $0\n\tVLE8V (X10)\n\tRET\n") f.Add("#define A A\nA\n") f.Fuzz(func(t *testing.T, src string) { diff --git a/asm/riscv_assemble.go b/asm/riscv_assemble.go index 25036ca..5963c9a 100644 --- a/asm/riscv_assemble.go +++ b/asm/riscv_assemble.go @@ -570,6 +570,11 @@ func riscvRevShiftSize(mnem string, ops []*ast.Operand) int { if mnem != "ROR" && mnem != "RORI" { return 4 // SLLIW has no compressed form } + if len(ops) < 2 { + // A malformed one-operand form: encoding rejects it with a + // diagnostic, and the layout pass only needs a word count. + return 4 + } imm := int(immFromOperand(ops[0])) rs1 := regFromOperand(ops[1]) rd := rs1 @@ -4830,6 +4835,9 @@ func encodeRISCVVecLS(mnem string, ops []*ast.Operand) ([]byte, bool, error) { if !ok { return nil, true, fmt.Errorf("unsupported vector load/store %q", mnem) } + if len(ops) < 2 { + return nil, true, fmt.Errorf("%s expects at least 2 operands, got %d", mnem, len(ops)) + } op := uint32(0x27) if v.load { op = 0x07 diff --git a/asm/riscv_frame.go b/asm/riscv_frame.go index 3f356f4..c7e0202 100644 --- a/asm/riscv_frame.go +++ b/asm/riscv_frame.go @@ -96,8 +96,10 @@ func riscvIsLeaf(t *ast.Text) bool { case "JALR": // JALR rd, offset(rs1) links when the destination register (the // first operand) is X1; JALR rs1, rd links when the second - // register is X1; JALR offset(rs1) always links to X1. - if len(in.Operands) == 1 { + // register is X1; JALR offset(rs1) always links to X1. A bare + // operand-less spelling is malformed and encoding rejects it; + // conservatively count it as a link so the frame stays honest. + if len(in.Operands) <= 1 { return false } if isMemOperand(in.Operands[1]) {