In the Linux kernel, the following vulnerability has been resolved:
x86/fpu: Drop fpregs lock before inheriting FPU permissions
Mike Galbraith reported the following against an old fork of preempt-rt but the same issue also applies to the current preempt-rt tree.
BUG: sleeping function called from invalid context at kernel/locking/spinlockrt.c:46 inatomic(): 1, irqsdisabled(): 0, nonblock: 0, pid: 1, name: systemd preemptcount: 1, expected: 0 RCU nest depth: 0, expected: 0 Preemption disabled at: fpuclone CPU: 6 PID: 1 Comm: systemd Tainted: G E (unreleased) Call Trace: <TASK> dumpstacklvl ? fpuclone _mightresched rtspinlock fpuclone ? copythread ? copyprocess ? shmemallocinode ? kmemcachealloc ? kernelclone ? _dosysclone ? dosyscall64 ? _x64sysrtsigprocmask ? syscallexittousermode ? dosyscall64 ? syscallexittousermode ? dosyscall64 ? syscallexittousermode ? dosyscall64 ? excpagefault ? entrySYSCALL64afterhwframe </TASK>
Mike says:
The splat comes from fpuinheritperms() being called under fpregslock(), and us reaching the spinlockirq() therein due to fpustatesizedynamic() returning true despite static key _fpustatesizedynamic having never been enabled.
Mike's assessment looks correct. fpregslock on a PREEMPTRT kernel disables preemption so calling spinlockirq() in fpuinheritperms() is unsafe. This problem exists since commit
9e798e9aa14c ("x86/fpu: Prepare fpu_clone() for dynamically enabled features").
Even though the original bug report should not have enabled the paths at all, the bug still exists.
fpregslock is necessary when editing the FPU registers or a task's FP state but it is not necessary for fpuinheritperms(). The only write of any FP state in fpuinheritperms() is for the new child which is not running yet and cannot context switch or be borrowed by a kernel thread yet. Hence, fpregslock is not protecting anything in the new child until clone() completes and can be dropped earlier. The siglock still needs to be acquired by fpuinheritperms() as the read of the parent's permissions has to be serialised.
[ bp: Cleanup splat. ]