Skip to content

Commit b6f7b2c

Browse files
tiwaigregkh
authored andcommitted
ALSA: info: Fix potential deadlock at disconnection
commit c7a6065 upstream. As reported recently, ALSA core info helper may cause a deadlock at the forced device disconnection during the procfs operation. The proc_remove() (that is called from the snd_card_disconnect() helper) has a synchronization of the pending procfs accesses via wait_for_completion(). Meanwhile, ALSA procfs helper takes the global mutex_lock(&info_mutex) at both the proc_open callback and snd_card_info_disconnect() helper. Since the proc_open can't finish due to the mutex lock, wait_for_completion() never returns, either, hence it deadlocks. TASK#1 TASK#2 proc_reg_open() takes use_pde() snd_info_text_entry_open() snd_card_disconnect() snd_info_card_disconnect() takes mutex_lock(&info_mutex) proc_remove() wait_for_completion(unused_pde) ... waiting task#1 closes mutex_lock(&info_mutex) => DEADLOCK This patch is a workaround for avoiding the deadlock scenario above. The basic strategy is to move proc_remove() call outside the mutex lock. proc_remove() can work gracefully without extra locking, and it can delete the tree recursively alone. So, we call proc_remove() at snd_info_card_disconnection() at first, then delete the rest resources recursively within the info_mutex lock. After the change, the function snd_info_disconnect() doesn't do disconnection by itself any longer, but it merely clears the procfs pointer. So rename the function to snd_info_clear_entries() for avoiding confusion. The similar change is applied to snd_info_free_entry(), too. Since the proc_remove() is called only conditionally with the non-NULL entry->p, it's skipped after the snd_info_clear_entries() call. Reported-by: Shinhyung Kang <[email protected]> Closes: https://lore.kernel.org/r/664457955.21699345385931.JavaMail.epsvc@epcpadp4 Reviewed-by: Jaroslav Kysela <[email protected]> Cc: <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Takashi Iwai <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0316887 commit b6f7b2c

File tree

1 file changed

+13
-8
lines changed

1 file changed

+13
-8
lines changed

sound/core/info.c

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ struct snd_info_private_data {
7272
};
7373

7474
static int snd_info_version_init(void);
75-
static void snd_info_disconnect(struct snd_info_entry *entry);
75+
static void snd_info_clear_entries(struct snd_info_entry *entry);
7676

7777
/*
7878
@@ -598,11 +598,16 @@ void snd_info_card_disconnect(struct snd_card *card)
598598
{
599599
if (!card)
600600
return;
601-
mutex_lock(&info_mutex);
601+
602602
proc_remove(card->proc_root_link);
603-
card->proc_root_link = NULL;
604603
if (card->proc_root)
605-
snd_info_disconnect(card->proc_root);
604+
proc_remove(card->proc_root->p);
605+
606+
mutex_lock(&info_mutex);
607+
if (card->proc_root)
608+
snd_info_clear_entries(card->proc_root);
609+
card->proc_root_link = NULL;
610+
card->proc_root = NULL;
606611
mutex_unlock(&info_mutex);
607612
}
608613

@@ -776,15 +781,14 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
776781
}
777782
EXPORT_SYMBOL(snd_info_create_card_entry);
778783

779-
static void snd_info_disconnect(struct snd_info_entry *entry)
784+
static void snd_info_clear_entries(struct snd_info_entry *entry)
780785
{
781786
struct snd_info_entry *p;
782787

783788
if (!entry->p)
784789
return;
785790
list_for_each_entry(p, &entry->children, list)
786-
snd_info_disconnect(p);
787-
proc_remove(entry->p);
791+
snd_info_clear_entries(p);
788792
entry->p = NULL;
789793
}
790794

@@ -801,8 +805,9 @@ void snd_info_free_entry(struct snd_info_entry * entry)
801805
if (!entry)
802806
return;
803807
if (entry->p) {
808+
proc_remove(entry->p);
804809
mutex_lock(&info_mutex);
805-
snd_info_disconnect(entry);
810+
snd_info_clear_entries(entry);
806811
mutex_unlock(&info_mutex);
807812
}
808813

0 commit comments

Comments
 (0)