target/riscv: SSPUSH and SSPOPCHK do not apply pointer masking to the shadow-stack address

Host environment

  • Operating system: AlmaLinux 9.7
  • Architecture: x86_64
  • QEMU flavor: qemu-system-riscv64
  • QEMU version: 11.1.2 (the code below is unchanged on master d7314928)
  • QEMU command line: qemu-system-riscv64 -nographic -semihosting -machine virt -cpu max -bios sspm-semihost.elf

Emulated/Virtualized environment

  • Bare-metal test program, TCG

Description of problem

SSPUSH and SSPOPCHK do not apply pointer masking to the shadow-stack address. With S-mode pointer masking enabled (menvcfg.PMM = 11, PMLEN = 16), an sspush or sspopchk whose ssp is tagged in bits 63:48 raises a store/AMO page fault with the tagged address in mtval. SSAMOSWAP through the same tagged pointer is masked and succeeds.

The pointer masking spec (zpm.adoc) says:

When pointer masking is enabled, the ignore transformation will be applied to every explicit memory access (e.g., loads/stores, atomics operations, and floating point loads/stores).

SSPUSH and SSPOPCHK make explicit memory accesses at the address in ssp, so the transformation applies to them too. Spike, the Sail reference model and Whisper all mask these accesses.

In target/riscv/tcg/insn_trans/trans_rvzicfiss.c.inc, trans_sspush and trans_sspopchk load ssp and pass it straight to tcg_gen_qemu_st_tl/tcg_gen_qemu_ld_tl. The other memory instructions, including trans_ssamoswap_w/_d, form their address with get_address(), which applies the pointer mask (ctx->addr_xl, ctx->addr_signed). The SSPUSH/SSPOPCHK address should go through the same masking (ssp itself is updated with the unmasked value).

Steps to reproduce

  1. Unpack the attached archive and run make run (needs riscv64-unknown-elf-gcc; the Spike/Sail/Whisper rows are optional).
  2. sspm.S enters S-mode under Sv39 with menvcfg.SSE = 1 and menvcfg.PMM = 11. VA 0xC0000000 is a shadow-stack gigapage. It runs ssamoswap.d through a pointer tagged in bit 48, sspush ra with an untagged ssp, sspush ra with ssp tagged in bit 48, and sspopchk ra with the tagged ssp. M-mode records mcause/mtval for each.

QEMU 11.1.2:

case 0: mcause=00 mtval=0000000000000000
case 1: mcause=00 mtval=0000000000000000 ssp after=00000000c0003038
case 2: mcause=0f mtval=00010000c0003038 ssp after=00010000c0003040
case 3: mcause=0f mtval=00010000c0003040 ssp after=00010000c0003040

Spike (Sail 0.16 and Whisper also take no trap):

case 0: mcause=00 mtval=0000000000000000
case 1: mcause=00 mtval=0000000000000000 ssp after=00000000c0003038
case 2: mcause=00 mtval=0000000000000000 ssp after=00010000c0003038
case 3: mcause=00 mtval=0000000000000000 ssp after=00010000c0003040

Additional information

Found by the RISC-V architectural tests (riscv-arch-test act4: Ssnpm and Smnpm_S sweeps with PMLEN = 16 or bit-63 tags), which currently waive these tests on QEMU.

2026-10-26-qemu-sspush-pointer-masking.tar.gz