[PATCH 07/32] s390/traps: Handle nullifying program checks in get_trap_ip()

From: Heiko Carstens

Date: Thu Oct 01 2026 - 06:35:17 EST


get_trap_ip() always rewinds the PSW address by the instruction length
from the interruption code. For nullifying program checks the PSW
already points at the faulting instruction, so rewinding is wrong.

Add a nullified[] bitmap and return the unchanged PSW address for
nullifying cases, or if PIF_PSW_ADDR_ADJUSTED is set.

No functional change for existing callers, which only deal with
suppressing, terminating, or completing program checks.

Signed-off-by: Heiko Carstens <hca@xxxxxxxxxxxxx>
---
arch/s390/kernel/traps.c | 21 +++++++++++++--------
1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index bfcab4592299..5e63032a56db 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -26,6 +26,7 @@
#include <linux/entry-common.h>
#include <linux/kmsan.h>
#include <linux/bug.h>
+#include <asm/pgmcheck-defs.h>
#include <asm/entry-percpu.h>
#include <asm/asm-extable.h>
#include <asm/irqflags.h>
@@ -41,15 +42,19 @@ struct pgm_stat {

static DEFINE_PER_CPU_SHARED_ALIGNED(struct pgm_stat, pgm_stat);

-static inline void __user *get_trap_ip(struct pt_regs *regs)
+static inline unsigned long get_trap_ip(struct pt_regs *regs)
{
- unsigned long address;
+ static const unsigned long nullified[2] = { PGM_CHECKS_NULLIFIED };
+ psw_t psw = regs->psw;

+ if (test_pt_regs_flag(regs, PIF_PSW_ADDR_ADJUSTED))
+ return psw.addr;
if (regs->int_code & 0x200)
- address = current->thread.trap_tdb.data[3];
- else
- address = regs->psw.addr;
- return (void __user *)(address - (regs->int_code >> 16));
+ psw.addr = current->thread.trap_tdb.data[3];
+ if (test_bit(regs->int_code & PGM_INT_CODE_MASK, nullified))
+ return psw.addr;
+ psw.addr = __rewind_psw(psw, regs->int_code >> 16);
+ return psw.addr;
}

#ifdef CONFIG_GENERIC_BUG
@@ -62,7 +67,7 @@ int is_valid_bugaddr(unsigned long addr)
void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str)
{
if (user_mode(regs)) {
- force_sig_fault(si_signo, si_code, get_trap_ip(regs));
+ force_sig_fault(si_signo, si_code, (void __user *)get_trap_ip(regs));
report_user_fault(regs, si_signo, 0);
} else {
if (!fixup_exception(regs))
@@ -152,7 +157,7 @@ static void illegal_op(struct pt_regs *regs)
int signal = 0;
u16 opcode;

- location = get_trap_ip(regs);
+ location = (void __user *)get_trap_ip(regs);
if (user_mode(regs)) {
if (get_user(opcode, location))
return;
--
2.53.0