CVE-2022-49783
Severity CVSS v4.0:
Pending analysis
Type:
Unavailable / Other
Publication date:
01/05/2025
Last modified:
02/05/2025
Description
In the Linux kernel, the following vulnerability has been resolved:<br />
<br />
x86/fpu: Drop fpregs lock before inheriting FPU permissions<br />
<br />
Mike Galbraith reported the following against an old fork of preempt-rt<br />
but the same issue also applies to the current preempt-rt tree.<br />
<br />
BUG: sleeping function called from invalid context at kernel/locking/spinlock_rt.c:46<br />
in_atomic(): 1, irqs_disabled(): 0, non_block: 0, pid: 1, name: systemd<br />
preempt_count: 1, expected: 0<br />
RCU nest depth: 0, expected: 0<br />
Preemption disabled at:<br />
fpu_clone<br />
CPU: 6 PID: 1 Comm: systemd Tainted: G E (unreleased)<br />
Call Trace:<br />
<br />
dump_stack_lvl<br />
? fpu_clone<br />
__might_resched<br />
rt_spin_lock<br />
fpu_clone<br />
? copy_thread<br />
? copy_process<br />
? shmem_alloc_inode<br />
? kmem_cache_alloc<br />
? kernel_clone<br />
? __do_sys_clone<br />
? do_syscall_64<br />
? __x64_sys_rt_sigprocmask<br />
? syscall_exit_to_user_mode<br />
? do_syscall_64<br />
? syscall_exit_to_user_mode<br />
? do_syscall_64<br />
? syscall_exit_to_user_mode<br />
? do_syscall_64<br />
? exc_page_fault<br />
? entry_SYSCALL_64_after_hwframe<br />
<br />
<br />
Mike says:<br />
<br />
The splat comes from fpu_inherit_perms() being called under fpregs_lock(),<br />
and us reaching the spin_lock_irq() therein due to fpu_state_size_dynamic()<br />
returning true despite static key __fpu_state_size_dynamic having never<br />
been enabled.<br />
<br />
Mike&#39;s assessment looks correct. fpregs_lock on a PREEMPT_RT kernel disables<br />
preemption so calling spin_lock_irq() in fpu_inherit_perms() is unsafe. This<br />
problem exists since commit<br />
<br />
9e798e9aa14c ("x86/fpu: Prepare fpu_clone() for dynamically enabled features").<br />
<br />
Even though the original bug report should not have enabled the paths at<br />
all, the bug still exists.<br />
<br />
fpregs_lock is necessary when editing the FPU registers or a task&#39;s FP<br />
state but it is not necessary for fpu_inherit_perms(). The only write<br />
of any FP state in fpu_inherit_perms() is for the new child which is<br />
not running yet and cannot context switch or be borrowed by a kernel<br />
thread yet. Hence, fpregs_lock is not protecting anything in the new<br />
child until clone() completes and can be dropped earlier. The siglock<br />
still needs to be acquired by fpu_inherit_perms() as the read of the<br />
parent&#39;s permissions has to be serialised.<br />
<br />
[ bp: Cleanup splat. ]