From 2bb6256576b038147105838f1f9e62a3bf9033c0 Mon Sep 17 00:00:00 2001 From: Max042004 Date: Thu, 9 Jul 2026 14:13:36 +0800 Subject: [PATCH] Preserve siginfo on HVC 9 faults The HVC 9 W^X path can fall back to SIGSEGV when the fault is not a legitimate permission toggle. signal_deliver_fault() installs the SA_SIGINFO handler registers and marks X8=2, but the shim always ran the W^X retry epilogue and restored the saved fault frame over them. Teach both HVC 9 return sites to honor the existing frame-drop marker, and clear X8 after successful W^X toggles so stale guest state cannot select that path. --- src/core/shim.S | 9 ++++++++- src/syscall/proc.c | 8 ++++++++ src/syscall/signal.c | 8 +++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/core/shim.S b/src/core/shim.S index 6d89038c..4c6a1b86 100644 --- a/src/core/shim.S +++ b/src/core/shim.S @@ -36,7 +36,10 @@ * shim restores X11 from the saved frame before * ERET so the EL0 caller never observes this hint.) * #7 MRS trap (host reads reg from ESR ISS; returns value in x0) - * #9 W^X toggle (x0=FAR, x1=type: 0=exec->RX, 1=write->RW) + * #9 W^X toggle (x0=FAR, x1=type: 0=exec->RX, 1=write->RW. + * On return, X8==0 means retry after W^X TLBI; + * X8==2 means host delivered a signal frame and the + * shim must drop the saved fault frame.) * #10 BRK from EL0 (SIGTRAP delivery / ptrace-stop; GPRs in frame) * #11 EL0 fault (SIGSEGV/SIGILL delivery; GPRs in frame. * On return the host sets X8 to the same TLBI @@ -1119,6 +1122,8 @@ handle_inst_abort: 1: mrs x0, far_el1 /* Faulting address */ mov x1, #0 /* Type 0 = instruction fault -> flip to RX */ hvc #9 + cmp x8, #2 + b.eq exec_drop_frame b tlbi_restore_eret /* handle_data_abort: EC=0x24: Data abort from lower EL @@ -1143,6 +1148,8 @@ handle_data_abort: mrs x0, far_el1 /* Faulting address */ mov x1, #1 /* Type 1 = write fault -> flip to RW */ hvc #9 + cmp x8, #2 + b.eq exec_drop_frame b tlbi_restore_eret /* handle_el0_fault: Deliver SIGSEGV/SIGILL for EL0 faults diff --git a/src/syscall/proc.c b/src/syscall/proc.c index 54a960b9..ac51750b 100644 --- a/src/syscall/proc.c +++ b/src/syscall/proc.c @@ -2065,8 +2065,16 @@ int vcpu_run_loop(hv_vcpu_t vcpu, * so the next syscall epilogue does not re-flush the W^X * page. cpu_tlbi_req is per-vCPU, so this only touches our * own slot -- concurrent vCPUs are unaffected. + * + * The HVC #9 shim now consumes X8 as a post-HVC marker: + * 0 means W^X succeeded and the shim should run the TLBI + * retry epilogue; 2 means signal_deliver_fault installed a + * handler frame and the shim must drop its saved frame. + * Clear X8 here so a guest's pre-fault X8 value cannot be + * misread as the frame-drop marker after a normal toggle. */ tlbi_request_clear(); + hv_vcpu_set_reg(vcpu, HV_REG_X8, 0); break; } diff --git a/src/syscall/signal.c b/src/syscall/signal.c index cb9fa1ca..d6f628a9 100644 --- a/src/syscall/signal.c +++ b/src/syscall/signal.c @@ -1735,9 +1735,11 @@ static int deliver_signal_locked(hv_vcpu_t vcpu, /* If delivery happens while returning from the syscall HVC path, the shim * still has the interrupted syscall frame on its EL1 stack. Tell it to drop * that frame so the handler PC/SP/LR/args installed above are not - * overwritten before ERET. Fault/BRK delivery paths ignore this marker. The - * EL0-preemption path resumes straight into the handler at EL0 with no shim - * frame to drop, so the marker is neither needed nor consulted. + * overwritten before ERET. HVC #9 fault fallback uses the same marker when + * it materializes a SIGSEGV frame; HVC #11 and BRK delivery paths do not + * consume it. The EL0-preemption path resumes straight into the handler at + * EL0 with no shim frame to drop, so the marker is neither needed nor + * consulted. */ if (!el0_preempt) hv_vcpu_set_reg(vcpu, HV_REG_X8, 2);