kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit dba8886b09e7bd057a9bcdc43c9588d458cbcf02
parent 37e9c50a8723354b323469550c4a38943eace07f
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 13 May 2026 07:44:31 -0700

Validate AArch64 SP and ZR asm operands

Diffstat:
Msrc/arch/aa64_asm.c | 33+++++++++++++++++++++++++++++++++
Mtest/arch/aa64_inline_test.c | 40++++++++++++++++++++++++++++++++++++++++
2 files changed, 73 insertions(+), 0 deletions(-)

diff --git a/src/arch/aa64_asm.c b/src/arch/aa64_asm.c @@ -223,6 +223,16 @@ static AA64Reg parse_ldstp_reg(AsmDriver* d) { return r; } +static void reject_sp_reg(AsmDriver* d, AA64Reg r, const char* what) { + if (r.is_sp) asm_driver_panic(d, "asm: %s: SP register not allowed", what); +} + +static void require_sp_spelling(AsmDriver* d, AA64Reg r, const char* what) { + if (r.num == 31u && !r.is_sp) + asm_driver_panic(d, "asm: %s: zero register not allowed in SP operand", + what); +} + /* Parse "#imm" (with optional + / -) or a bare expression — GNU as is * lenient about the leading hash. Returns an i64. */ static i64 parse_imm_const(AsmDriver* d) { @@ -387,6 +397,8 @@ static void p_mov(AsmDriver* d) { /* mov involving SP encodes as `ADD Rd, Rsp, #0` per AArch64; * approximate with that exact form. */ if (rd.is_sp || src.is_sp) { + require_sp_spelling(d, rd, "mov sp"); + require_sp_spelling(d, src, "mov sp"); emit32(d, aa64_add_imm(rd.is64, rd.num, src.num, 0, 0)); return; } @@ -515,6 +527,14 @@ static void p_addsub(AsmDriver* d, int is_sub, int set_flags) { if (tok_punct(t, '#') || t.kind == TOK_NUM || tok_punct(t, '-') || tok_punct(t, '+')) { /* immediate form */ + if (rd.is64 != rn.is64) + asm_driver_panic(d, "asm: add/sub imm: width mismatch"); + require_sp_spelling(d, rn, "add/sub imm"); + if (set_flags) { + reject_sp_reg(d, rd, "add/sub imm"); + } else { + require_sp_spelling(d, rd, "add/sub imm"); + } i64 imm = parse_imm_const(d); u32 sh = 0; if (asm_driver_eat_comma(d)) { @@ -540,6 +560,9 @@ static void p_addsub(AsmDriver* d, int is_sub, int set_flags) { } /* register form */ AA64Reg rm = parse_reg(d); + reject_sp_reg(d, rd, "add/sub reg"); + reject_sp_reg(d, rn, "add/sub reg"); + reject_sp_reg(d, rm, "add/sub reg"); if (rd.is64 != rm.is64 || rd.is64 != rn.is64) asm_driver_panic(d, "asm: add/sub reg: width mismatch"); u32 shift = 0, imm6 = 0; @@ -561,6 +584,7 @@ static void p_cmp(AsmDriver* d, int is_neg /* cmn flips op */) { Tok t = asm_driver_peek(d); if (tok_punct(t, '#') || t.kind == TOK_NUM || tok_punct(t, '-') || tok_punct(t, '+')) { + require_sp_spelling(d, rn, "cmp imm"); i64 imm = parse_imm_const(d); u32 sh = 0; if (asm_driver_eat_comma(d)) { @@ -587,6 +611,8 @@ static void p_cmp(AsmDriver* d, int is_neg /* cmn flips op */) { return; } AA64Reg rm = parse_reg(d); + reject_sp_reg(d, rn, "cmp reg"); + reject_sp_reg(d, rm, "cmp reg"); if (rm.is64 != rn.is64) asm_driver_panic(d, "asm: cmp: width mismatch"); u32 shift = 0, imm6 = 0; if (asm_driver_eat_comma(d)) parse_shift_mod(d, &shift, &imm6); @@ -619,6 +645,8 @@ static void p_neg(AsmDriver* d, int set_flags) { AA64Reg rd = parse_reg(d); expect_comma(d, "neg"); AA64Reg rm = parse_reg(d); + reject_sp_reg(d, rd, "neg"); + reject_sp_reg(d, rm, "neg"); if (rd.is64 != rm.is64) asm_driver_panic(d, "asm: neg: width mismatch"); u32 shift = 0, imm6 = 0; if (asm_driver_eat_comma(d)) parse_shift_mod(d, &shift, &imm6); @@ -777,6 +805,7 @@ static AA64Mem parse_mem(AsmDriver* d) { m.base = parse_reg(d); if (!m.base.is64) asm_driver_panic(d, "asm: ldr/str: base register must be 64-bit"); + require_sp_spelling(d, m.base, "ldr/str base"); if (asm_driver_eat_comma(d)) { m.imm = parse_imm_const(d); m.has_offset = 1; @@ -791,6 +820,7 @@ static AA64Mem parse_mem(AsmDriver* d) { * alignment of imm. */ static void p_ldr_str(AsmDriver* d, int is_load) { AA64Reg rt = parse_reg(d); + reject_sp_reg(d, rt, "ldr/str"); expect_comma(d, "ldr/str"); AA64Mem m = parse_mem(d); u32 size = rt.is64 ? 3u : 2u; @@ -824,6 +854,7 @@ static void p_ldr_str(AsmDriver* d, int is_load) { /* ldur/stur — unscaled signed-imm9. */ static void p_ldur_stur(AsmDriver* d, int is_load) { AA64Reg rt = parse_reg(d); + reject_sp_reg(d, rt, "ldur/stur"); expect_comma(d, "ldur/stur"); AA64Mem m = parse_mem(d); u32 size = rt.is64 ? 3u : 2u; @@ -843,6 +874,8 @@ static void p_ldp_stp(AsmDriver* d, int is_load) { expect_comma(d, "ldp/stp"); AA64Reg rt2 = parse_ldstp_reg(d); expect_comma(d, "ldp/stp"); + reject_sp_reg(d, rt, "ldp/stp"); + reject_sp_reg(d, rt2, "ldp/stp"); if (rt.is64 != rt2.is64 || rt.is_fp != rt2.is_fp) asm_driver_panic(d, "asm: ldp/stp: width mismatch"); AA64Mem m = parse_mem(d); diff --git a/test/arch/aa64_inline_test.c b/test/arch/aa64_inline_test.c @@ -81,6 +81,7 @@ static int g_fail = 0; * (no bare hex literals as load-bearing values). */ #define EXPECTED_MOV_W0_W9 0x2a0903e0u /* mov w0, w9 ≡ orr w0, wzr, w9 */ #define EXPECTED_SVC_0 0xd4000001u /* svc #0 */ +#define EXPECTED_ADD_W0_WSP_7 0x11001fe0u /* add w0, wsp, #7 */ static u32 read_word_le(const Section* s, u32 ofs) { u8 b[4]; @@ -245,6 +246,45 @@ int main(void) { } } + /* ---- smoke case 5: ADD/SUB immediate treats reg31 as SP, not ZR ---- */ + { + u32 start = mc->pos(mc); + target->asm_block(target, "add w0, wsp, #7", + NULL, 0, NULL, NULL, 0, NULL, NULL, 0); + u32 end = mc->pos(mc); + EXPECT(end - start == 4u, "smoke5: expected 4 bytes, got %u", + (end - start)); + if (end - start == 4u) { + const Section* sec = obj_section_get(ob, text_sec); + u32 w = read_word_le(sec, start); + EXPECT(w == EXPECTED_ADD_W0_WSP_7, + "smoke5: add w0, wsp, #7 = 0x%08x, want 0x%08x", w, + EXPECTED_ADD_W0_WSP_7); + } + } + + /* ---- smoke case 6: ZR spelling is rejected in SP-only slots ---- */ + { + int saw_panic = 0; + if (setjmp(c->panic) == 0) { + target->asm_block(target, "add w0, wzr, #7", + NULL, 0, NULL, NULL, 0, NULL, NULL, 0); + } else { + saw_panic = 1; + } + EXPECT(saw_panic, "smoke6: expected add w0, wzr, #7 to panic"); + } + { + int saw_panic = 0; + if (setjmp(c->panic) == 0) { + target->asm_block(target, "ldr w0, [xzr]", + NULL, 0, NULL, NULL, 0, NULL, NULL, 0); + } else { + saw_panic = 1; + } + EXPECT(saw_panic, "smoke6: expected ldr w0, [xzr] to panic"); + } + cfree_compiler_free(cc); if (g_fail) {