🇰🇷 한글 설명
Linux 커널의 PowerPC 아키텍처에서 인터럽트 재처리(replay) 과정 중 irq_soft_mask 값이 손상되는 취약점이 발견되었습니다. 인터럽트 핸들러 종료 시 특정 비트가 누락되어 시스템 경고(WARNING)가 발생할 수 있으며, 이를 해결하기 위해 local_irq_disable()을 hard_irq_disable()로 교체하는 패치가 적용되었습니다.
💡 쉬운 설명
컴퓨터가 하드웨어 신호(인터럽트)를 처리하는 과정에서 관리용 설정 값이 잘못 지워지는 버그입니다. 이로 인해 시스템이 비정상적인 상태임을 알리는 경고 메시지를 출력할 수 있습니다.
🇺🇸 원문 (English)
In the Linux kernel, the following vulnerability has been resolved:
powerpc/entry: Fix irq_soft_mask corruption on replayed interrupt exit
When __replay_soft_interrupts() replays a pending interrupt (e.g.
PACA_IRQ_DEC -> timer_interrupt), it calls the handler directly with a
synthetic pt_regs. The DEFINE_INTERRUPT_HANDLER_ASYNC wrapper around
each handler calls arch_interrupt_async_exit_prepare() on the way out,
which calls arch_interrupt_exit_prepare() -> local_irq_disable() ->
arch_local_irq_disable(), which does:
irq_soft_mask_set(IRQS_DISABLED) /* 0x1 */
This unconditionally overwrites irq_soft_mask with IRQS_DISABLED (0x1),
stripping the IRQS_PMI_DISABLED (0x2) bit. The result is that
irq_soft_mask is 0x1 instead of IRQS_ALL_DISABLED (0x3) when the
handler returns to __replay_soft_interrupts().
For a normally-taken interrupt this is harmless: the next interrupt
always enters through arch_interrupt_enter_prepare() which
unconditionally sets irq_soft_mask to IRQS_ALL_DISABLED. But during
replay, next_interrupt() is called directly between replayed handlers
without going back through arch_interrupt_enter_prepare(), so the
stripped bit is never restored. next_interrupt() then fires a WARNING:
WARNING: arch/powerpc/kernel/irq_64.c:75
WARN_ON(irq_soft_mask_return() != IRQS_ALL_DISABLED)
The warning was observed early in boot on a POWER10 pseries guest
during kmem_cache_init_late(), where a spinlock release triggers
interrupt replay that processes a pending timer interrupt.
Debugger state confirming the bug:
Before timer_interrupt(®s):
irq_soft_mask = 0x3 (IRQS_ALL_DISABLED) correct
irq_happened = 0x41 (HARD_DIS|REPLAYING) correct
After timer_interrupt(®s) returns:
irq_soft_mask = 0x1 (IRQS_DISABLED) WRONG - PMI bit stripped
irq_happened = 0x41 unchanged
The fix is to replace local_irq_disable() with hard_irq_disable().
hard_irq_disable() is the right primitive here for two reasons:
1. On PPC64 (hw_irq.h:301) it calls irq_soft_mask_set_return(IRQS_ALL_DISABLED),
setting the soft mask to 0x3 (both IRQS_DISABLED and IRQS_PMI_DISABLED),
which preserves the PMI bit and fixes the WARNING. The additional
work it does (__hard_irq_disable(), PACA_IRQ_HARD_DIS |=) is
redundant but safe since both are already set at this point in the
exit path; the trace_hardirqs_off() inside is guarded by
if (!arch_irqs_disabled_flags(flags)) so it will not double-fire.
2. On PPC32 (hw_irq.h:467) hard_irq_disable() maps to
arch_local_irq_disable() -> __hard_irq_disable(), which clears
MSR[EE] in hardware. This is exactly correct: PPC32 has no soft-mask
PACA mechanism, so the hardware disable is the right way to satisfy
irqentry_exit()'s requirement. This also fixes a build error on PPC32
where irq_soft_mask_set() is only defined under CONFIG_PPC64:
arch/powerpc/include/asm/entry-common.h:273: error: implicit
declaration of function 'irq_soft_mask_set'
Using hard_irq_disable() requires no #ifdef and is consistent with
how the rest of the entry code (e.g. entry-common.h:463) handles the
same PPC32/PPC64 split.
📍 영향받는 시스템
Linux kernel (PowerPC/PPC64/PPC32 architectures)
⚔️ 공격 벡터
Local (시스템 내부의 특정 인터럽트 재처리 조건 발생 시)
🛡️ 대응 방법
Linux 커널을 최신 보안 패치가 적용된 버전으로 업데이트하십시오.
🔧 검증 코드
시스템 아키텍처가 PowerPC 계열인지 확인하고, 커널 패치 내역을 통해 해당 수정 사항이 포함되었는지 검토하십시오.
uname -a && grep -E 'CONFIG_PPC64|CONFIG_PPC32' /proc/config.gz 2>/dev/null || grep -E 'CONFIG_PPC64|CONFIG_PPC32' /boot/config-$(uname -r)