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:
(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.