From c238acf3051405731dfcb45c9918fdf9cec0431c Mon Sep 17 00:00:00 2001 From: Mihawk <163614687+mihawk-99@users.noreply.github.com> Date: Thu, 8 Oct 2026 19:29:12 -0400 Subject: [PATCH] include: account for PS5 signal-frame padding before mcontext --- include/freebsd/sys/_ucontext.h | 2 + samples/test_signal_context/Makefile | 48 +++++++++++++++++++++ samples/test_signal_context/main.c | 64 ++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+) create mode 100644 samples/test_signal_context/Makefile create mode 100644 samples/test_signal_context/main.c diff --git a/include/freebsd/sys/_ucontext.h b/include/freebsd/sys/_ucontext.h index 7c13c366..bfc9369b 100644 --- a/include/freebsd/sys/_ucontext.h +++ b/include/freebsd/sys/_ucontext.h @@ -41,6 +41,8 @@ typedef struct __ucontext { * note: the union is not defined, though. */ __sigset_t uc_sigmask; + /* PS5 signal frames place uc_mcontext at offset 64 (observed on 12.09). */ + int __ps5_reserved[12]; mcontext_t uc_mcontext; struct __ucontext *uc_link; diff --git a/samples/test_signal_context/Makefile b/samples/test_signal_context/Makefile new file mode 100644 index 00000000..699bf923 --- /dev/null +++ b/samples/test_signal_context/Makefile @@ -0,0 +1,48 @@ +# Copyright (C) 2023 John Törnblom +# +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not see +# . + +PS5_HOST ?= ps5 +PS5_PORT ?= 9021 + +ifdef PS5_PAYLOAD_SDK + include $(PS5_PAYLOAD_SDK)/toolchain/prospero.mk +else + $(error PS5_PAYLOAD_SDK is undefined) +endif + +ELF := test_signal_context.elf + +CFLAGS := -Wall -Werror -g + +all: $(ELF) + +$(ELF): main.c + $(CC) $(CFLAGS) -o $@ $^ + +clean: + rm -f $(ELF) + +test: $(ELF) + $(PS5_DEPLOY) -h $(PS5_HOST) -p $(PS5_PORT) $^ + +debug: $(ELF) + gdb-multiarch \ + -ex "set architecture i386:x86-64" \ + -ex "target extended-remote $(PS5_HOST):2159" \ + -ex "file $(ELF)" \ + -ex "remote put $(ELF) /data/$(ELF)" \ + -ex "set remote exec-file /data/$(ELF)" \ + -ex "start" diff --git a/samples/test_signal_context/main.c b/samples/test_signal_context/main.c new file mode 100644 index 00000000..ba33ccc4 --- /dev/null +++ b/samples/test_signal_context/main.c @@ -0,0 +1,64 @@ +/* Copyright (C) 2026 Mihawk + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include + +_Static_assert(offsetof(ucontext_t, uc_mcontext) == 64, "PS5 mcontext offset"); +_Static_assert(offsetof(ucontext_t, uc_mcontext.mc_rip) == 224, "PS5 RIP offset"); + +static sigjmp_buf escape; +static volatile uint64_t saved_rax; +static volatile uintptr_t saved_rip; +extern void context_trigger(void); +extern const char context_fault[]; + +/* UD2 gives a known faulting instruction and a caller-saved register marker. */ +__asm__(".text\n" + ".global context_trigger\n" + ".type context_trigger,@function\n" + "context_trigger:\n" + "movabs $0x5a17000000001234, %rax\n" + ".global context_fault\n" + "context_fault:\n" + "ud2\n" + "ret\n"); + +static void +handle_fault(int signal_number, siginfo_t *info, void *opaque) { + const ucontext_t *context = opaque; + (void)signal_number; + (void)info; + saved_rax = context->uc_mcontext.mc_rax; + saved_rip = context->uc_mcontext.mc_rip; + siglongjmp(escape, 1); +} + +int +main(void) { + struct sigaction action = {0}, previous; + action.sa_sigaction = handle_fault; + action.sa_flags = SA_SIGINFO; + sigemptyset(&action.sa_mask); + if(sigaction(SIGILL, &action, &previous) != 0) { + perror("sigaction"); + return 1; + } + if(sigsetjmp(escape, 1) == 0) { + context_trigger(); + } + if(sigaction(SIGILL, &previous, NULL) != 0) { + perror("restore sigaction"); + return 1; + } + printf("RAX=%016" PRIx64 " RIP=%" PRIxPTR " expected RIP=%" PRIxPTR "\n", + saved_rax, saved_rip, (uintptr_t)context_fault); + return saved_rax != UINT64_C(0x5a17000000001234) || + saved_rip != (uintptr_t)context_fault; +}