From 7d488fec76c8fc36b2d6d058e172fc37f72f9787 Mon Sep 17 00:00:00 2001 From: Yelninei Date: Fri, 17 Oct 2025 18:57:58 +0100 Subject: [PATCH] gnu: glibc/hurd: Add patches from glibc 2.42. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * gnu/packages/base.scm (glibc/hurd): Remove intr-msg-clobber.patch, add signal-fpe-exception.patch and xstate.patch. gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch, gnu/packages/patches/glibc-hurd-xstate.patch : New patches * gnu/local.mk (dist_patch_DATA): Register them. Change-Id: Ib5b38f1fb8b43d76ec236232be8ff7552dad7422 Signed-off-by: Ludovic Courtès --- gnu/local.mk | 2 + gnu/packages/base.scm | 13 +- .../glibc-hurd-signal-fpe-exception.patch | 67 ++ gnu/packages/patches/glibc-hurd-xstate.patch | 813 ++++++++++++++++++ 4 files changed, 894 insertions(+), 1 deletion(-) create mode 100644 gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch create mode 100644 gnu/packages/patches/glibc-hurd-xstate.patch diff --git a/gnu/local.mk b/gnu/local.mk index ec0efb1a0a9ee767417d9837f78a00e984ccf662..82a6577562be04160fcc6c7d61364ee05d1cbc60 100644 --- a/gnu/local.mk +++ b/gnu/local.mk @@ -1465,10 +1465,12 @@ dist_patch_DATA = \ %D%/packages/patches/glibc-hurd-gettyent.patch \ %D%/packages/patches/glibc-hurd-mach-print.patch \ %D%/packages/patches/glibc-hurd-pthread_setcancelstate.patch \ + %D%/packages/patches/glibc-hurd-signal-fpe-exception.patch \ %D%/packages/patches/glibc-hurd-signal-sa-siginfo.patch \ %D%/packages/patches/glibc-hurd-2.41-pthread-once.patch \ %D%/packages/patches/glibc-hurd-2.41-pthread-sigmask.patch \ %D%/packages/patches/glibc-hurd-2.41-symlink.patch \ + %D%/packages/patches/glibc-hurd-xstate.patch \ %D%/packages/patches/glibc-hurd64-gcc-14.2-tls-bug.patch \ %D%/packages/patches/glibc-hurd64-intr-msg-clobber.patch \ %D%/packages/patches/glibc-ldd-powerpc.patch \ diff --git a/gnu/packages/base.scm b/gnu/packages/base.scm index 26096743192a887cfdd9cd2f425e3e5f14448394..79810bccd10b74dd4c0eb390425efabc6fb62e09 100644 --- a/gnu/packages/base.scm +++ b/gnu/packages/base.scm @@ -1548,7 +1548,18 @@ variety of options. It is an alternative to the shell \"type\" built-in command.") (license gpl3+))) ; some files are under GPLv2+ -(define-public glibc/hurd glibc) +(define-public glibc/hurd + (hidden-package + (package/inherit glibc + (source + (origin + (inherit (package-source glibc)) + (patches + (append (map search-patch + (delete "glibc-hurd64-intr-msg-clobber.patch" %glibc-patches)) + (search-patches + "glibc-hurd-signal-fpe-exception.patch" + "glibc-hurd-xstate.patch")))))))) (define-public glibc/hurd-headers (package/inherit glibc/hurd diff --git a/gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch b/gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch new file mode 100644 index 0000000000000000000000000000000000000000..fefd9f693c0f48c980f731bf1fb0794c2a19888c --- /dev/null +++ b/gnu/packages/patches/glibc-hurd-signal-fpe-exception.patch @@ -0,0 +1,67 @@ +Cherry picked from glibc-2.42 + +0d99714680 * hurd: Clear FP exceptions before calling signal handler + +From 0d9971468087c77e8727638123ae4aa60724e0ff Mon Sep 17 00:00:00 2001 +From: Samuel Thibault +Date: Mon, 21 Apr 2025 19:58:04 +0200 +Subject: [PATCH] hurd: Clear FP exceptions before calling signal handler + +This avoids SIGFPE handlers (or code longjmp-ed to) getting disturbed by the +exception that generated it. + +Note: gcc's unwinding depends on the rpc_wait_trampoline/trampoline exact +code, so we here avoid breaking it. +--- + sysdeps/mach/hurd/x86/trampoline.c | 14 ++++++++++---- + 1 file changed, 10 insertions(+), 4 deletions(-) + +diff --git a/sysdeps/mach/hurd/x86/trampoline.c b/sysdeps/mach/hurd/x86/trampoline.c +index 02510b178c..6f23c567da 100644 +--- a/sysdeps/mach/hurd/x86/trampoline.c ++++ b/sysdeps/mach/hurd/x86/trampoline.c +@@ -461,7 +461,10 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action + - in gdb: gdb/i386-gnu-tdep.c gnu_sigtramp_code. */ + + #ifdef __x86_64__ +-asm ("rpc_wait_trampoline:\n" ++asm ("trampoline:\n" ++ "fnclex\n" /* Clear any pending exception. */ ++ "jmp _trampoline\n" ++ "rpc_wait_trampoline:\n" + /* This is the entry point when we have an RPC reply message to receive + before running the handler. The MACH_MSG_SEND bit has already been + cleared in the OPTION argument in our %rsi. The interrupted user +@@ -480,7 +483,7 @@ asm ("rpc_wait_trampoline:\n" + /* Switch to the signal stack. */ + "movq %rbx, %rsp\n" + +- "trampoline:\n" ++ "_trampoline:\n" + /* Entry point for running the handler normally. The arguments to the + handler function are on the top of the stack, same as in the i386 + version: +@@ -506,7 +509,10 @@ asm ("rpc_wait_trampoline:\n" + "movq 16(%rsp), %rdi\n" + "ret"); + #else +-asm ("rpc_wait_trampoline:\n"); ++asm ("trampoline:\n" ++ "fnclex\n" /* Clear any pending exception. */ ++ "jmp _trampoline\n" ++ "rpc_wait_trampoline:\n"); + /* This is the entry point when we have an RPC reply message to receive + before running the handler. The MACH_MSG_SEND bit has already been + cleared in the OPTION argument on our stack. The interrupted user +@@ -526,7 +532,7 @@ asm (/* Retry the interrupted mach_msg system call. */ + /* Switch to the signal stack. */ + "movl %ebx, %esp\n"); + +- asm ("trampoline:\n"); ++asm ("_trampoline:\n"); + /* Entry point for running the handler normally. The arguments to the + handler function are already on the top of the stack: + +-- +2.51.0 + diff --git a/gnu/packages/patches/glibc-hurd-xstate.patch b/gnu/packages/patches/glibc-hurd-xstate.patch new file mode 100644 index 0000000000000000000000000000000000000000..f46a84adb218a472c227cb04f4b59629138ec21a --- /dev/null +++ b/gnu/packages/patches/glibc-hurd-xstate.patch @@ -0,0 +1,813 @@ +Cherry picked from glibc 2.42 + +6d6a6e2dd2 * hurd: save xstate during signal handling +8d54b428cf * hurd: Do not restore xstate when it is not initialized +0f2df19d95 * hurd: Do not check for xstate level if it was not initialized + +From 6d6a6e2dd2133908e3f5cb8a2ed817ccb2a0bb06 Mon Sep 17 00:00:00 2001 +From: Luca Dariz +Date: Wed, 19 Mar 2025 18:11:18 +0100 +Subject: [PATCH] hurd: save xstate during signal handling + +* hurd/Makefile: add new tests +* hurd/test-sig-rpc-interrupted.c: check xstate save and restore in + the case where a signal is delivered to a thread which is waiting + for an rpc. This test implements the rpc interruption protocol used + by the hurd servers. It was so far passing on Debian thanks to the + local-intr-msg-clobber.diff patch, which is now obsolete. +* hurd/test-sig-xstate.c: check xstate save and restore in the case + where a signal is delivered to a running thread, making sure that + the xstate is modified in the signal handler. +* hurd/test-xstate.h: add helpers to test xstate +* sysdeps/mach/hurd/i386/bits/sigcontext.h: add xstate to the + sigcontext structure. ++ sysdeps/mach/hurd/i386/sigreturn.c: restore xstate from the saved + context +* sysdeps/mach/hurd/x86/trampoline.c: save xstate if + supported. Otherwise we fall back to the previous behaviour of + ignoring xstate. +* sysdeps/mach/hurd/x86_64/bits/sigcontext.h: add xstate to the + sigcontext structure. +* sysdeps/mach/hurd/x86_64/sigreturn.c: restore xstate from the saved + context + +Signed-off-by: Luca Dariz +Signed-off-by: Samuel Thibault +Message-ID: <20250319171118.142163-1-luca@orpolo.org> +--- + hurd/Makefile | 5 + + hurd/test-sig-rpc-interrupted.c | 185 +++++++++++++++++++++ + hurd/test-sig-xstate.c | 94 +++++++++++ + hurd/test-xstate.h | 40 +++++ + sysdeps/mach/hurd/i386/bits/sigcontext.h | 2 + + sysdeps/mach/hurd/i386/sigreturn.c | 32 +++- + sysdeps/mach/hurd/x86/trampoline.c | 91 ++++++++-- + sysdeps/mach/hurd/x86_64/bits/sigcontext.h | 2 + + sysdeps/mach/hurd/x86_64/sigreturn.c | 32 +++- + 9 files changed, 458 insertions(+), 25 deletions(-) + create mode 100644 hurd/test-sig-rpc-interrupted.c + create mode 100644 hurd/test-sig-xstate.c + create mode 100644 hurd/test-xstate.h + +diff --git a/hurd/Makefile b/hurd/Makefile +index cf70b8c65c..cbc3c23b1f 100644 +--- a/hurd/Makefile ++++ b/hurd/Makefile +@@ -19,6 +19,11 @@ subdir := hurd + + include ../Makeconfig + ++tests := test-sig-xstate \ ++ test-sig-rpc-interrupted ++$(objpfx)test-sig-xstate: $(shared-thread-library) ++$(objpfx)test-sig-rpc-interrupted: $(shared-thread-library) $(objdir)/hurd/libhurduser.so ++ + headers = \ + $(interface-headers) \ + hurd.h \ +diff --git a/hurd/test-sig-rpc-interrupted.c b/hurd/test-sig-rpc-interrupted.c +new file mode 100644 +index 0000000000..a89d70e5a4 +--- /dev/null ++++ b/hurd/test-sig-rpc-interrupted.c +@@ -0,0 +1,185 @@ ++/* Test the state save/restore procedures during signal handling when an ++ interruptible RPC is restarted. ++ ++ Copyright (C) 2024 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "test-xstate.h" ++ ++void handler (int signum, siginfo_t *info, void *context) ++{ ++ printf ("signal %d setting a different CPU state\n", signum); ++ char buf3[XSTATE_BUFFER_SIZE]; ++ memset (buf3, 0x77, XSTATE_BUFFER_SIZE); ++ SET_XSTATE (buf3); ++} ++ ++static const mach_msg_type_t RetCodeCheck = { ++ .msgt_name = (unsigned char) MACH_MSG_TYPE_INTEGER_32, ++ .msgt_size = 32, ++ .msgt_number = 1, ++ .msgt_inline = TRUE, ++ .msgt_longform = FALSE, ++ .msgt_deallocate = FALSE, ++ .msgt_unused = 0 ++}; ++ ++ ++/* Helper thread to simulate a proper RPC interruption during dignal handling */ ++void* fake_interruptor (void *arg) ++{ ++ int err; ++ sigset_t ss; ++ TEST_COMPARE (sigemptyset (&ss), 0); ++ TEST_COMPARE (sigaddset (&ss, SIGUSR1), 0); ++ TEST_COMPARE (sigprocmask (SIG_BLOCK, &ss, NULL), 0); ++ ++ struct { ++ mach_msg_header_t Head; ++ } request; ++ mach_port_t rxport = *((mach_port_t*)arg); ++ err = mach_msg (&request.Head, MACH_RCV_MSG, 0, sizeof (request), rxport, ++ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); ++ TEST_COMPARE (err, MACH_MSG_SUCCESS); ++ TEST_COMPARE (request.Head.msgh_bits, 0x1112); ++ TEST_COMPARE (request.Head.msgh_size, sizeof (request.Head)); ++ TEST_COMPARE (request.Head.msgh_id, 33000); ++ ++ mig_reply_header_t reply; ++ reply.Head = request.Head; ++ reply.Head.msgh_id += 100; ++ reply.RetCodeType = RetCodeCheck; ++ reply.RetCode = KERN_SUCCESS; ++ err = mach_msg (&reply.Head, MACH_SEND_MSG, sizeof (reply), 0, MACH_PORT_NULL, ++ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); ++ TEST_COMPARE (err, MACH_MSG_SUCCESS); ++ ++ return NULL; ++} ++ ++ ++/* Helper thread to send a signal to the main thread in the middle of ++ * an interruptible rpc */ ++void* signal_sender (void *arg) ++{ ++ int err; ++ sigset_t ss; ++ TEST_COMPARE (sigemptyset (&ss), 0); ++ TEST_COMPARE (sigaddset (&ss, SIGUSR1), 0); ++ TEST_COMPARE (sigprocmask (SIG_BLOCK, &ss, NULL), 0); ++ ++ /* Receive the first request, we won't answer to this. */ ++ struct { ++ mach_msg_header_t head; ++ char data[64]; ++ } m1, m2; ++ mach_port_t rxport = *((mach_port_t*)arg); ++ memset (&m1, 0, sizeof (m1)); ++ memset (&m2, 0, sizeof (m2)); ++ err = mach_msg (&m1.head, MACH_RCV_MSG, 0, sizeof (m1), rxport, ++ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); ++ TEST_COMPARE (err, MACH_MSG_SUCCESS); ++ ++ /* interrupt the ongoing rpc with a signal, using the ++ * interruptible rpc protocol */ ++ pthread_t thintr = xpthread_create (NULL, fake_interruptor, arg); ++ TEST_COMPARE (kill (getpid (), SIGUSR1), 0); ++ xpthread_join (thintr); ++ ++ /* Complete the interruption by sending EINTR */ ++ mig_reply_header_t reply; ++ reply.Head = m1.head; ++ reply.Head.msgh_id += 100; ++ reply.RetCodeType = RetCodeCheck; ++ reply.RetCode = EINTR; ++ err = mach_msg (&reply.Head, MACH_SEND_MSG, sizeof (reply), 0, MACH_PORT_NULL, ++ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); ++ TEST_COMPARE (err, MACH_MSG_SUCCESS); ++ ++ /* Receive the retried rpc, and check that it has the same payload ++ * as the first one. Port names might still be different. */ ++ err = mach_msg (&m2.head, MACH_RCV_MSG, 0, sizeof (m2), rxport, ++ MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); ++ TEST_COMPARE (m1.head.msgh_bits, m2.head.msgh_bits); ++ TEST_COMPARE (m1.head.msgh_size, m2.head.msgh_size); ++ TEST_COMPARE (m1.head.msgh_id, m2.head.msgh_id); ++ TEST_COMPARE_BLOB (m1.data, sizeof (m1.data), m2.data, sizeof (m2.data)); ++ ++ /* And finally make the rpc succeed by sending a valid reply */ ++ err = io_read_reply (m2.head.msgh_remote_port, MACH_MSG_TYPE_MOVE_SEND_ONCE, ++ KERN_SUCCESS, NULL, 0); ++ TEST_COMPARE (err, MACH_MSG_SUCCESS); ++ ++ return NULL; ++} ++ ++ ++static int do_test (void) ++{ ++#if ! XSTATE_HELPERS_SUPPORTED ++ FAIL_UNSUPPORTED ("Test not supported on this arch."); ++#endif ++ ++ /* Setup signal handling; we need to handle the signal in the main ++ * thread, the other ones will explicitely block SIGUSR1. */ ++ struct sigaction act = { 0 }; ++ act.sa_flags = SA_RESTART; ++ act.sa_sigaction = &handler; ++ TEST_COMPARE (sigaction (SIGUSR1, &act, NULL), 0); ++ ++ mach_port_t fakeio; ++ int err; ++ err = mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &fakeio); ++ TEST_COMPARE (err, MACH_MSG_SUCCESS); ++ ++ err = mach_port_insert_right (mach_task_self (), fakeio, fakeio, ++ MACH_MSG_TYPE_MAKE_SEND); ++ TEST_COMPARE (err, MACH_MSG_SUCCESS); ++ ++ pthread_t thsender = xpthread_create (NULL, signal_sender, &fakeio); ++ ++ char *buf; ++ mach_msg_type_number_t n; ++ TEST_COMPARE (io_read (fakeio, &buf, &n, 1, 2), 0); ++ ++ xpthread_join (thsender); ++ return EXIT_SUCCESS; ++} ++ ++#include +diff --git a/hurd/test-sig-xstate.c b/hurd/test-sig-xstate.c +new file mode 100644 +index 0000000000..0a68a44fd7 +--- /dev/null ++++ b/hurd/test-sig-xstate.c +@@ -0,0 +1,94 @@ ++/* Test the state save/restore procedures during signal handling. ++ ++ Copyright (C) 2025 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include "test-xstate.h" ++ ++static volatile bool loopflag = true; ++ ++void handler (int signum, siginfo_t *info, void *context) ++{ ++ char buf3[XSTATE_BUFFER_SIZE]; ++ memset (buf3, 0x77, XSTATE_BUFFER_SIZE); ++ SET_XSTATE (buf3); ++ printf ("signal %d setting a different CPU state\n", signum); ++ loopflag = false; ++} ++ ++/* Helper thread to send a signal to the main thread */ ++void* signal_sender (void *arg) ++{ ++ sigset_t ss; ++ assert (! sigemptyset (&ss)); ++ assert (! sigaddset (&ss, SIGUSR1)); ++ assert (! sigprocmask (SIG_BLOCK, &ss, NULL)); ++ ++ TEST_COMPARE (kill (getpid (), SIGUSR1), 0); ++ ++ return NULL; ++} ++ ++static int do_test (void) ++{ ++#if ! XSTATE_HELPERS_SUPPORTED ++ FAIL_UNSUPPORTED ("Test not supported on this arch."); ++#endif ++ ++ struct sigaction act = { 0 }; ++ act.sa_sigaction = &handler; ++ TEST_COMPARE (sigaction (SIGUSR1, &act, NULL), 0); ++ ++ pthread_t thsender = xpthread_create (NULL, signal_sender, NULL); ++ ++ char buf1[XSTATE_BUFFER_SIZE], buf2[XSTATE_BUFFER_SIZE]; ++ memset (buf1, 0x33, XSTATE_BUFFER_SIZE); ++ ++ SET_XSTATE (buf1); ++ ++ while (loopflag) ++ ; ++ ++ GET_XSTATE (buf2); ++ TEST_COMPARE_BLOB (buf1, sizeof (buf1), buf2, sizeof (buf2)); ++ ++ xpthread_join (thsender); ++ return EXIT_SUCCESS; ++} ++ ++#include +diff --git a/hurd/test-xstate.h b/hurd/test-xstate.h +new file mode 100644 +index 0000000000..a8185dcb07 +--- /dev/null ++++ b/hurd/test-xstate.h +@@ -0,0 +1,40 @@ ++/* Helpers to test XSTATE during signal handling ++ ++ Copyright (C) 2025 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library 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 ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#ifndef _TEST_XSTATE_H ++#define _TEST_XSTATE_H ++ ++#if defined __x86_64__ || defined __i386__ ++#define XSTATE_HELPERS_SUPPORTED 1 ++#define XSTATE_BUFFER_SIZE 16 ++#define SET_XSTATE(b) do { \ ++ asm volatile ("movups (%0),%%xmm0" :: "r" (b)); \ ++ } while (0) ++ ++#define GET_XSTATE(b) do { \ ++ asm volatile ("movups %%xmm0,(%0)" :: "r" (b)); \ ++ } while (0) ++ ++#else ++#define XSTATE_HELPERS_SUPPORTED 0 ++#define XSTATE_BUFFER_SIZE 1 ++#define SET_XSTATE(b) ++#endif ++ ++#endif /* _TEST_XSTATE_H */ +diff --git a/sysdeps/mach/hurd/i386/bits/sigcontext.h b/sysdeps/mach/hurd/i386/bits/sigcontext.h +index 6e5e220e9d..c44e4deac6 100644 +--- a/sysdeps/mach/hurd/i386/bits/sigcontext.h ++++ b/sysdeps/mach/hurd/i386/bits/sigcontext.h +@@ -88,6 +88,8 @@ struct sigcontext + struct i386_fp_save sc_fpsave; + struct i386_fp_regs sc_fpregs; + int sc_fpexcsr; /* FPSR including exception bits. */ ++ ++ struct i386_xfloat_state *xstate; + }; + + /* Traditional BSD names for some members. */ +diff --git a/sysdeps/mach/hurd/i386/sigreturn.c b/sysdeps/mach/hurd/i386/sigreturn.c +index ce8df8d02b..37fa984070 100644 +--- a/sysdeps/mach/hurd/i386/sigreturn.c ++++ b/sysdeps/mach/hurd/i386/sigreturn.c +@@ -21,6 +21,8 @@ + #include + #include + ++#include ++ + /* This is run on the thread stack after restoring it, to be able to + unlock SS off sigstack. */ + static void +@@ -123,10 +125,32 @@ __sigreturn (struct sigcontext *scp) + if (scp->sc_onstack) + ss->sigaltstack.ss_flags &= ~SS_ONSTACK; + +- if (scp->sc_fpused) +- /* Restore the FPU state. Mach conveniently stores the state +- in the format the i387 `frstor' instruction uses to restore it. */ +- asm volatile ("frstor %0" : : "m" (scp->sc_fpsave)); ++#ifdef i386_XFLOAT_STATE ++ if ((scp->xstate) && (scp->xstate->initialized)) ++ { ++ unsigned eax, ebx, ecx, edx; ++ __cpuid_count(0xd, 0, eax, ebx, ecx, edx); ++ switch (scp->xstate->fp_save_kind) ++ { ++ case 0: // FNSAVE ++ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state)); ++ break; ++ case 1: // FXSAVE ++ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES ++ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ } ++ } ++ else ++#endif ++ if (scp->sc_fpused) ++ /* Restore the FPU state. Mach conveniently stores the state ++ in the format the i387 `frstor' instruction uses to restore it. */ ++ asm volatile ("frstor %0" : : "m" (scp->sc_fpsave)); + + { + /* There are convenient instructions to pop state off the stack, so we +diff --git a/sysdeps/mach/hurd/x86/trampoline.c b/sysdeps/mach/hurd/x86/trampoline.c +index 8e2890f8c5..db756e8a1f 100644 +--- a/sysdeps/mach/hurd/x86/trampoline.c ++++ b/sysdeps/mach/hurd/x86/trampoline.c +@@ -26,7 +26,11 @@ + #include "hurdfault.h" + #include + #include +- ++#ifdef __x86_64__ ++#include ++#else ++#include ++#endif + + /* Fill in a siginfo_t structure for SA_SIGINFO-enabled handlers. */ + static void fill_siginfo (siginfo_t *si, int signo, +@@ -106,6 +110,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action + void firewall (void); + void *sigsp; + struct sigcontext *scp; ++ vm_size_t xstate_size; + struct + { + union +@@ -145,6 +150,14 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action + struct hurd_userlink link; + ucontext_t ucontext; + siginfo_t siginfo; ++#ifdef __x86_64__ ++ char _pad2[56]; ++#else ++ char _pad2[20]; ++#endif ++ char xstate[]; ++ /* Don't add anything after xstate, as it's dynamically ++ sized. */ + } *stackframe; + + #ifdef __x86_64__ +@@ -170,6 +183,17 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action + if (! machine_get_basic_state (ss->thread, state)) + return NULL; + ++ /* Initialize the size of the CPU extended state, to be saved during ++ * signal handling */ ++#ifdef i386_XFLOAT_STATE ++ _Static_assert ((sizeof(*stackframe) + sizeof(struct i386_xfloat_state)) % 64 == 0, ++ "stackframe size must be multiple of 64-byte minus " ++ "sizeof(struct i386_xfloat_state), please adjust _pad2"); ++ ++ if (__i386_get_xstate_size(__mach_host_self(), &xstate_size)) ++#endif ++ xstate_size = 0; ++ + /* Save the original SP in the gratuitous `esp' slot. + We may need to reset the SP (the `uesp' slot) to avoid clobbering an + interrupted RPC frame. */ +@@ -196,14 +220,21 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action + #endif + } + +- /* Push the arguments to call `trampoline' on the stack. */ +- sigsp -= sizeof (*stackframe); +-#ifdef __x86_64__ +- /* Align SP at 16 bytes. Coupled with the fact that sigreturn_addr is +- 16-byte aligned within the stackframe struct, this ensures that it ends +- up on a 16-byte aligned address, as required by the ABI. */ +- sigsp = (void *) ((uintptr_t) sigsp & ~15UL); +-#endif ++ /* Push the arguments to call `trampoline' on the stack. ++ * The extended state might have a variable size depending on the platform, ++ * so we dynamically allocate it on the stack frame.*/ ++ sigsp -= sizeof (*stackframe) + xstate_size; ++ ++ /* Align SP at 64 bytes. This is needed for two reasons: ++ * - sigreturn_addr is 16-byte aligned within the stackframe ++ * struct, and this ensures that it ends up on a 16-byte aligned ++ * address, as required by the ABI. ++ * - the XSAVE state needs to be aligned at 64 bytes (on both i386 and ++ * x86_64), so we align the stackframe also at 64 bytes and add the ++ * required padding at the end, see the _pad2 field. ++ */ ++ sigsp = (void *) ((uintptr_t) sigsp & ~63UL); ++ + stackframe = sigsp; + + if (_hurdsig_catch_memory_fault (stackframe)) +@@ -248,14 +279,40 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action + memcpy (&scp->sc_i386_thread_state, + &state->basic, sizeof (state->basic)); + +- /* struct sigcontext is laid out so that starting at sc_fpkind mimics +- a struct i386_float_state. */ +- _Static_assert (offsetof (struct sigcontext, sc_i386_float_state) +- % __alignof__ (struct i386_float_state) == 0, +- "sc_i386_float_state layout mismatch"); +- ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE, +- &state->fpu, &scp->sc_i386_float_state, +- sizeof (state->fpu)); ++ scp->xstate = NULL; ++#ifdef i386_XFLOAT_STATE ++ if (xstate_size > 0) ++ { ++ mach_msg_type_number_t got = (xstate_size / sizeof (int)); ++ ++ ok = (! __thread_get_state (ss->thread, i386_XFLOAT_STATE, ++ (thread_state_t) stackframe->xstate, &got) ++ && got == (xstate_size / sizeof (int))); ++ ++ if (((struct i386_xfloat_state*) stackframe->xstate)->fp_save_kind > 5) ++ /* We support up to XSAVES */ ++ ok = 0; ++ ++ if (ok) ++ { ++ scp->xstate = (struct i386_xfloat_state*) stackframe->xstate; ++ assert((uintptr_t)scp->xstate->hw_state % 64 == 0); ++ } ++ } ++ else ++#endif ++ ok = 0; ++ if (!ok) ++ { ++ /* struct sigcontext is laid out so that starting at sc_fpkind mimics ++ a struct i386_float_state. */ ++ _Static_assert (offsetof (struct sigcontext, sc_i386_float_state) ++ % __alignof__ (struct i386_float_state) == 0, ++ "sc_i386_float_state layout mismatch"); ++ ok = machine_get_state (ss->thread, state, i386_FLOAT_STATE, ++ &state->fpu, &scp->sc_i386_float_state, ++ sizeof (state->fpu)); ++ } + + /* Set up the arguments for the signal handler. */ + stackframe->signo = signo; +diff --git a/sysdeps/mach/hurd/x86_64/bits/sigcontext.h b/sysdeps/mach/hurd/x86_64/bits/sigcontext.h +index 7bac881176..d83795fcbc 100644 +--- a/sysdeps/mach/hurd/x86_64/bits/sigcontext.h ++++ b/sysdeps/mach/hurd/x86_64/bits/sigcontext.h +@@ -96,6 +96,8 @@ struct sigcontext + struct i386_fp_save sc_fpsave; + struct i386_fp_regs sc_fpregs; + int sc_fpexcsr; /* FPSR including exception bits. */ ++ ++ struct i386_xfloat_state *xstate; + }; + + /* Traditional BSD names for some members. */ +diff --git a/sysdeps/mach/hurd/x86_64/sigreturn.c b/sysdeps/mach/hurd/x86_64/sigreturn.c +index 81a2d3ba74..dff8e76dc8 100644 +--- a/sysdeps/mach/hurd/x86_64/sigreturn.c ++++ b/sysdeps/mach/hurd/x86_64/sigreturn.c +@@ -20,6 +20,8 @@ + #include + #include + ++#include ++ + /* This is run on the thread stack after restoring it, to be able to + unlock SS off sigstack. */ + void +@@ -116,10 +118,32 @@ __sigreturn (struct sigcontext *scp) + if (scp->sc_onstack) + ss->sigaltstack.ss_flags &= ~SS_ONSTACK; + +- if (scp->sc_fpused) +- /* Restore the FPU state. Mach conveniently stores the state +- in the format the i387 `frstor' instruction uses to restore it. */ +- asm volatile ("frstor %0" : : "m" (scp->sc_fpsave)); ++#ifdef i386_XFLOAT_STATE ++ if ((scp->xstate) && (scp->xstate->initialized)) ++ { ++ unsigned eax, ebx, ecx, edx; ++ __cpuid_count(0xd, 0, eax, ebx, ecx, edx); ++ switch (scp->xstate->fp_save_kind) ++ { ++ case 0: // FNSAVE ++ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state)); ++ break; ++ case 1: // FXSAVE ++ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES ++ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ } ++ } ++ else ++#endif ++ if (scp->sc_fpused) ++ /* Restore the FPU state. Mach conveniently stores the state ++ in the format the i387 `frstor' instruction uses to restore it. */ ++ asm volatile ("frstor %0" : : "m" (scp->sc_fpsave)); + + /* Copy the registers onto the user's stack, to be able to release the + altstack (by unlocking sigstate). Note that unless an altstack is used, +-- +2.51.0 + +From 8d54b428cfe98c21049f94c8af3bf302e44091e9 Mon Sep 17 00:00:00 2001 +From: Samuel Thibault +Date: Mon, 21 Apr 2025 19:42:27 +0200 +Subject: [PATCH] hurd: Do not restore xstate when it is not initialized + +If the process has never used fp before getting a signal, xstate is set +(and thus the x87 state is not initialized) but xstate->initialized is still +0, and we should not restore anything. +--- + sysdeps/mach/hurd/i386/sigreturn.c | 37 +++++++++++++++------------- + sysdeps/mach/hurd/x86_64/sigreturn.c | 37 +++++++++++++++------------- + 2 files changed, 40 insertions(+), 34 deletions(-) + +diff --git a/sysdeps/mach/hurd/i386/sigreturn.c b/sysdeps/mach/hurd/i386/sigreturn.c +index 37fa984070..dc57d6122c 100644 +--- a/sysdeps/mach/hurd/i386/sigreturn.c ++++ b/sysdeps/mach/hurd/i386/sigreturn.c +@@ -126,24 +126,27 @@ __sigreturn (struct sigcontext *scp) + ss->sigaltstack.ss_flags &= ~SS_ONSTACK; + + #ifdef i386_XFLOAT_STATE +- if ((scp->xstate) && (scp->xstate->initialized)) ++ if (scp->xstate) + { +- unsigned eax, ebx, ecx, edx; +- __cpuid_count(0xd, 0, eax, ebx, ecx, edx); +- switch (scp->xstate->fp_save_kind) +- { +- case 0: // FNSAVE +- asm volatile("frstor %0" : : "m" (scp->xstate->hw_state)); +- break; +- case 1: // FXSAVE +- asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \ +- "a" (eax), "d" (edx)); +- break; +- default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES +- asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \ +- "a" (eax), "d" (edx)); +- break; +- } ++ if (scp->xstate->initialized) ++ { ++ unsigned eax, ebx, ecx, edx; ++ __cpuid_count(0xd, 0, eax, ebx, ecx, edx); ++ switch (scp->xstate->fp_save_kind) ++ { ++ case 0: // FNSAVE ++ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state)); ++ break; ++ case 1: // FXSAVE ++ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES ++ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ } ++ } + } + else + #endif +diff --git a/sysdeps/mach/hurd/x86_64/sigreturn.c b/sysdeps/mach/hurd/x86_64/sigreturn.c +index dff8e76dc8..773c00f86d 100644 +--- a/sysdeps/mach/hurd/x86_64/sigreturn.c ++++ b/sysdeps/mach/hurd/x86_64/sigreturn.c +@@ -119,24 +119,27 @@ __sigreturn (struct sigcontext *scp) + ss->sigaltstack.ss_flags &= ~SS_ONSTACK; + + #ifdef i386_XFLOAT_STATE +- if ((scp->xstate) && (scp->xstate->initialized)) ++ if (scp->xstate) + { +- unsigned eax, ebx, ecx, edx; +- __cpuid_count(0xd, 0, eax, ebx, ecx, edx); +- switch (scp->xstate->fp_save_kind) +- { +- case 0: // FNSAVE +- asm volatile("frstor %0" : : "m" (scp->xstate->hw_state)); +- break; +- case 1: // FXSAVE +- asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \ +- "a" (eax), "d" (edx)); +- break; +- default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES +- asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \ +- "a" (eax), "d" (edx)); +- break; +- } ++ if (scp->xstate->initialized) ++ { ++ unsigned eax, ebx, ecx, edx; ++ __cpuid_count(0xd, 0, eax, ebx, ecx, edx); ++ switch (scp->xstate->fp_save_kind) ++ { ++ case 0: // FNSAVE ++ asm volatile("frstor %0" : : "m" (scp->xstate->hw_state)); ++ break; ++ case 1: // FXSAVE ++ asm volatile("fxrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ default: // XSAVE, XSAVEOPT, XSAVEC, XSAVES ++ asm volatile("xrstor %0" : : "m" (scp->xstate->hw_state), \ ++ "a" (eax), "d" (edx)); ++ break; ++ } ++ } + } + else + #endif +-- +2.51.0 + +From 0f2df19d9535d234b31c65f84a6c67ac2e0bd027 Mon Sep 17 00:00:00 2001 +From: Samuel Thibault +Date: Mon, 21 Apr 2025 19:45:02 +0200 +Subject: [PATCH] hurd: Do not check for xstate level if it was not + initialized + +If __thread_get_state failed, there is no xstate level to check. +ok is 0 already and the memory exists, but better not read uninitialized +memory. +--- + sysdeps/mach/hurd/x86/trampoline.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/sysdeps/mach/hurd/x86/trampoline.c b/sysdeps/mach/hurd/x86/trampoline.c +index db756e8a1f..02510b178c 100644 +--- a/sysdeps/mach/hurd/x86/trampoline.c ++++ b/sysdeps/mach/hurd/x86/trampoline.c +@@ -289,7 +289,7 @@ _hurd_setup_sighandler (struct hurd_sigstate *ss, const struct sigaction *action + (thread_state_t) stackframe->xstate, &got) + && got == (xstate_size / sizeof (int))); + +- if (((struct i386_xfloat_state*) stackframe->xstate)->fp_save_kind > 5) ++ if (ok && ((struct i386_xfloat_state*) stackframe->xstate)->fp_save_kind > 5) + /* We support up to XSAVES */ + ok = 0; + +-- +2.51.0 +