Skip to content

Commit 9bc9ccb

Browse files
leitaoakpm00
authored andcommitted
mm/kfence: fix potential deadlock in reboot notifier
The reboot notifier callback can deadlock when calling cancel_delayed_work_sync() if toggle_allocation_gate() is blocked in wait_event_idle() waiting for allocations, that might not happen on shutdown path. The issue is that cancel_delayed_work_sync() waits for the work to complete, but the work is waiting for kfence_allocation_gate > 0 which requires allocations to happen (each allocation is increased by 1) - allocations that may have stopped during shutdown. Fix this by: 1. Using cancel_delayed_work() (non-sync) to avoid blocking. Now the callback succeeds and return. 2. Adding wake_up() to unblock any waiting toggle_allocation_gate() 3. Adding !kfence_enabled to the wait condition so the wake succeeds The static_branch_disable() IPI will still execute after the wake, but at this early point in shutdown (reboot notifier runs with INT_MAX priority), the system is still functional and CPUs can respond to IPIs. Link: https://lkml.kernel.org/r/20260116-kfence_fix-v1-1-4165a055933f@debian.org Fixes: ce2bba8 ("mm/kfence: add reboot notifier to disable KFENCE on shutdown") Signed-off-by: Breno Leitao <leitao@debian.org> Reported-by: Chris Mason <clm@meta.com> Closes: https://lore.kernel.org/all/20260113140234.677117-1-clm@meta.com/ Reviewed-by: Marco Elver <elver@google.com> Cc: Alexander Potapenko <glider@google.com> Cc: Breno Leitao <leitao@debian.org> Cc: Chris Mason <clm@meta.com> Cc: Dmitriy Vyukov <dvyukov@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
1 parent cb7d761 commit 9bc9ccb

1 file changed

Lines changed: 12 additions & 5 deletions

File tree

mm/kfence/core.c

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,9 @@ static struct notifier_block kfence_check_canary_notifier = {
823823
static struct delayed_work kfence_timer;
824824

825825
#ifdef CONFIG_KFENCE_STATIC_KEYS
826+
/* Wait queue to wake up allocation-gate timer task. */
827+
static DECLARE_WAIT_QUEUE_HEAD(allocation_wait);
828+
826829
static int kfence_reboot_callback(struct notifier_block *nb,
827830
unsigned long action, void *data)
828831
{
@@ -832,7 +835,12 @@ static int kfence_reboot_callback(struct notifier_block *nb,
832835
*/
833836
WRITE_ONCE(kfence_enabled, false);
834837
/* Cancel any pending timer work */
835-
cancel_delayed_work_sync(&kfence_timer);
838+
cancel_delayed_work(&kfence_timer);
839+
/*
840+
* Wake up any blocked toggle_allocation_gate() so it can complete
841+
* early while the system is still able to handle IPIs.
842+
*/
843+
wake_up(&allocation_wait);
836844

837845
return NOTIFY_OK;
838846
}
@@ -842,9 +850,6 @@ static struct notifier_block kfence_reboot_notifier = {
842850
.priority = INT_MAX, /* Run early to stop timers ASAP */
843851
};
844852

845-
/* Wait queue to wake up allocation-gate timer task. */
846-
static DECLARE_WAIT_QUEUE_HEAD(allocation_wait);
847-
848853
static void wake_up_kfence_timer(struct irq_work *work)
849854
{
850855
wake_up(&allocation_wait);
@@ -873,7 +878,9 @@ static void toggle_allocation_gate(struct work_struct *work)
873878
/* Enable static key, and await allocation to happen. */
874879
static_branch_enable(&kfence_allocation_key);
875880

876-
wait_event_idle(allocation_wait, atomic_read(&kfence_allocation_gate) > 0);
881+
wait_event_idle(allocation_wait,
882+
atomic_read(&kfence_allocation_gate) > 0 ||
883+
!READ_ONCE(kfence_enabled));
877884

878885
/* Disable static key and reset timer. */
879886
static_branch_disable(&kfence_allocation_key);

0 commit comments

Comments
 (0)