From 1a6a071f51a5373de21edd34f9b79610be2d02e3 Mon Sep 17 00:00:00 2001 From: Yu Hao Date: Tue, 3 Nov 2020 14:33:35 -0800 Subject: ALSA: seq: remove useless function The function snd_seq_queue_client_termination() is only called from the function seq_free_client1(). The function seq_free_client1() calls the function snd_seq_queue_client_leave() and the function snd_seq_queue_client_termination() together. Because the function snd_seq_queue_client_leave() does all things, so the function snd_seq_queue_client_termination() is a useless function. Signed-off-by: Yu Hao Link: https://lore.kernel.org/r/20201103223335.21831-1-yuhaobehappy@gmail.com Signed-off-by: Takashi Iwai --- sound/core/seq/seq_clientmgr.c | 1 - sound/core/seq/seq_queue.c | 27 --------------------------- sound/core/seq/seq_queue.h | 3 --- 3 files changed, 31 deletions(-) (limited to 'sound/core') diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index cc93157fa950..f9f2fea58b32 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -279,7 +279,6 @@ static int seq_free_client1(struct snd_seq_client *client) snd_seq_delete_all_ports(client); snd_seq_queue_client_leave(client->number); snd_use_lock_sync(&client->use_lock); - snd_seq_queue_client_termination(client->number); if (client->pool) snd_seq_pool_delete(&client->pool); spin_lock_irq(&clients_lock); diff --git a/sound/core/seq/seq_queue.c b/sound/core/seq/seq_queue.c index 71a6ea62c3be..13cfc2d47fa7 100644 --- a/sound/core/seq/seq_queue.c +++ b/sound/core/seq/seq_queue.c @@ -537,33 +537,6 @@ int snd_seq_queue_is_used(int queueid, int client) /*----------------------------------------------------------------*/ -/* notification that client has left the system - - * stop the timer on all queues owned by this client - */ -void snd_seq_queue_client_termination(int client) -{ - unsigned long flags; - int i; - struct snd_seq_queue *q; - bool matched; - - for (i = 0; i < SNDRV_SEQ_MAX_QUEUES; i++) { - if ((q = queueptr(i)) == NULL) - continue; - spin_lock_irqsave(&q->owner_lock, flags); - matched = (q->owner == client); - if (matched) - q->klocked = 1; - spin_unlock_irqrestore(&q->owner_lock, flags); - if (matched) { - if (q->timer->running) - snd_seq_timer_stop(q->timer); - snd_seq_timer_reset(q->timer); - } - queuefree(q); - } -} - /* final stage notification - * remove cells for no longer exist client (for non-owned queue) * or delete this queue (for owned queue) diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h index 9254c8dbe5e3..1c3a8d3254d9 100644 --- a/sound/core/seq/seq_queue.h +++ b/sound/core/seq/seq_queue.h @@ -59,9 +59,6 @@ struct snd_seq_queue *snd_seq_queue_alloc(int client, int locked, unsigned int f /* delete queue (destructor) */ int snd_seq_queue_delete(int client, int queueid); -/* notification that client has left the system */ -void snd_seq_queue_client_termination(int client); - /* final stage */ void snd_seq_queue_client_leave(int client); -- cgit v1.2.3 From 9be9f2d3d073ef42127475f4fb6a392ab133f629 Mon Sep 17 00:00:00 2001 From: Gyeongtaek Lee Date: Thu, 26 Nov 2020 21:34:52 +0900 Subject: ALSA: compress: allow pause and resume during draining With a stream with low bitrate, user can't pause or resume the stream near the end of the stream because current ALSA doesn't allow it. If the stream has very low bitrate enough to store whole stream into the buffer, user can't do anything except stop the stream and then restart it from the first because most of applications call draining after sending last frame to the kernel. If pause, resume are allowed during draining, user experience can be enhanced. To prevent malfunction in HW drivers which don't support pause during draining, pause during draining will only work if HW driver enable this feature explicitly by calling snd_compr_use_pause_in_draining(). Signed-off-by: Gyeongtaek Lee Acked-by: Vinod Koul Link: https://lore.kernel.org/r/000101d6c3f0$89b312b0$9d193810$@samsung.com Signed-off-by: Takashi Iwai --- include/sound/compress_driver.h | 16 ++++++++++++++++ sound/core/compress_offload.c | 39 +++++++++++++++++++++++++++++++-------- 2 files changed, 47 insertions(+), 8 deletions(-) (limited to 'sound/core') diff --git a/include/sound/compress_driver.h b/include/sound/compress_driver.h index 70cbc5095e72..277087f635f3 100644 --- a/include/sound/compress_driver.h +++ b/include/sound/compress_driver.h @@ -67,6 +67,7 @@ struct snd_compr_runtime { * @metadata_set: metadata set flag, true when set * @next_track: has userspace signal next track transition, true when set * @partial_drain: undergoing partial_drain for stream, true when set + * @pause_in_draining: paused during draining state, true when set * @private_data: pointer to DSP private data * @dma_buffer: allocated buffer if any */ @@ -80,6 +81,7 @@ struct snd_compr_stream { bool metadata_set; bool next_track; bool partial_drain; + bool pause_in_draining; void *private_data; struct snd_dma_buffer dma_buffer; }; @@ -142,6 +144,7 @@ struct snd_compr_ops { * @direction: Playback or capture direction * @lock: device lock * @device: device id + * @use_pause_in_draining: allow pause in draining, true when set */ struct snd_compr { const char *name; @@ -152,6 +155,7 @@ struct snd_compr { unsigned int direction; struct mutex lock; int device; + bool use_pause_in_draining; #ifdef CONFIG_SND_VERBOSE_PROCFS /* private: */ char id[64]; @@ -166,6 +170,18 @@ int snd_compress_deregister(struct snd_compr *device); int snd_compress_new(struct snd_card *card, int device, int type, const char *id, struct snd_compr *compr); +/** + * snd_compr_use_pause_in_draining - Allow pause and resume in draining state + * @substream: compress substream to set + * + * Allow pause and resume in draining state. + * Only HW driver supports this transition can call this API. + */ +static inline void snd_compr_use_pause_in_draining(struct snd_compr_stream *substream) +{ + substream->device->use_pause_in_draining = true; +} + /* dsp driver callback apis * For playback: driver should call snd_compress_fragment_elapsed() to let the * framework know that a fragment has been consumed from the ring buffer diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c index c1fec932c49d..debc30fcf5b3 100644 --- a/sound/core/compress_offload.c +++ b/sound/core/compress_offload.c @@ -709,11 +709,22 @@ static int snd_compr_pause(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_RUNNING: + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_PAUSED; + break; + case SNDRV_PCM_STATE_DRAINING: + if (!stream->device->use_pause_in_draining) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); + if (!retval) + stream->pause_in_draining = true; + break; + default: return -EPERM; - retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_PUSH); - if (!retval) - stream->runtime->state = SNDRV_PCM_STATE_PAUSED; + } return retval; } @@ -721,11 +732,22 @@ static int snd_compr_resume(struct snd_compr_stream *stream) { int retval; - if (stream->runtime->state != SNDRV_PCM_STATE_PAUSED) + switch (stream->runtime->state) { + case SNDRV_PCM_STATE_PAUSED: + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (!retval) + stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + break; + case SNDRV_PCM_STATE_DRAINING: + if (!stream->pause_in_draining) + return -EPERM; + retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); + if (!retval) + stream->pause_in_draining = false; + break; + default: return -EPERM; - retval = stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_PAUSE_RELEASE); - if (!retval) - stream->runtime->state = SNDRV_PCM_STATE_RUNNING; + } return retval; } @@ -768,6 +790,7 @@ static int snd_compr_stop(struct snd_compr_stream *stream) /* clear flags and stop any drain wait */ stream->partial_drain = false; stream->metadata_set = false; + stream->pause_in_draining = false; snd_compr_drain_notify(stream); stream->runtime->total_bytes_available = 0; stream->runtime->total_bytes_transferred = 0; -- cgit v1.2.3 From 4ebd47037027c4beae99680bff3b20fdee5d7c1e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 6 Dec 2020 09:34:56 +0100 Subject: ALSA: seq: Use bool for snd_seq_queue internal flags The snd_seq_queue struct contains various flags in the bit fields. Those are categorized to two different use cases, both of which are protected by different spinlocks. That implies that there are still potential risks of the bad operations for bit fields by concurrent accesses. For addressing the problem, this patch rearranges those flags to be a standard bool instead of a bit field. Reported-by: syzbot+63cbe31877bb80ef58f5@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/20201206083456.21110-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/seq/seq_queue.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'sound/core') diff --git a/sound/core/seq/seq_queue.h b/sound/core/seq/seq_queue.h index 1c3a8d3254d9..c69105dc1a10 100644 --- a/sound/core/seq/seq_queue.h +++ b/sound/core/seq/seq_queue.h @@ -26,10 +26,10 @@ struct snd_seq_queue { struct snd_seq_timer *timer; /* time keeper for this queue */ int owner; /* client that 'owns' the timer */ - unsigned int locked:1, /* timer is only accesibble by owner if set */ - klocked:1, /* kernel lock (after START) */ - check_again:1, - check_blocked:1; + bool locked; /* timer is only accesibble by owner if set */ + bool klocked; /* kernel lock (after START) */ + bool check_again; /* concurrent access happened during check */ + bool check_blocked; /* queue being checked */ unsigned int flags; /* status flags */ unsigned int info_flags; /* info for sync */ -- cgit v1.2.3 From 88a06d6fd6b369d88cec46c62db3e2604a2f50d5 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Sun, 6 Dec 2020 09:35:27 +0100 Subject: ALSA: rawmidi: Access runtime->avail always in spinlock The runtime->avail field may be accessed concurrently while some places refer to it without taking the runtime->lock spinlock, as detected by KCSAN. Usually this isn't a big problem, but for consistency and safety, we should take the spinlock at each place referencing this field. Reported-by: syzbot+a23a6f1215c84756577c@syzkaller.appspotmail.com Reported-by: syzbot+3d367d1df1d2b67f5c19@syzkaller.appspotmail.com Link: https://lore.kernel.org/r/20201206083527.21163-1-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/rawmidi.c | 49 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 35 insertions(+), 14 deletions(-) (limited to 'sound/core') diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index c78720a3299c..257ad5206240 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -95,11 +95,21 @@ static inline unsigned short snd_rawmidi_file_flags(struct file *file) } } -static inline int snd_rawmidi_ready(struct snd_rawmidi_substream *substream) +static inline bool __snd_rawmidi_ready(struct snd_rawmidi_runtime *runtime) +{ + return runtime->avail >= runtime->avail_min; +} + +static bool snd_rawmidi_ready(struct snd_rawmidi_substream *substream) { struct snd_rawmidi_runtime *runtime = substream->runtime; + unsigned long flags; + bool ready; - return runtime->avail >= runtime->avail_min; + spin_lock_irqsave(&runtime->lock, flags); + ready = __snd_rawmidi_ready(runtime); + spin_unlock_irqrestore(&runtime->lock, flags); + return ready; } static inline int snd_rawmidi_ready_append(struct snd_rawmidi_substream *substream, @@ -1019,7 +1029,7 @@ int snd_rawmidi_receive(struct snd_rawmidi_substream *substream, if (result > 0) { if (runtime->event) schedule_work(&runtime->event_work); - else if (snd_rawmidi_ready(substream)) + else if (__snd_rawmidi_ready(runtime)) wake_up(&runtime->sleep); } spin_unlock_irqrestore(&runtime->lock, flags); @@ -1098,7 +1108,7 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun result = 0; while (count > 0) { spin_lock_irq(&runtime->lock); - while (!snd_rawmidi_ready(substream)) { + while (!__snd_rawmidi_ready(runtime)) { wait_queue_entry_t wait; if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { @@ -1115,9 +1125,11 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; - if (!runtime->avail) - return result > 0 ? result : -EIO; spin_lock_irq(&runtime->lock); + if (!runtime->avail) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EIO; + } } spin_unlock_irq(&runtime->lock); count1 = snd_rawmidi_kernel_read1(substream, @@ -1255,7 +1267,7 @@ int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int coun runtime->avail += count; substream->bytes += count; if (count > 0) { - if (runtime->drain || snd_rawmidi_ready(substream)) + if (runtime->drain || __snd_rawmidi_ready(runtime)) wake_up(&runtime->sleep); } return count; @@ -1444,9 +1456,11 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; - if (!runtime->avail && !timeout) - return result > 0 ? result : -EIO; spin_lock_irq(&runtime->lock); + if (!runtime->avail && !timeout) { + spin_unlock_irq(&runtime->lock); + return result > 0 ? result : -EIO; + } } spin_unlock_irq(&runtime->lock); count1 = snd_rawmidi_kernel_write1(substream, buf, NULL, count); @@ -1526,6 +1540,7 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, struct snd_rawmidi *rmidi; struct snd_rawmidi_substream *substream; struct snd_rawmidi_runtime *runtime; + unsigned long buffer_size, avail, xruns; rmidi = entry->private_data; snd_iprintf(buffer, "%s\n\n", rmidi->name); @@ -1544,13 +1559,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; + spin_lock_irq(&runtime->lock); + buffer_size = runtime->buffer_size; + avail = runtime->avail; + spin_unlock_irq(&runtime->lock); snd_iprintf(buffer, " Mode : %s\n" " Buffer size : %lu\n" " Avail : %lu\n", runtime->oss ? "OSS compatible" : "native", - (unsigned long) runtime->buffer_size, - (unsigned long) runtime->avail); + buffer_size, avail); } } } @@ -1568,13 +1586,16 @@ static void snd_rawmidi_proc_info_read(struct snd_info_entry *entry, " Owner PID : %d\n", pid_vnr(substream->pid)); runtime = substream->runtime; + spin_lock_irq(&runtime->lock); + buffer_size = runtime->buffer_size; + avail = runtime->avail; + xruns = runtime->xruns; + spin_unlock_irq(&runtime->lock); snd_iprintf(buffer, " Buffer size : %lu\n" " Avail : %lu\n" " Overruns : %lu\n", - (unsigned long) runtime->buffer_size, - (unsigned long) runtime->avail, - (unsigned long) runtime->xruns); + buffer_size, avail, xruns); } } } -- cgit v1.2.3 From 175b8d89fe292796811fdee87fa39799a5b6b87a Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Wed, 9 Dec 2020 09:45:52 +0100 Subject: ALSA: pcm: oss: Fix potential out-of-bounds shift syzbot spotted a potential out-of-bounds shift in the PCM OSS layer where it calculates the buffer size with the arbitrary shift value given via an ioctl. Add a range check for avoiding the undefined behavior. As the value can be treated by a signed integer, the max shift should be 30. Reported-by: syzbot+df7dc146ebdd6435eea3@syzkaller.appspotmail.com Cc: Link: https://lore.kernel.org/r/20201209084552.17109-2-tiwai@suse.de Signed-off-by: Takashi Iwai --- sound/core/oss/pcm_oss.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'sound/core') diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c index 327ec42a36b0..de1917484647 100644 --- a/sound/core/oss/pcm_oss.c +++ b/sound/core/oss/pcm_oss.c @@ -1935,11 +1935,15 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsigned int val) { struct snd_pcm_runtime *runtime; + int fragshift; runtime = substream->runtime; if (runtime->oss.subdivision || runtime->oss.fragshift) return -EINVAL; - runtime->oss.fragshift = val & 0xffff; + fragshift = val & 0xffff; + if (fragshift >= 31) + return -EINVAL; + runtime->oss.fragshift = fragshift; runtime->oss.maxfrags = (val >> 16) & 0xffff; if (runtime->oss.fragshift < 4) /* < 16 */ runtime->oss.fragshift = 4; -- cgit v1.2.3