All of lore.kernel.org
 help / color / mirror / Atom feed
* pcm_hw bug when calling snd_pcm_sw_params twice
@ 2019-01-07 23:26 Jamey Sharp
  2019-01-09 11:08 ` Takashi Iwai
  0 siblings, 1 reply; 2+ messages in thread
From: Jamey Sharp @ 2019-01-07 23:26 UTC (permalink / raw)
  To: alsa-devel

[-- Attachment #1: Type: text/plain, Size: 783 bytes --]

I would have reported this to your bug tracker, but I can't find it; the
links on alsa-project.org are dead.

I've attached a small test program that demonstrates that calling
snd_pcm_sw_params twice changes the value of the period_event flag in
the sw_params struct, at least on pcm_hw devices.

In src/pcm/pcm_hw.c, in snd_pcm_hw_sw_params, there's an early
`sw_set_period_event(params, 0)` call. Its effect is not undone if the
function returns early. This is easiest to trigger by calling it with
unchanged parameters, although there are other early-exit paths with the
same problem.

I don't actually care about this bug, but since I noticed it while
reading the alsa-lib source code trying to figure out what a period
event is for, I thought I'd go ahead and report it.

Jamey

[-- Attachment #2: alsa_period_event.c --]
[-- Type: text/plain, Size: 1323 bytes --]

#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>

#define check(v) if(v < 0) { perror(#v); exit(1); }

int main(void)
{
	snd_pcm_t *handle;
	snd_pcm_hw_params_t *hw_params;
	snd_pcm_sw_params_t *sw_params;
	int val = 0;
	char *name = getenv("DEVICE");

	snd_pcm_hw_params_alloca(&hw_params);
	snd_pcm_sw_params_alloca(&sw_params);

	if(!name)
		name = "hw:0";
	printf("opening %s\n", name);

	check(snd_pcm_open(&handle, name, SND_PCM_STREAM_PLAYBACK, 0));
	check(snd_pcm_hw_params_any(handle, hw_params));
	check(snd_pcm_hw_params(handle, hw_params));
	check(snd_pcm_sw_params_current(handle, sw_params));

	// Enable the period event
	check(snd_pcm_sw_params_set_period_event(handle, sw_params, 1));
	check(snd_pcm_sw_params_get_period_event(sw_params, &val));
	assert(val == 1);

	// After applying sw params once, the params struct still says the
	// period event is enabled.
	check(snd_pcm_sw_params(handle, sw_params));
	check(snd_pcm_sw_params_get_period_event(sw_params, &val));
	assert(val == 1);

	// Applying the same sw params a second time should still leave the
	// period event enabled, but this assert fails.
	check(snd_pcm_sw_params(handle, sw_params));
	check(snd_pcm_sw_params_get_period_event(sw_params, &val));
	assert(val == 1);

	return 0;
}

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: pcm_hw bug when calling snd_pcm_sw_params twice
  2019-01-07 23:26 pcm_hw bug when calling snd_pcm_sw_params twice Jamey Sharp
@ 2019-01-09 11:08 ` Takashi Iwai
  0 siblings, 0 replies; 2+ messages in thread
From: Takashi Iwai @ 2019-01-09 11:08 UTC (permalink / raw)
  To: Jamey Sharp; +Cc: alsa-devel

On Tue, 08 Jan 2019 00:26:20 +0100,
Jamey Sharp wrote:
> 
> I would have reported this to your bug tracker, but I can't find it; the
> links on alsa-project.org are dead.
> 
> I've attached a small test program that demonstrates that calling
> snd_pcm_sw_params twice changes the value of the period_event flag in
> the sw_params struct, at least on pcm_hw devices.
> 
> In src/pcm/pcm_hw.c, in snd_pcm_hw_sw_params, there's an early
> `sw_set_period_event(params, 0)` call. Its effect is not undone if the
> function returns early. This is easiest to trigger by calling it with
> unchanged parameters, although there are other early-exit paths with the
> same problem.
> 
> I don't actually care about this bug, but since I noticed it while
> reading the alsa-lib source code trying to figure out what a period
> event is for, I thought I'd go ahead and report it.

Thanks for the report.  The patch below should fix the issue.
I'm going to merge it.


Takashi

-- 8< --
From: Takashi Iwai <tiwai@suse.de>
Subject: [PATCH] pcm: Preserve period_event in snd_pcm_hw_sw_params() call

snd_pcm_hw_sw_params() in pcm_hw.c tries to abuse the reserved bits
for passing period_Event flag.  In this hackish way, we clear the
reserved bits at beginning, and restore before returning.  However,
the code paths that return earlier don't restore the value, hence when
user calls this function twice, it may pass an unexpected value.

This patch fixes the failure, restoring the value always before
returning from the function.

Reported-by: Jamey Sharp <jamey@minilop.net>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 src/pcm/pcm_hw.c | 22 +++++++++++++---------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/src/pcm/pcm_hw.c b/src/pcm/pcm_hw.c
index 59a242009e9f..91370a88c0fd 100644
--- a/src/pcm/pcm_hw.c
+++ b/src/pcm/pcm_hw.c
@@ -496,7 +496,7 @@ static int snd_pcm_hw_hw_free(snd_pcm_t *pcm)
 static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
 {
 	snd_pcm_hw_t *hw = pcm->private_data;
-	int fd = hw->fd, err;
+	int fd = hw->fd, err = 0;
 	int old_period_event = sw_get_period_event(params);
 	sw_set_period_event(params, 0);
 	if ((snd_pcm_tstamp_t) params->tstamp_mode == pcm->tstamp_mode &&
@@ -508,22 +508,25 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
 	    params->silence_size == pcm->silence_size &&
 	    old_period_event == hw->period_event) {
 		hw->mmap_control->avail_min = params->avail_min;
-		return issue_avail_min(hw);
+		err = issue_avail_min(hw);
+		goto out;
 	}
 	if (params->tstamp_type == SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW &&
 	    hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
 		SYSMSG("Kernel doesn't support SND_PCM_TSTAMP_TYPE_MONOTONIC_RAW");
-		return -EINVAL;
+		err = -EINVAL;
+		goto out;
 	}
 	if (params->tstamp_type == SND_PCM_TSTAMP_TYPE_MONOTONIC &&
 	    hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 5)) {
 		SYSMSG("Kernel doesn't support SND_PCM_TSTAMP_TYPE_MONOTONIC");
-		return -EINVAL;
+		err = -EINVAL;
+		goto out;
 	}
 	if (ioctl(fd, SNDRV_PCM_IOCTL_SW_PARAMS, params) < 0) {
 		err = -errno;
 		SYSMSG("SNDRV_PCM_IOCTL_SW_PARAMS failed (%i)", err);
-		return err;
+		goto out;
 	}
 	if ((snd_pcm_tstamp_type_t) params->tstamp_type != pcm->tstamp_type) {
 		if (hw->version < SNDRV_PROTOCOL_VERSION(2, 0, 12)) {
@@ -532,20 +535,21 @@ static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
 			if (ioctl(fd, SNDRV_PCM_IOCTL_TSTAMP, &on) < 0) {
 				err = -errno;
 				SNDMSG("TSTAMP failed\n");
-				return err;
+				goto out;
 			}
 		}
 		pcm->tstamp_type = params->tstamp_type;
 	}
-	sw_set_period_event(params, old_period_event);
 	hw->mmap_control->avail_min = params->avail_min;
 	if (hw->period_event != old_period_event) {
 		err = snd_pcm_hw_change_timer(pcm, old_period_event);
 		if (err < 0)
-			return err;
+			goto out;
 		hw->period_event = old_period_event;
 	}
-	return 0;
+ out:
+	sw_set_period_event(params, old_period_event);
+	return err;
 }
 
 static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
-- 
2.20.1

^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2019-01-09 11:08 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-07 23:26 pcm_hw bug when calling snd_pcm_sw_params twice Jamey Sharp
2019-01-09 11:08 ` Takashi Iwai

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.