[PATCH v2 0/5] ALSA: Drop async signal support
Hi,
this is a revised patch set for dropping fasync support from ALSA core.
The async signal itself is very difficult to use properly due to various restrictions (e.g. you cannot perform any I/O in the context), hence it's a feature that has been never used by real applications.
OTOH, the real problem is that there have been quite a few syzcaller reports indicating that fasync code path may lead to some potential deadlocks for long time. Dropping the feature is the easiest solution, obviously.
The corresponding update for alsa-lib will follow once when we agree with this approach.
thanks,
Takashi
===
v1: https://lore.kernel.org/r/20220715102935.4695-1-tiwai@suse.de v1->v2: Fix unused variable warning in patch 2
===
Takashi Iwai (5): ALSA: timer: Drop async signal support ALSA: pcm: Drop async signal support ALSA: control: Drop async signal support ALSA: core: Drop async signal support ALSA: doc: Drop stale fasync entry
.../kernel-api/writing-an-alsa-driver.rst | 1 - include/sound/control.h | 1 - include/sound/pcm.h | 1 - sound/core/control.c | 11 ----------- sound/core/init.c | 11 +---------- sound/core/pcm_lib.c | 8 +------- sound/core/pcm_native.c | 18 ------------------ sound/core/timer.c | 13 ------------- 8 files changed, 2 insertions(+), 62 deletions(-)
The async signal (SIGIO) support for ALSA timer API has been never used by real applications, but yet it can be a cause of various potential deadlocks, as spotted by syzkaller. Let's drop the feature as the simplest solution.
Reported-by: syzbot+1ee0910eca9c94f71f25@syzkaller.appspotmail.com Reported-by: syzbot+49b10793b867871ee26f@syzkaller.appspotmail.com Reported-by: syzbot+8285e973a41b5aa68902@syzkaller.appspotmail.com Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/timer.c | 13 ------------- 1 file changed, 13 deletions(-)
diff --git a/sound/core/timer.c b/sound/core/timer.c index b3214baa8919..4ac3ab2cf575 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -83,7 +83,6 @@ struct snd_timer_user { unsigned int filter; struct timespec64 tstamp; /* trigger tstamp */ wait_queue_head_t qchange_sleep; - struct fasync_struct *fasync; struct mutex ioctl_lock; };
@@ -1345,7 +1344,6 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, } __wake: spin_unlock(&tu->qlock); - kill_fasync(&tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); }
@@ -1383,7 +1381,6 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, spin_lock_irqsave(&tu->qlock, flags); snd_timer_user_append_to_tqueue(tu, &r1); spin_unlock_irqrestore(&tu->qlock, flags); - kill_fasync(&tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); }
@@ -1453,7 +1450,6 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, spin_unlock(&tu->qlock); if (append == 0) return; - kill_fasync(&tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); }
@@ -2130,14 +2126,6 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, return ret; }
-static int snd_timer_user_fasync(int fd, struct file * file, int on) -{ - struct snd_timer_user *tu; - - tu = file->private_data; - return fasync_helper(fd, file, on, &tu->fasync); -} - static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) { @@ -2280,7 +2268,6 @@ static const struct file_operations snd_timer_f_ops = .poll = snd_timer_user_poll, .unlocked_ioctl = snd_timer_user_ioctl, .compat_ioctl = snd_timer_user_ioctl_compat, - .fasync = snd_timer_user_fasync, };
/* unregister the system timer */
The async signal (SIGIO) support for ALSA PCM API has been never used by real applications, but yet it can be a cause of various potential deadlocks, as spotted by syzkaller. Let's drop the feature as the simplest solution.
Reported-by: syzbot+8285e973a41b5aa68902@syzkaller.appspotmail.com Reported-by: syzbot+669c9abf11a6a011dd09@syzkaller.appspotmail.com Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/pcm.h | 1 - sound/core/pcm_lib.c | 8 +------- sound/core/pcm_native.c | 18 ------------------ 3 files changed, 1 insertion(+), 26 deletions(-)
diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 08cf4a5801f3..9bbd3742ef59 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -399,7 +399,6 @@ struct snd_pcm_runtime { snd_pcm_uframes_t twake; /* do transfer (!poll) wakeup if non-zero */ wait_queue_head_t sleep; /* poll sleep */ wait_queue_head_t tsleep; /* transfer sleep */ - struct fasync_struct *fasync; bool stop_operating; /* sync_stop will be called */ struct mutex buffer_mutex; /* protect for buffer changes */ atomic_t buffer_accessing; /* >0: in r/w operation, <0: blocked */ diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1fc7c50ffa62..e490a575aa03 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1807,22 +1807,16 @@ EXPORT_SYMBOL(snd_pcm_lib_ioctl); */ void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substream) { - struct snd_pcm_runtime *runtime; - if (PCM_RUNTIME_CHECK(substream)) return; - runtime = substream->runtime; - if (!snd_pcm_running(substream) || snd_pcm_update_hw_ptr0(substream, 1) < 0) - goto _end; + return;
#ifdef CONFIG_SND_PCM_TIMER if (substream->timer_running) snd_timer_interrupt(substream->timer, 1); #endif - _end: - kill_fasync(&runtime->fasync, SIGIO, POLL_IN); } EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index aa0453e51595..c8e340b97277 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3938,22 +3938,6 @@ static int snd_pcm_mmap(struct file *file, struct vm_area_struct *area) return 0; }
-static int snd_pcm_fasync(int fd, struct file * file, int on) -{ - struct snd_pcm_file * pcm_file; - struct snd_pcm_substream *substream; - struct snd_pcm_runtime *runtime; - - pcm_file = file->private_data; - substream = pcm_file->substream; - if (PCM_RUNTIME_CHECK(substream)) - return -ENXIO; - runtime = substream->runtime; - if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) - return -EBADFD; - return fasync_helper(fd, file, on, &runtime->fasync); -} - /* * ioctl32 compat */ @@ -4118,7 +4102,6 @@ const struct file_operations snd_pcm_f_ops[2] = { .unlocked_ioctl = snd_pcm_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, - .fasync = snd_pcm_fasync, .get_unmapped_area = snd_pcm_get_unmapped_area, }, { @@ -4132,7 +4115,6 @@ const struct file_operations snd_pcm_f_ops[2] = { .unlocked_ioctl = snd_pcm_ioctl, .compat_ioctl = snd_pcm_ioctl_compat, .mmap = snd_pcm_mmap, - .fasync = snd_pcm_fasync, .get_unmapped_area = snd_pcm_get_unmapped_area, } };
The async signal (SIGIO) support for ALSA control API has been never used by real applications, but yet it can be a cause of various potential deadlocks (although there hasn't been a bug report in this code path unlike timer and PCM). Let's drop the feature as the simplest solution to align with other APIs.
Signed-off-by: Takashi Iwai tiwai@suse.de --- include/sound/control.h | 1 - sound/core/control.c | 11 ----------- 2 files changed, 12 deletions(-)
diff --git a/include/sound/control.h b/include/sound/control.h index fcd3cce673ec..0390d4952dac 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -109,7 +109,6 @@ struct snd_ctl_file { int preferred_subdevice[SND_CTL_SUBDEV_ITEMS]; wait_queue_head_t change_sleep; spinlock_t read_lock; - struct fasync_struct *fasync; int subscribed; /* read interface is activated */ struct list_head events; /* waiting events for read */ }; diff --git a/sound/core/control.c b/sound/core/control.c index 4dba3a342458..3d3b8bf93f80 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -181,7 +181,6 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, _found: wake_up(&ctl->change_sleep); spin_unlock(&ctl->read_lock); - kill_fasync(&ctl->fasync, SIGIO, POLL_IN); } read_unlock_irqrestore(&card->ctl_files_rwlock, flags); } @@ -2129,14 +2128,6 @@ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn) EXPORT_SYMBOL(snd_ctl_unregister_ioctl_compat); #endif
-static int snd_ctl_fasync(int fd, struct file * file, int on) -{ - struct snd_ctl_file *ctl; - - ctl = file->private_data; - return fasync_helper(fd, file, on, &ctl->fasync); -} - /* return the preferred subdevice number if already assigned; * otherwise return -1 */ @@ -2264,7 +2255,6 @@ static const struct file_operations snd_ctl_f_ops = .poll = snd_ctl_poll, .unlocked_ioctl = snd_ctl_ioctl, .compat_ioctl = snd_ctl_ioctl_compat, - .fasync = snd_ctl_fasync, };
/* @@ -2302,7 +2292,6 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) read_lock_irqsave(&card->ctl_files_rwlock, flags); list_for_each_entry(ctl, &card->ctl_files, list) { wake_up(&ctl->change_sleep); - kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); } read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
Now fasync support is dropped from all ALSA interfaces, and we can drop the fasync workaround at the card disconnection code, too.
Signed-off-by: Takashi Iwai tiwai@suse.de --- sound/core/init.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-)
diff --git a/sound/core/init.c b/sound/core/init.c index 3ac95c66a4b5..899bfe838a91 100644 --- a/sound/core/init.c +++ b/sound/core/init.c @@ -432,11 +432,8 @@ static int snd_disconnect_release(struct inode *inode, struct file *file) } spin_unlock(&shutdown_lock);
- if (likely(df)) { - if ((file->f_flags & FASYNC) && df->disconnected_f_op->fasync) - df->disconnected_f_op->fasync(-1, file, 0); + if (likely(df)) return df->disconnected_f_op->release(inode, file); - }
panic("%s(%p, %p) failed!", __func__, inode, file); } @@ -457,11 +454,6 @@ static int snd_disconnect_mmap(struct file *file, struct vm_area_struct *vma) return -ENODEV; }
-static int snd_disconnect_fasync(int fd, struct file *file, int on) -{ - return -ENODEV; -} - static const struct file_operations snd_shutdown_f_ops = { .owner = THIS_MODULE, @@ -475,7 +467,6 @@ static const struct file_operations snd_shutdown_f_ops = .compat_ioctl = snd_disconnect_ioctl, #endif .mmap = snd_disconnect_mmap, - .fasync = snd_disconnect_fasync };
/**
The fasync entry has been dropped recently. Update the documentation as well.
Signed-off-by: Takashi Iwai tiwai@suse.de --- Documentation/sound/kernel-api/writing-an-alsa-driver.rst | 1 - 1 file changed, 1 deletion(-)
diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst index 176b73583b7a..84b4ecae1485 100644 --- a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst +++ b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -1597,7 +1597,6 @@ are the contents of this file: spinlock_t lock; wait_queue_head_t sleep; struct timer_list tick_timer; - struct fasync_struct *fasync;
/* -- private section -- */ void *private_data;
Dne 17. 07. 22 v 9:05 Takashi Iwai napsal(a):
Hi,
this is a revised patch set for dropping fasync support from ALSA core.
The async signal itself is very difficult to use properly due to various restrictions (e.g. you cannot perform any I/O in the context), hence it's a feature that has been never used by real applications.
OTOH, the real problem is that there have been quite a few syzcaller reports indicating that fasync code path may lead to some potential deadlocks for long time. Dropping the feature is the easiest solution, obviously.
I would probably prefer to fix the problem (deferred async kill or so). The SIGIO is just another way to wakeup the applications and there may be some corner cases, where this wakeup is usable (threaded apps). Note that we had some users (or testers) of SIGIO in past (at least there were questions about this support).
Jaroslav
On Sun, 17 Jul 2022 12:16:13 +0200, Jaroslav Kysela wrote:
Dne 17. 07. 22 v 9:05 Takashi Iwai napsal(a):
Hi,
this is a revised patch set for dropping fasync support from ALSA core.
The async signal itself is very difficult to use properly due to various restrictions (e.g. you cannot perform any I/O in the context), hence it's a feature that has been never used by real applications.
OTOH, the real problem is that there have been quite a few syzcaller reports indicating that fasync code path may lead to some potential deadlocks for long time. Dropping the feature is the easiest solution, obviously.
I would probably prefer to fix the problem (deferred async kill or so). The SIGIO is just another way to wakeup the applications and there may be some corner cases, where this wakeup is usable (threaded apps). Note that we had some users (or testers) of SIGIO in past (at least there were questions about this support).
Hm, OK, then let's discard it for now.
The deferred kill_async() implementation would be something like below. A quick test looks OK, so far, with the given syzkaller reproducer.
Ideally speaking, this should be fixed in the fasync sighandler side, but it appears fragile -- kill_fasync() calls send_sigio(), and send_sigio() takes read_lock(&tasklist_lock). And read_lock(&tasklist_lock) itself is a common pattern taken in the non/soft-IRQ contexts, which would conflict with the call in an hard-IRQ context...
thanks,
Takashi
-- 8< -- diff --git a/include/sound/control.h b/include/sound/control.h index fcd3cce673ec..eae443ba79ba 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -109,7 +109,7 @@ struct snd_ctl_file { int preferred_subdevice[SND_CTL_SUBDEV_ITEMS]; wait_queue_head_t change_sleep; spinlock_t read_lock; - struct fasync_struct *fasync; + struct snd_fasync *fasync; int subscribed; /* read interface is activated */ struct list_head events; /* waiting events for read */ }; diff --git a/include/sound/core.h b/include/sound/core.h index dd28de2343b8..4365c35d038b 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -507,4 +507,12 @@ snd_pci_quirk_lookup_id(u16 vendor, u16 device, } #endif
+/* async signal helpers */ +struct snd_fasync; + +int snd_fasync_helper(int fd, struct file *file, int on, + struct snd_fasync **fasyncp); +void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll); +void snd_fasync_free(struct snd_fasync *fasync); + #endif /* __SOUND_CORE_H */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 2d03c10f6a36..8c48a5bce88c 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -399,7 +399,7 @@ struct snd_pcm_runtime { snd_pcm_uframes_t twake; /* do transfer (!poll) wakeup if non-zero */ wait_queue_head_t sleep; /* poll sleep */ wait_queue_head_t tsleep; /* transfer sleep */ - struct fasync_struct *fasync; + struct snd_fasync *fasync; bool stop_operating; /* sync_stop will be called */ struct mutex buffer_mutex; /* protect for buffer changes */ atomic_t buffer_accessing; /* >0: in r/w operation, <0: blocked */ diff --git a/sound/core/control.c b/sound/core/control.c index 4dba3a342458..f3e893715369 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -127,6 +127,7 @@ static int snd_ctl_release(struct inode *inode, struct file *file) if (control->vd[idx].owner == ctl) control->vd[idx].owner = NULL; up_write(&card->controls_rwsem); + snd_fasync_free(ctl->fasync); snd_ctl_empty_read_queue(ctl); put_pid(ctl->pid); kfree(ctl); @@ -181,7 +182,7 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask, _found: wake_up(&ctl->change_sleep); spin_unlock(&ctl->read_lock); - kill_fasync(&ctl->fasync, SIGIO, POLL_IN); + snd_kill_fasync(ctl->fasync, SIGIO, POLL_IN); } read_unlock_irqrestore(&card->ctl_files_rwlock, flags); } @@ -2134,7 +2135,7 @@ static int snd_ctl_fasync(int fd, struct file * file, int on) struct snd_ctl_file *ctl;
ctl = file->private_data; - return fasync_helper(fd, file, on, &ctl->fasync); + return snd_fasync_helper(fd, file, on, &ctl->fasync); }
/* return the preferred subdevice number if already assigned; @@ -2302,7 +2303,7 @@ static int snd_ctl_dev_disconnect(struct snd_device *device) read_lock_irqsave(&card->ctl_files_rwlock, flags); list_for_each_entry(ctl, &card->ctl_files, list) { wake_up(&ctl->change_sleep); - kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); + snd_kill_fasync(ctl->fasync, SIGIO, POLL_ERR); } read_unlock_irqrestore(&card->ctl_files_rwlock, flags);
diff --git a/sound/core/misc.c b/sound/core/misc.c index 50e4aaa6270d..f9d100905b98 100644 --- a/sound/core/misc.c +++ b/sound/core/misc.c @@ -145,3 +145,53 @@ snd_pci_quirk_lookup(struct pci_dev *pci, const struct snd_pci_quirk *list) } EXPORT_SYMBOL(snd_pci_quirk_lookup); #endif + +/* async signal helpers */ +struct snd_fasync { + struct fasync_struct *fasync; + struct work_struct work; + int signal; + int poll; +}; + +static void snd_fasync_work(struct work_struct *work) +{ + struct snd_fasync *fasync = container_of(work, struct snd_fasync, work); + + kill_fasync(&fasync->fasync, fasync->signal, fasync->poll); +} + +int snd_fasync_helper(int fd, struct file *file, int on, + struct snd_fasync **fasyncp) +{ + struct snd_fasync *fasync = *fasyncp; + + if (!fasync) { + fasync = kzalloc(sizeof(*fasync), GFP_KERNEL); + if (!fasync) + return -ENOMEM; + INIT_WORK(&fasync->work, snd_fasync_work); + *fasyncp = fasync; + } + return fasync_helper(fd, file, on, &fasync->fasync); +} +EXPORT_SYMBOL_GPL(snd_fasync_helper); + +void snd_kill_fasync(struct snd_fasync *fasync, int signal, int poll) +{ + if (!fasync) + return; + fasync->signal = signal; + fasync->poll = poll; + schedule_work(&fasync->work); +} +EXPORT_SYMBOL_GPL(snd_kill_fasync); + +void snd_fasync_free(struct snd_fasync *fasync) +{ + if (!fasync) + return; + cancel_work_sync(&fasync->work); + kfree(fasync); +} +EXPORT_SYMBOL_GPL(snd_fasync_free); diff --git a/sound/core/pcm.c b/sound/core/pcm.c index 03fc5fa5813e..2ac742035310 100644 --- a/sound/core/pcm.c +++ b/sound/core/pcm.c @@ -1007,6 +1007,7 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream) substream->runtime = NULL; } mutex_destroy(&runtime->buffer_mutex); + snd_fasync_free(runtime->fasync); kfree(runtime); put_pid(substream->pid); substream->pid = NULL; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c index 1fc7c50ffa62..40751e5aff09 100644 --- a/sound/core/pcm_lib.c +++ b/sound/core/pcm_lib.c @@ -1822,7 +1822,7 @@ void snd_pcm_period_elapsed_under_stream_lock(struct snd_pcm_substream *substrea snd_timer_interrupt(substream->timer, 1); #endif _end: - kill_fasync(&runtime->fasync, SIGIO, POLL_IN); + snd_kill_fasync(runtime->fasync, SIGIO, POLL_IN); } EXPORT_SYMBOL(snd_pcm_period_elapsed_under_stream_lock);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c index aa0453e51595..ad0541e9e888 100644 --- a/sound/core/pcm_native.c +++ b/sound/core/pcm_native.c @@ -3951,7 +3951,7 @@ static int snd_pcm_fasync(int fd, struct file * file, int on) runtime = substream->runtime; if (runtime->status->state == SNDRV_PCM_STATE_DISCONNECTED) return -EBADFD; - return fasync_helper(fd, file, on, &runtime->fasync); + return snd_fasync_helper(fd, file, on, &runtime->fasync); }
/* diff --git a/sound/core/timer.c b/sound/core/timer.c index b3214baa8919..e08a37c23add 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -83,7 +83,7 @@ struct snd_timer_user { unsigned int filter; struct timespec64 tstamp; /* trigger tstamp */ wait_queue_head_t qchange_sleep; - struct fasync_struct *fasync; + struct snd_fasync *fasync; struct mutex ioctl_lock; };
@@ -1345,7 +1345,7 @@ static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, } __wake: spin_unlock(&tu->qlock); - kill_fasync(&tu->fasync, SIGIO, POLL_IN); + snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); }
@@ -1383,7 +1383,7 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, spin_lock_irqsave(&tu->qlock, flags); snd_timer_user_append_to_tqueue(tu, &r1); spin_unlock_irqrestore(&tu->qlock, flags); - kill_fasync(&tu->fasync, SIGIO, POLL_IN); + snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); }
@@ -1453,7 +1453,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, spin_unlock(&tu->qlock); if (append == 0) return; - kill_fasync(&tu->fasync, SIGIO, POLL_IN); + snd_kill_fasync(tu->fasync, SIGIO, POLL_IN); wake_up(&tu->qchange_sleep); }
@@ -1521,6 +1521,7 @@ static int snd_timer_user_release(struct inode *inode, struct file *file) snd_timer_instance_free(tu->timeri); } mutex_unlock(&tu->ioctl_lock); + snd_fasync_free(tu->fasync); kfree(tu->queue); kfree(tu->tqueue); kfree(tu); @@ -2135,7 +2136,7 @@ static int snd_timer_user_fasync(int fd, struct file * file, int on) struct snd_timer_user *tu;
tu = file->private_data; - return fasync_helper(fd, file, on, &tu->fasync); + return snd_fasync_helper(fd, file, on, &tu->fasync); }
static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
participants (2)
-
Jaroslav Kysela
-
Takashi Iwai