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
- Unpack the attached archive and run
make run(needs riscv64-unknown-elf-gcc; the Spike/Sail/Whisper rows are optional). - 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.dthrough a pointer tagged in bit 48,sspush rawith an untagged ssp,sspush rawith ssp tagged in bit 48, andsspopchk rawith 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=00010000c0003040Spike (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=00010000c0003040Additional 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.