grab_mcontext() only copies the second halves of vs0-31 into the context while the thread owns the VSX unit, but sigreturn(2) gives up ownership so that the next use reloads the registers lazily. So a signal that comes right after a sigreturn, like one the handler's mask was holding back, gets a context without them, and its own sigreturn then zeroes them in the PCB.
The halves in the PCB are current whenever the FP state is: VSX instructions can't run without PCB_VSX, and save_fpu() stores them in that case. So just export them together with the FP registers.
I noticed this with a small test that keeps a pattern in vs20 across two back-to-back signals, the low half came back as zero. There are a few more problems in the same area (grab_mcontext() dropping FPU ownership, the red zone not fitting a MINSIGSTKSZ stack, VSCR on little endian, VRSAVE never being switched), I'll send those separately.