signalfd 在 SIGPROF timer hardirq 下可能发生同锁重入死锁
问题摘要
SignalFdInode.state 当前使用普通 SpinLock<SignalFdState>,多个进程上下文路径通过 self.state.lock() 获取该锁,而 lock() 不关闭本地硬中断。
例如SignalFdInode::read_at中:
|
let state_guard = self.state.lock(); |
|
if self.nonblock(&state_guard) { |
|
return Err(SystemError::EAGAIN_OR_EWOULDBLOCK); |
|
} |
|
drop(state_guard); |
当线程正在执行 signalfd 相关操作并持有这把锁时,如果本 CPU 的 timer hardirq 恰好到来,且 ITIMER_PROF 到期触发 SIGPROF,内核会在 hardirq 主链路中沿着以下调用链执行:
tick_handle_periodic
update_process_times
irqtime_account_process_tick
send_signal_to_pcb(SIGPROF)
complete_signal
notify_signalfd_for_pcb
SignalFdInode::notify_signal
而 notify_signal() 会再次获取同一个 self.state.lock(),从而形成“进程上下文持普通自旋锁,被本 CPU hardirq 重入同锁”的自锁死。
|
pub fn notify_signal(&self, sig: Signal) { |
|
let mask = self.state.lock().mask; |
|
if !mask.contains(sig.into()) { |
|
return; |
|
} |
|
// 唤醒阻塞 read() 的等待者 |
|
self.wait_queue.wakeup_all(None); |
|
// 唤醒 epoll 等待者 |
|
let _ = EventPoll::wakeup_epoll( |
|
&self.epitems, |
|
EPollEventType::EPOLLIN | EPollEventType::EPOLLRDNORM, |
|
); |
|
} |
影响范围
理论上,线程只要满足以下条件:
signalfd 的 mask 包含 SIGPROF
- 线程或进程启用了
ITIMER_PROF
- 线程以非irq_save的方式获取SignalFdInode.state.lock() (例如,执行
read_at()、readable())
就可能以 timer tick 在 SignalFdInode.state.lock() 的普通持锁窗口内到来这一时机触发自锁死。
一个可行的PoC是在监听 SIGPROF 的 signalfd、已配置 ITIMER_PROF的条件下阻塞 read(signalfd) 循环,因为它会高频进入 read_at() 的空队列路径。
修复方案
由于SignalFdInode.state这个锁可能会在 hardirq 上下文获取,因此所有其余地方对该锁的获取应从self.state.lock()改为self.state.lock_irq_save()以确保持锁时关闭中断。
signalfd在SIGPROFtimer hardirq 下可能发生同锁重入死锁问题摘要
SignalFdInode.state当前使用普通SpinLock<SignalFdState>,多个进程上下文路径通过self.state.lock()获取该锁,而lock()不关闭本地硬中断。例如
SignalFdInode::read_at中:DragonOS/kernel/src/ipc/signalfd.rs
Lines 262 to 266 in 7de74e5
当线程正在执行
signalfd相关操作并持有这把锁时,如果本 CPU 的 timer hardirq 恰好到来,且ITIMER_PROF到期触发SIGPROF,内核会在 hardirq 主链路中沿着以下调用链执行:tick_handle_periodicupdate_process_timesirqtime_account_process_ticksend_signal_to_pcb(SIGPROF)complete_signalnotify_signalfd_for_pcbSignalFdInode::notify_signal而
notify_signal()会再次获取同一个self.state.lock(),从而形成“进程上下文持普通自旋锁,被本 CPU hardirq 重入同锁”的自锁死。DragonOS/kernel/src/ipc/signalfd.rs
Lines 152 to 164 in 7de74e5
影响范围
理论上,线程只要满足以下条件:
signalfd的 mask 包含SIGPROFITIMER_PROFread_at()、readable())就可能以 timer tick 在
SignalFdInode.state.lock()的普通持锁窗口内到来这一时机触发自锁死。一个可行的PoC是在监听
SIGPROF的signalfd、已配置ITIMER_PROF的条件下阻塞read(signalfd)循环,因为它会高频进入read_at()的空队列路径。修复方案
由于
SignalFdInode.state这个锁可能会在 hardirq 上下文获取,因此所有其余地方对该锁的获取应从self.state.lock()改为self.state.lock_irq_save()以确保持锁时关闭中断。