NetBSD Problem Report #60426

From bsiegert@netbsd.org  Wed Jul  8 09:22:36 2026
Return-Path: <bsiegert@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
	 client-signature RSA-PSS (2048 bits) client-digest SHA256)
	(Client CN "mail.netbsd.org", Issuer "YR2" (not verified))
	by mollari.NetBSD.org (Postfix) with ESMTPS id B75361A923A
	for <gnats-bugs@gnats.NetBSD.org>; Wed,  8 Jul 2026 09:22:36 +0000 (UTC)
Message-Id: <20260708092235.711B61A923F@mollari.NetBSD.org>
Date: Wed,  8 Jul 2026 09:22:35 +0000 (UTC)
From: bsiegert@netbsd.org
Reply-To: bsiegert@netbsd.org
To: gnats-bugs@NetBSD.org
Subject: Signal handler corrupts AVX (YMM) registers
X-Send-Pr-Version: 3.95
X-From4GNATS: "bsiegert@netbsd.org via gnats" <gnats-admin@NetBSD.org>

>Number:         60426
>Category:       kern
>Synopsis:       Signal handler corrupts AVX (YMM) registers
>Confidential:   no
>Severity:       critical
>Priority:       medium
>Responsible:    riastradh
>State:          needs-pullups
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Wed Jul 08 09:25:00 +0000 2026
>Closed-Date:    
>Last-Modified:  Sun Jul 19 16:00:02 +0000 2026
>Originator:     Benny Siegert
>Release:        NetBSD 10.1_STABLE
>Organization:
The NetBSD Foundation
>Environment:
NetBSD 10.1/amd64
Architecture: x86_64
Machine: amd64
>Description:
This is a bug reported to Go about memory corruption in the runtime:

https://github.com/golang/go/issues/80285
runtime: async preemption corrupts AVX (YMM) state on netbsd/amd64

> It seems NetBSD 10.1/amd64 does not preserve the upper 128 bits of AVX (YMM)
> registers across signal delivery.

For now, the workaround has been to disable AVX completely in Go on NetBSD/amd64.
>How-To-Repeat:
> Repro (no Go, ymmsig.c attached to the bug report in GitHub): park a canary
> in ymm0, spin comparing it, while another thread sends SIGUSR1 whose handler
> clobbers YMM. On sigreturn the upper YMM half is not restored - the compare
> fails with mask=0000ffff (low XMM intact, high YMM lost). Clean on Linux
> (>1e9 iterations). Observed on QEMU -enable-kvm -cpu host (AMD Zen2 host,
> AVX2, no AVX-512), go-independent. This corrupts any AVX code interrupted by
> a signal (e.g. Go async preemption interrupting memmove).
>Fix:
Yes please!

>Release-Note:

>Audit-Trail:

Responsible-Changed-From-To: kern-bug-people->riastradh
Responsible-Changed-By: riastradh@NetBSD.org
Responsible-Changed-When: Wed, 08 Jul 2026 23:48:44 +0000
Responsible-Changed-Why:
mine


State-Changed-From-To: open->analyzed
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Wed, 08 Jul 2026 23:48:44 +0000
State-Changed-Why:
Well, that's embarrassing.  Thought I was done with the menagerie of
x86 extended CPU state for a while until Intel or AMD invents another
new extension, after all the work to save and restore it properly on
thread switch.

But the signal delivery logic, and return-from-signal logic, simply
doesn't save and restore anything beyond the xmm registers.  And
there's no place to put them in ucontext_t, which is only around
700-800 bytes, while the extended CPU states range into the tens of
kilobytes.

So what we'll have to do is:

1. Add proper test cases for all this (with a template for adding new
   architectures and new extended CPU states).

2. Make mcontext_t::__fpregs a union (or extend the existing union for
   i386) of which one variant is simply a pointer to an extended save
   area.  Allocate a bit from _UC_MD_BIT<n> to mark this.

   https://nxr.netbsd.org/xref/src/sys/arch/amd64/include/mcontext.h?r=1.24#53

   For i386, we already use _UC_MD_BIT5 to indicate FXSAVE instead of
   FSAVE:

   https://nxr.netbsd.org/xref/src/sys/arch/i386/include/mcontext.h?r=1.19#76
   https://nxr.netbsd.org/xref/src/sys/arch/amd64/include/mcontext.h?r=1.24#119

3. Teach signal delivery (sendsig_siginfo -> cpu_getmcontext) to check
   whether:

   (a) anything beyond the xmm registers are in use,
   AND
   (b) there is space in the signal stack for XSAVEing whatever else is
       in use.

   If so, it will use the new variant.

   https://nxr.netbsd.org/xref/src/sys/arch/amd64/amd64/machdep.c?r=1.379#2126

   NOTE: We might want to change MINSIGSTKSZ, because the default of
   8192 is not enough for, e.g., Intel AMX.  But I'm not sure what we
   should bump this to because it's dynamically sized!

   https://nxr.netbsd.org/xref/src/sys/sys/signal.h?r=1.78#265

4. Teach return-from-signal (i.e., setcontext -> cpu_setmcontext) to
   understand the new _UC_MD_BIT<n> to recover the extended save state.

This way:

- Applications that don't use anything beyond xmm registers won't see
  any changes.

- Applications that do use registers beyond the xmm registers at least
  have them saved and restored by signal handlers, and can have access
  to them via siginfo.

- getcontext() is still busted, and I don't see any way to fix that
  without versioning it so we can expand mcontext_t and therefore
  ucontext_t -- but how large do we expand it?  Just like the OS needs
  to negotiate extended save area sizes with the CPU, userland will
  need to negotiate mcontext_t sizes with the kernel, and that
  basically requires another API.

We also need to create ptrace accessors for this stuff, but that's not
as urgent as avoiding corrupting CPU state beyond the xmm registers
when a signal is delivered.


From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60426 CVS commit: src
Date: Thu, 9 Jul 2026 02:05:45 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Thu Jul  9 02:05:45 UTC 2026

 Modified Files:
 	src/distrib/sets/lists/debug: mi
 	src/distrib/sets/lists/tests: mi
 	src/tests/kernel: Makefile
 Added Files:
 	src/tests/kernel: t_signal_and_fpu.c
 	src/tests/kernel/arch/x86: sig_fpu.c sig_fpu.h

 Log Message:
 Verify signal delivery preserves various FPU state.

 Should also test AVX512 but I'm not sure I have hardware handy to do
 this right now and qemu doesn't emulate it, as far as I know!  TBD.
 (Plus: there's umpteen different cpuid feature bits to consider,
 annoyingly, and that means a lot of manual reading to sort through
 them.)

 PR kern/60426: Signal handler corrupts AVX (YMM) registers


 To generate a diff of this commit:
 cvs rdiff -u -r1.515 -r1.516 src/distrib/sets/lists/debug/mi
 cvs rdiff -u -r1.1423 -r1.1424 src/distrib/sets/lists/tests/mi
 cvs rdiff -u -r1.97 -r1.98 src/tests/kernel/Makefile
 cvs rdiff -u -r0 -r1.1 src/tests/kernel/t_signal_and_fpu.c
 cvs rdiff -u -r0 -r1.1 src/tests/kernel/arch/x86/sig_fpu.c \
     src/tests/kernel/arch/x86/sig_fpu.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org
Cc: 
Subject: Re: kern/60426 (Signal handler corrupts AVX (YMM) registers)
Date: Thu, 09 Jul 2026 10:33:14 +0700

     Date:        Wed,  8 Jul 2026 23:48:44 +0000 (UTC)
     From:        riastradh@NetBSD.org
     Message-ID:  <20260708234844.E01951A923F@mollari.NetBSD.org>

   | But the signal delivery logic, and return-from-signal logic, simply
   | doesn't save and restore anything beyond the xmm registers.

 Does it really need to?   There are lots of restrictions on what
 signal handlers are allowed to do, couldn't one of them simply be
 that if these YMM registers are to be touched in the signal handler,
 (or anything it calls) then it becomes the handler's responsibility
 to save and restore them?

 Since (as I understand things) not all processors even have the things
 (if running on one old enough) and I think I have seen firmware options
 to disable them as well, I am guessing that the compilers don't just
 emit instructions to use those registers for everyday normal code, and
 that the application needs to do something special to access them.
 In that case, just "don't do that in signal handlers" (just as "don't
 call malloc() in signal handlers, and hence lots other functions which
 might do that", is another of the rules for them).

 If the go system wants signal handlers to be able to use AVX stuff, let
 their signal handlers make it work (save/restore whatever state is needed).

 A good signal handler often does no more than

 	seen_sig_N = 1;

 where seen_sig_N is a volatile (global) variable, and then
 returns, leaving it to the normal code (which might have received
 an EINTR from some system call) to check that variable frequently
 enough to behave properly when the signal is received.

 Of course, if the signal handler never intends to return, just diagnose
 what happened, and exit, much of this is moot.

 kre

From: Taylor R Campbell <riastradh@NetBSD.org>
To: Robert Elz <kre@munnari.OZ.AU>
Cc: gnats-bugs@netbsd.org, gnats-admin@netbsd.org, netbsd-bugs@netbsd.org,
	bsiegert@netbsd.org, martin@netbsd.org
Subject: Re: kern/60426 (Signal handler corrupts AVX (YMM) registers)
Date: Thu, 9 Jul 2026 10:29:18 +0000

 > Date: Thu, 09 Jul 2026 10:33:14 +0700
 > From: Robert Elz <kre@munnari.OZ.AU>
 >
 >     Date:        Wed,  8 Jul 2026 23:48:44 +0000 (UTC)
 >     From:        riastradh@NetBSD.org
 >=20
 >   | But the signal delivery logic, and return-from-signal logic, simply
 >   | doesn't save and restore anything beyond the xmm registers.
 >=20
 > Does it really need to?   There are lots of restrictions on what
 > signal handlers are allowed to do, couldn't one of them simply be
 > that if these YMM registers are to be touched in the signal handler,
 > (or anything it calls) then it becomes the handler's responsibility
 > to save and restore them?

 The xmm registers, which have always been available on amd64 since the
 beginning, are aliases for the low halves of the ymm registers.  Any
 SSE2 instruction (again: available on amd64 since the beginning)
 writing to xmmN will generally zero the high half of ymmN as a side
 effect.

 So it's not enough to say that signal handlers must avoid using new
 instructions that modify the ymm registers, because old instructions
 which have always been reliable in signal handlers modify the ymm
 registers too.

 > Since (as I understand things) not all processors even have the things
 > (if running on one old enough) and I think I have seen firmware options
 > to disable them as well, I am guessing that the compilers don't just
 > emit instructions to use those registers for everyday normal code, and
 > that the application needs to do something special to access them.

 Compilers will routinely generate SSE2 instructions for amd64 and i686
 code.  And libraries often use SSE2 instructions for basic (and
 async-signal-safe) functions like memcpy and strlen, like these in
 glibc (used on i686 and amd64 targets, respectively):

 https://sourceware.org/git/?p=3Dglibc.git;a=3Dblob;f=3Dsysdeps/i386/i686/mu=
 ltiarch/memcpy-sse2-unaligned.S;h=3Df5d3582d638c80043e77459deaa6a76d411e28c=
 0;hb=3Df762ccf84f122d1354f103a151cba8bde797d521
 https://sourceware.org/git/?p=3Dglibc.git;a=3Dblob;f=3Dsysdeps/x86_64/multi=
 arch/strlen-sse2.S;h=3D1b118a0245b4a40e4f8e1c434cbb7a0426efdfc5;hb=3Df762cc=
 f84f122d1354f103a151cba8bde797d521

 I expect golang is doing similarly, either in a library or in compiler
 codegen, in its signal handler, e.g. to copy a 16-byte object on the
 stack using a pair of MOVDQA instructions to move from memory to xmmN
 and back to memory, which will clear the high half of ymmN.

 Here's an example of gcc16 doing something like that, with only the
 -O2 option, nothing to ask it to go out of its way to use the vector
 unit:

 /* input */
 struct bigstruct {
 	long x[2];
 };

 void
 copybigstruct(struct bigstruct *dst, const struct bigstruct *src)
 {
 	*dst =3D *src;
 }

 /* output */
 "copybigstruct":
         movdqu  xmm0, XMMWORD PTR [rsi]
         movups  XMMWORD PTR [rdi], xmm0
         ret

 https://godbolt.org/z/1ccGYTEdE

From: Robert Elz <kre@munnari.OZ.AU>
To: Taylor R Campbell <riastradh@NetBSD.org>
Cc: gnats-bugs@netbsd.org, netbsd-bugs@netbsd.org, bsiegert@netbsd.org,
        martin@netbsd.org
Subject: Re: kern/60426 (Signal handler corrupts AVX (YMM) registers)
Date: Thu, 09 Jul 2026 18:35:56 +0700

     Date:        Thu, 9 Jul 2026 10:29:18 +0000
     From:        Taylor R Campbell <riastradh@NetBSD.org>
     Message-ID:  <20260709102920.640E184DAC@mail.netbsd.org>


   | The xmm registers, which have always been available on amd64 since the
   | beginning, are aliases for the low halves of the ymm registers.  Any
   | SSE2 instruction (again: available on amd64 since the beginning)
   | writing to xmmN will generally zero the high half of ymmN as a side
   | effect.

 Ugh.   OK - I know zero about the architecture, I am still fuming that
 IBM picked the 8080 when the NetSemi equiv (the identity of which I have
 forgotten) was so much nicer.   Even the z80 was (slightly) better.
 I assume that so few people wanted 8080s (in quantity) that they got
 a good deal...

 That however sounds like yet another horrid Intel design choice.

 kre


From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60426 CVS commit: src
Date: Fri, 10 Jul 2026 15:11:26 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Fri Jul 10 15:11:25 UTC 2026

 Modified Files:
 	src/sys/arch/amd64/amd64: machdep.c netbsd32_machdep.c
 	src/sys/arch/amd64/include: mcontext.h
 	src/sys/arch/i386/i386: machdep.c
 	src/sys/arch/i386/include: mcontext.h
 	src/sys/arch/x86/include: cpu_extended_state.h fpu.h specialreg.h
 	src/sys/arch/x86/x86: fpu.c identcpu.c
 	src/tests/kernel: t_signal_and_fpu.c

 Log Message:
 x86: Save and restore all supported extended CPU state on signals.

 While here, disable Intel AMX, whose state size (>>8 KiB) exceeds
 MINSIGSTKSZ (8 KiB), until we are ready to safely update the ABI for
 sigaltstack(2).  This isn't a regression: we've never had a release
 with Intel AMX support.

 Previously, on signal delivery, we would only save and restore at
 most what FXSAVE does, which is the x87 and SSE registers that always
 exist on amd64.

 To save and restore the upper halves of the YMM or ZMM registers
 (AVX/AVX2/AVX512), or the AVX512 registers ZMM16..ZMM31, or the
 enormous AMX state, we need to do more.  And we need to do that even if
 the signal handler doesn't use AVX instructions, because an SSE
 instruction modifying xmmN (e.g., in a vectorized memcpy) will, as a
 side effect, zero the high half of ymmN.

 Fortunately, the x86 architecture has an extensible mechanism for
 saving and restoring extended CPU state, the XSAVE/XRSTOR instruction
 family, so we don't need to update this code for every extension --
 just handling the generic XSAVE/XRSTOR mechanism will work for a
 while.

 When delivering a signal to a thread that has actually made use of
 these extended registers, we:

 1. allocate a separate space on the user's stack for an XSAVE area of
    whatever size is necessary (as long as it fits in the sigaltstack,
    if one was provided), and

 2. store a pointer to and the length of the separate XSAVE area in an
    architecturally unused section of the FXSAVE area of the
    mcontext_t, with the _UC_XSAVE bit set.

 This way existing applications that have SA_SIGINFO signal handlers
 and expect to find an FXSAVE area in the mcontext_t with x87 and SSE
 register content will continue to work, but if the thread has used
 any state beyond that like AVX registers requiring XSAVE/XRSTOR, on
 return from signal it can restore everything.

 Unfortunately, the _size_ that XSAVE needs is variable and might be
 expanded by architectural extensions, such as Intel AMX.  So the
 minimum stack size to store it all might exceed MINSIGSTKSZ.  The
 current MINSIGSTKSZ is plenty for AVX/AVX2/AVX-512.  But before
 raising MINSIGSTKSZ to support larger XSAVE sizes by adding them to
 XCR0_FPU, we need to make sure that older binaries built with the old
 MINSIGSTKSZ continue to work -- of course, they won't be able to use
 (e.g.) Intel AMX state.

 To make sure we don't break the ABI accidentally, I've added some
 __CTASSERTs relating XSAVE_MAX_BYTES, the maximum supported XSAVE
 area size, and MINSIGSTKSZ, and a panic at boot if the CPU advertises
 an XSAVE size larger than XSAVE_MAX_BYTES, which would mean either:

 (a) the CPU's advertised size for various architecturally defined
     extended CPU state does not match the documentation, or

 (b) we have inadvertently bitten off more extended state CPU state
     than we can chew in XCR0_FPU and we have to adjust the ABI and
     add compatibility for userland programs.

 Fortunately for getcontext(2), all of the registers in question are
 caller-saves, so it doesn't need to store any extra state.

 PR kern/60426: Signal handler corrupts AVX (YMM) registers


 To generate a diff of this commit:
 cvs rdiff -u -r1.379 -r1.380 src/sys/arch/amd64/amd64/machdep.c
 cvs rdiff -u -r1.142 -r1.143 src/sys/arch/amd64/amd64/netbsd32_machdep.c
 cvs rdiff -u -r1.24 -r1.25 src/sys/arch/amd64/include/mcontext.h
 cvs rdiff -u -r1.851 -r1.852 src/sys/arch/i386/i386/machdep.c
 cvs rdiff -u -r1.19 -r1.20 src/sys/arch/i386/include/mcontext.h
 cvs rdiff -u -r1.19 -r1.20 src/sys/arch/x86/include/cpu_extended_state.h
 cvs rdiff -u -r1.23 -r1.24 src/sys/arch/x86/include/fpu.h
 cvs rdiff -u -r1.221 -r1.222 src/sys/arch/x86/include/specialreg.h
 cvs rdiff -u -r1.94 -r1.95 src/sys/arch/x86/x86/fpu.c
 cvs rdiff -u -r1.139 -r1.140 src/sys/arch/x86/x86/identcpu.c
 cvs rdiff -u -r1.1 -r1.2 src/tests/kernel/t_signal_and_fpu.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60426 CVS commit: src/tests/kernel
Date: Fri, 10 Jul 2026 20:36:11 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Fri Jul 10 20:36:11 UTC 2026

 Modified Files:
 	src/tests/kernel: t_signal_and_fpu.c

 Log Message:
 t_signal_and_fpu: Fix machine-independent double test.

 Copypasta error!  Float and long double tests were fine.

 PR kern/60426: Signal handler corrupts AVX (YMM) registers


 To generate a diff of this commit:
 cvs rdiff -u -r1.2 -r1.3 src/tests/kernel/t_signal_and_fpu.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

State-Changed-From-To: analyzed->needs-pullups
State-Changed-By: riastradh@NetBSD.org
State-Changed-When: Fri, 10 Jul 2026 21:53:23 +0000
State-Changed-Why:
fixed in HEAD, needs pullup-11, pullup-10, pullup-9

(Fortunately, this should have essentially no ABI implications, except
perhaps that a signal handler can no longer update the x87/SSE registers
in the same part of an mcontext_t in a process that _also_ uses AVX
state or other XSAVE extensions.  And, perhaps we could make that work
too with a little more effort, to have the XSAVE area overlap with the
FXSAVE area that's already in the mcontext_t.)


From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60426 CVS commit: src/sys/arch/x86/x86
Date: Sat, 11 Jul 2026 03:26:26 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Sat Jul 11 03:26:26 UTC 2026

 Modified Files:
 	src/sys/arch/x86/x86: identcpu.c

 Log Message:
 x86: Redo boot-time XSAVE area size ABI check.

 Instead of checking each component's offset+size from

     size = CPUID[EAX=0x0d,ECX=i].EAX,
     offset = CPUID[EAX=0x0d,ECX=i].EBX,

 to make sure it fits in the XSAVE_MAX_BYTES implied by the
 MINSIGSTKSZ ABI parameter, just check the total _enabled_ XSAVE area
 size from

     CPUID[EAX=0x0d,ECX=0].EBX,

 which is what we use to allocate the XSAVE area in software anyway.

 The Intel documentation[1] is not very clear on exactly what
 CPUID[EAX=0x0d,ECX=i] reports for i >= 2, saying `valid bit in the
 XCR0 register' without distinguishing `supported' from `enabled'
 bits, and the AMD documentation I skimmed didn't have these leaves in
 any obvious place.  But it seems CPUID[EAX=0xd,ECX=i].EAX/.EBX give
 the size and offset of the CPU's _supported_ XSAVE features, rather
 than only those for _enabled_ XSAVE features, so the original panic
 would have tripped on features we haven't enabled in XCR0_FPU.

 In any case, better to verify the parameter we use directly for
 allocating space.

 [1] Intel 64 and IA-32 Architectures Software Developer's Manual,
     Volume 1: Basic Architecture, Intel, Order Number: 253665-092US,
     June 2026, Sec. 21.3 `CPUID Leaves', Subsection `CPUID.0DH --
     Processor Extended State', Subsubsection `CPUID.0DH.SUB-LEAVES -
     Sub-leaves', pp. 21-43 -- 21-44.
     https://web.archive.org/web/20260709150417/https://cdrdv2-public.intel.com/922477/253665-092-sdm-vol-1.pdf

 PR kern/60426: Signal handler corrupts AVX (YMM) registers


 To generate a diff of this commit:
 cvs rdiff -u -r1.140 -r1.141 src/sys/arch/x86/x86/identcpu.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60426 CVS commit: src/tests/kernel/arch/x86
Date: Wed, 15 Jul 2026 01:02:42 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Wed Jul 15 01:02:42 UTC 2026

 Modified Files:
 	src/tests/kernel/arch/x86: sig_fpu.c

 Log Message:
 t_signal_and_fpu: Add test case for AVX-512 ZMM registers.

 PR kern/60426: Signal handler corrupts AVX (YMM) registers


 To generate a diff of this commit:
 cvs rdiff -u -r1.1 -r1.2 src/tests/kernel/arch/x86/sig_fpu.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Taylor R Campbell" <riastradh@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60426 CVS commit: src/tests/kernel/arch/x86
Date: Wed, 15 Jul 2026 02:08:19 +0000

 Module Name:	src
 Committed By:	riastradh
 Date:		Wed Jul 15 02:08:18 UTC 2026

 Modified Files:
 	src/tests/kernel/arch/x86: sig_fpu.c

 Log Message:
 t_signal_and_fpu: Fix XMM register trashing.

 The units we read from memory into registers are 16 bytes (128 bits)
 each, not 32 bytes (256 bits).

 Fortunately, this mistake didn't prevent the test from generally
 working: reading past the end of our stack frame will just give us
 other garbage that is unlikely to coincide with the register content
 that the interrupted code expects.

 PR kern/60426: Signal handler corrupts AVX (YMM) registers


 To generate a diff of this commit:
 cvs rdiff -u -r1.2 -r1.3 src/tests/kernel/arch/x86/sig_fpu.c

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

From: "Martin Husemann" <martin@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc: 
Subject: PR/60426 CVS commit: [netbsd-11] src
Date: Sun, 19 Jul 2026 15:57:29 +0000

 Module Name:	src
 Committed By:	martin
 Date:		Sun Jul 19 15:57:28 UTC 2026

 Modified Files:
 	src/distrib/sets/lists/debug [netbsd-11]: mi
 	src/distrib/sets/lists/tests [netbsd-11]: mi
 	src/sys/arch/amd64/amd64 [netbsd-11]: machdep.c netbsd32_machdep.c
 	src/sys/arch/amd64/include [netbsd-11]: mcontext.h
 	src/sys/arch/i386/i386 [netbsd-11]: machdep.c
 	src/sys/arch/i386/include [netbsd-11]: mcontext.h
 	src/sys/arch/x86/include [netbsd-11]: cpu_extended_state.h fpu.h
 	    specialreg.h
 	src/sys/arch/x86/x86 [netbsd-11]: fpu.c identcpu.c
 	src/tests/kernel [netbsd-11]: Makefile
 Added Files:
 	src/tests/kernel [netbsd-11]: t_signal_and_fpu.c
 	src/tests/kernel/arch/x86 [netbsd-11]: sig_fpu.c sig_fpu.h

 Log Message:
 Pull up following revision(s) (requested by riastradh in ticket #381):

 	sys/arch/amd64/amd64/machdep.c: revision 1.380
 	sys/arch/amd64/include/mcontext.h: revision 1.25
 	sys/arch/amd64/amd64/netbsd32_machdep.c: revision 1.143
 	distrib/sets/lists/tests/mi: revision 1.1424
 	sys/arch/x86/include/specialreg.h: revision 1.222
 	sys/arch/x86/x86/identcpu.c: revision 1.140
 	sys/arch/i386/include/mcontext.h: revision 1.20
 	sys/arch/x86/x86/identcpu.c: revision 1.141
 	tests/kernel/t_signal_and_fpu.c: revision 1.1
 	tests/kernel/t_signal_and_fpu.c: revision 1.2
 	tests/kernel/t_signal_and_fpu.c: revision 1.3
 	sys/arch/x86/include/fpu.h: revision 1.24
 	sys/arch/x86/include/cpu_extended_state.h: revision 1.20
 	sys/arch/i386/i386/machdep.c: revision 1.852
 	distrib/sets/lists/debug/mi: revision 1.516
 	tests/kernel/Makefile: revision 1.98
 	tests/kernel/arch/x86/sig_fpu.c: revision 1.1
 	sys/arch/x86/x86/fpu.c: revision 1.95
 	tests/kernel/arch/x86/sig_fpu.h: revision 1.1

 Verify signal delivery preserves various FPU state.

 Should also test AVX512 but I'm not sure I have hardware handy to do
 this right now and qemu doesn't emulate it, as far as I know!  TBD.
 (Plus: there's umpteen different cpuid feature bits to consider,
 annoyingly, and that means a lot of manual reading to sort through
 them.)

 PR kern/60426: Signal handler corrupts AVX (YMM) registers

 x86: Save and restore all supported extended CPU state on signals.

 While here, disable Intel AMX, whose state size (>>8 KiB) exceeds
 MINSIGSTKSZ (8 KiB), until we are ready to safely update the ABI for
 sigaltstack(2).  This isn't a regression: we've never had a release
 with Intel AMX support.

 Previously, on signal delivery, we would only save and restore at
 most what FXSAVE does, which is the x87 and SSE registers that always
 exist on amd64.

 To save and restore the upper halves of the YMM or ZMM registers
 (AVX/AVX2/AVX512), or the AVX512 registers ZMM16..ZMM31, or the
 enormous AMX state, we need to do more.  And we need to do that even if
 the signal handler doesn't use AVX instructions, because an SSE
 instruction modifying xmmN (e.g., in a vectorized memcpy) will, as a
 side effect, zero the high half of ymmN.

 Fortunately, the x86 architecture has an extensible mechanism for
 saving and restoring extended CPU state, the XSAVE/XRSTOR instruction
 family, so we don't need to update this code for every extension --
 just handling the generic XSAVE/XRSTOR mechanism will work for a
 while.

 When delivering a signal to a thread that has actually made use of
 these extended registers, we:

 1. allocate a separate space on the user's stack for an XSAVE area of
    whatever size is necessary (as long as it fits in the sigaltstack,
    if one was provided), and
 2. store a pointer to and the length of the separate XSAVE area in an
    architecturally unused section of the FXSAVE area of the
    mcontext_t, with the _UC_XSAVE bit set.

 This way existing applications that have SA_SIGINFO signal handlers
 and expect to find an FXSAVE area in the mcontext_t with x87 and SSE
 register content will continue to work, but if the thread has used
 any state beyond that like AVX registers requiring XSAVE/XRSTOR, on
 return from signal it can restore everything.

 Unfortunately, the _size_ that XSAVE needs is variable and might be
 expanded by architectural extensions, such as Intel AMX.  So the
 minimum stack size to store it all might exceed MINSIGSTKSZ.  The
 current MINSIGSTKSZ is plenty for AVX/AVX2/AVX-512.  But before
 raising MINSIGSTKSZ to support larger XSAVE sizes by adding them to
 XCR0_FPU, we need to make sure that older binaries built with the old
 MINSIGSTKSZ continue to work -- of course, they won't be able to use
 (e.g.) Intel AMX state.

 To make sure we don't break the ABI accidentally, I've added some
 __CTASSERTs relating XSAVE_MAX_BYTES, the maximum supported XSAVE
 area size, and MINSIGSTKSZ, and a panic at boot if the CPU advertises
 an XSAVE size larger than XSAVE_MAX_BYTES, which would mean either:

 (a) the CPU's advertised size for various architecturally defined
     extended CPU state does not match the documentation, or
 (b) we have inadvertently bitten off more extended state CPU state
     than we can chew in XCR0_FPU and we have to adjust the ABI and
     add compatibility for userland programs.

 Fortunately for getcontext(2), all of the registers in question are
 caller-saves, so it doesn't need to store any extra state.

 PR kern/60426: Signal handler corrupts AVX (YMM) registers

 t_signal_and_fpu: Fix machine-independent double test.

 Copypasta error!  Float and long double tests were fine.

 PR kern/60426: Signal handler corrupts AVX (YMM) registers

 x86: Redo boot-time XSAVE area size ABI check.
 Instead of checking each component's offset+size from

     size = CPUID[EAX=0x0d,ECX=i].EAX,
     offset = CPUID[EAX=0x0d,ECX=i].EBX,

 to make sure it fits in the XSAVE_MAX_BYTES implied by the
 MINSIGSTKSZ ABI parameter, just check the total _enabled_ XSAVE area
 size from

     CPUID[EAX=0x0d,ECX=0].EBX,

 which is what we use to allocate the XSAVE area in software anyway.

 The Intel documentation[1] is not very clear on exactly what
 CPUID[EAX=0x0d,ECX=i] reports for i >= 2, saying `valid bit in the
 XCR0 register' without distinguishing `supported' from `enabled'
 bits, and the AMD documentation I skimmed didn't have these leaves in
 any obvious place.  But it seems CPUID[EAX=0xd,ECX=i].EAX/.EBX give
 the size and offset of the CPU's _supported_ XSAVE features, rather
 than only those for _enabled_ XSAVE features, so the original panic
 would have tripped on features we haven't enabled in XCR0_FPU.

 In any case, better to verify the parameter we use directly for
 allocating space.

 [1] Intel 64 and IA-32 Architectures Software Developer's Manual,
     Volume 1: Basic Architecture, Intel, Order Number: 253665-092US,
     June 2026, Sec. 21.3 `CPUID Leaves', Subsection `CPUID.0DH --
     Processor Extended State', Subsubsection `CPUID.0DH.SUB-LEAVES -
     Sub-leaves', pp. 21-43 -- 21-44.
     https://web.archive.org/web/20260709150417/https://cdrdv2-public.intel.com/922477/253665-092-sdm-vol-1.pdf

 PR kern/60426: Signal handler corrupts AVX (YMM) registers


 To generate a diff of this commit:
 cvs rdiff -u -r1.485.2.10 -r1.485.2.11 src/distrib/sets/lists/debug/mi
 cvs rdiff -u -r1.1387.2.11 -r1.1387.2.12 src/distrib/sets/lists/tests/mi
 cvs rdiff -u -r1.376.2.1 -r1.376.2.2 src/sys/arch/amd64/amd64/machdep.c
 cvs rdiff -u -r1.142 -r1.142.2.1 src/sys/arch/amd64/amd64/netbsd32_machdep.c
 cvs rdiff -u -r1.24 -r1.24.2.1 src/sys/arch/amd64/include/mcontext.h
 cvs rdiff -u -r1.849 -r1.849.2.1 src/sys/arch/i386/i386/machdep.c
 cvs rdiff -u -r1.19 -r1.19.2.1 src/sys/arch/i386/include/mcontext.h
 cvs rdiff -u -r1.19 -r1.19.2.1 src/sys/arch/x86/include/cpu_extended_state.h
 cvs rdiff -u -r1.23 -r1.23.28.1 src/sys/arch/x86/include/fpu.h
 cvs rdiff -u -r1.219 -r1.219.2.1 src/sys/arch/x86/include/specialreg.h
 cvs rdiff -u -r1.93.2.1 -r1.93.2.2 src/sys/arch/x86/x86/fpu.c
 cvs rdiff -u -r1.138 -r1.138.2.1 src/sys/arch/x86/x86/identcpu.c
 cvs rdiff -u -r1.96.2.1 -r1.96.2.2 src/tests/kernel/Makefile
 cvs rdiff -u -r0 -r1.3.2.2 src/tests/kernel/t_signal_and_fpu.c
 cvs rdiff -u -r0 -r1.3.2.2 src/tests/kernel/arch/x86/sig_fpu.c
 cvs rdiff -u -r0 -r1.1.2.2 src/tests/kernel/arch/x86/sig_fpu.h

 Please note that diffs are not public domain; they are subject to the
 copyright notices on the relevant files.

>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.