mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Takashi Iwai <tiwai@suse.de>
To: Dmitry Vyukov <dvyukov@google.com>
Cc: alsa-devel@alsa-project.org,
	Peter Zijlstra <peterz@infradead.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	Jaroslav Kysela <perex@perex.cz>,
	LKML <linux-kernel@vger.kernel.org>,
	Alexander Potapenko <glider@google.com>,
	Kostya Serebryany <kcc@google.com>,
	syzkaller <syzkaller@googlegroups.com>,
	Sasha Levin <sasha.levin@oracle.com>
Subject: Re: sound: use-after-free in snd_timer_interrupt
Date: Wed, 20 Apr 2016 12:31:00 +0200	[thread overview]
Message-ID: <s5h37qg372z.wl-tiwai@suse.de> (raw)
In-Reply-To: <s5hd1pkzoq0.wl-tiwai@suse.de>

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

On Wed, 20 Apr 2016 10:08:55 +0200,
Takashi Iwai wrote:
> 
> On Wed, 20 Apr 2016 09:56:04 +0200,
> Dmitry Vyukov wrote:
> > 
> > On Sun, Apr 3, 2016 at 8:33 AM, Takashi Iwai <tiwai@suse.de> wrote:
> > >> >> It is not easily reproducible. I've hit several times while running
> > >> >> fuzzer for a week. Here is one of the logs for the record:
> > >> >> https://gist.githubusercontent.com/dvyukov/c84798ee55721563ecb537c4d51dc9f5/raw/f00b865a85877656f13b41917f7321730f140d35/gistfile1.txt
> > >> >
> > >> > There are a few more fixes in sound/core/timer.c since 4.5, and they
> > >> > possibly already cover this.
> > >> >
> > >> > Please let me know if this is still seen on the upcoming 4.6-rc2.
> > >>
> > >> Hi Takashi,
> > >>
> > >> I've updated fuzzer to 05cf8077e54b20dddb756eaa26f3aeb5c38dd3cf (Apr
> > >> 1) yesterday. Let's see if it still happens.
> > >>
> > >> Out of curiosity, how was the bug found?
> > >
> > > Well, I'm not entirely sure whether they really cover.  It's just a
> > > hope, as these are patches to close some possible races :)
> > >
> > > 9984d1b5835ca29fc7025186a891ee7398d21cc7
> > >     ALSA: timer: Protect the whole snd_timer_close() with open race
> > > f65e0d299807d8a11812845c972493c3f9a18e10
> > >     ALSA: timer: Call notifier in the same spinlock
> > > 4a07083ed613644c96c34a7dd2853dc5d7c70902
> > >     ALSA: timer: Use mod_timer() for rearming the system timer
> > 
> > 
> > Hi Takashi,
> > 
> > I've hit it again on 806fdcce017dc98c4dbf8ed001750a0d7d2bb0af (Apr
> > 14), all 3 commits are already in my tree.
> > 
> > [  343.222218] ------------[ cut here ]------------
> > [  343.222218] WARNING: CPU: 3 PID: 7040 at kernel/time/hrtimer.c:837
> > hrtimer_forward+0x26a/0x3e0
> 
> This is a different warning.  The previous was use-after-free, and
> this is a warning about re-arming the queued hrtimer.
> Maybe there is a slightly remaining race about hrtimer_start() and the
> interrupt handler in snd-hrtimer.

Could you check whether two patches below help anything?
This should harden against the race between hrtimer callback and
another start/stop calls.


Takashi


[-- Attachment #2: 0001-ALSA-timer-Allow-backend-disabling-start-stop-from-h.patch --]
[-- Type: application/octet-stream, Size: 4254 bytes --]

>From 18dee7aebf62375a224025ee0ee287c36784aa6d Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Wed, 20 Apr 2016 12:02:36 +0200
Subject: [PATCH 1/2] ALSA: timer: Allow backend disabling start/stop from
 handler

Some timer backend doesn't particularly like (re)start / stop calls
from its interrupt handler.  For example, hrtimer can't stop properly
with sync, and we still seem to have some open race.

This patch introduced a new flag, SNDRV_TIMER_HW_RET_CTRL, so that the
timer backend can specify whether snd_timer_interrupt() should call hw
start() and hw.stop() callbacks or not.  If the new flag is set,
snd_timer_interrupt() won't call hw.start() and hw.stop() callbacks
but return SNDRV_TIMER_RET_START and SNDRV_TIMER_RET_STOP,
respectively.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 include/sound/timer.h | 12 +++++++++++-
 sound/core/timer.c    | 24 ++++++++++++++++++------
 2 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/include/sound/timer.h b/include/sound/timer.h
index c4d76ff056c6..6ca6ed4169da 100644
--- a/include/sound/timer.h
+++ b/include/sound/timer.h
@@ -37,6 +37,7 @@
 #define SNDRV_TIMER_HW_SLAVE	0x00000004	/* only slave timer (variable resolution) */
 #define SNDRV_TIMER_HW_FIRST	0x00000008	/* first tick can be incomplete */
 #define SNDRV_TIMER_HW_TASKLET	0x00000010	/* timer is called from tasklet */
+#define SNDRV_TIMER_HW_RET_CTRL	0x00000020	/* don't start/stop at irq handler */
 
 #define SNDRV_TIMER_IFLG_SLAVE	  0x00000001
 #define SNDRV_TIMER_IFLG_RUNNING  0x00000002
@@ -50,6 +51,15 @@
 #define SNDRV_TIMER_FLG_CHANGE	0x00000001
 #define SNDRV_TIMER_FLG_RESCHED	0x00000002	/* need reschedule */
 
+/* return value from snd_timer_interrupt();
+ * START and STOP are returned only when SNDRV_TIMER_HW_RET_CTRL is set
+ */
+enum {
+	SNDRV_TIMER_RET_NONE = 0,
+	SNDRV_TIMER_RET_START = 1,
+	SNDRV_TIMER_RET_STOP = 2,
+};
+
 struct snd_timer;
 
 struct snd_timer_hardware {
@@ -139,6 +149,6 @@ int snd_timer_stop(struct snd_timer_instance *timeri);
 int snd_timer_continue(struct snd_timer_instance *timeri);
 int snd_timer_pause(struct snd_timer_instance *timeri);
 
-void snd_timer_interrupt(struct snd_timer *timer, unsigned long ticks_left);
+int snd_timer_interrupt(struct snd_timer *timer, unsigned long ticks_left);
 
 #endif /* __SOUND_TIMER_H */
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 6469bedda2f3..c653c409d74d 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -683,19 +683,20 @@ static void snd_timer_tasklet(unsigned long arg)
  * ticks_left is usually equal to timer->sticks.
  *
  */
-void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
+int snd_timer_interrupt(struct snd_timer *timer, unsigned long ticks_left)
 {
 	struct snd_timer_instance *ti, *ts, *tmp;
 	unsigned long resolution, ticks;
 	struct list_head *p, *ack_list_head;
 	unsigned long flags;
 	int use_tasklet = 0;
+	int ret = 0;
 
 	if (timer == NULL)
-		return;
+		return -ENODEV;
 
 	if (timer->card && timer->card->shutdown)
-		return;
+		return -ENODEV;
 
 	spin_lock_irqsave(&timer->lock, flags);
 
@@ -747,17 +748,26 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
 		snd_timer_reschedule(timer, timer->sticks);
 	if (timer->running) {
 		if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
-			timer->hw.stop(timer);
+			if (timer->hw.flags & SNDRV_TIMER_HW_RET_CTRL)
+				ret = SNDRV_TIMER_RET_STOP;
+			else
+				timer->hw.stop(timer);
 			timer->flags |= SNDRV_TIMER_FLG_CHANGE;
 		}
 		if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
 		    (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
 			/* restart timer */
 			timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
-			timer->hw.start(timer);
+			if (timer->hw.flags & SNDRV_TIMER_HW_RET_CTRL)
+				ret = SNDRV_TIMER_RET_START;
+			else
+				timer->hw.start(timer);
 		}
 	} else {
-		timer->hw.stop(timer);
+		if (timer->hw.flags & SNDRV_TIMER_HW_RET_CTRL)
+			ret = SNDRV_TIMER_RET_STOP;
+		else
+			timer->hw.stop(timer);
 	}
 
 	/* now process all fast callbacks */
@@ -785,6 +795,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
 
 	if (use_tasklet)
 		tasklet_schedule(&timer->task_queue);
+
+	return ret;
 }
 
 /*
-- 
2.8.1


[-- Attachment #3: 0002-ALSA-hrtimer-Use-manual-start-stop-in-callback.patch --]
[-- Type: application/octet-stream, Size: 3931 bytes --]

>From 360230be1a52e0f81470820cf2be4e7da50e25f8 Mon Sep 17 00:00:00 2001
From: Takashi Iwai <tiwai@suse.de>
Date: Wed, 20 Apr 2016 12:10:10 +0200
Subject: [PATCH 2/2] ALSA: hrtimer: Use manual start/stop in callback

With the new SNDRV_TIMER_HW_RET_CTRL flag, hrtimer can manage the
callback behavior more correctly.  Now it gets a return value from
snd_timer_interrupt() whether to reprogram or stop the timer, and it
can choose the right return value.

The biggest bonus by this change is that we can protect the whole
interrupt with a spinlock against start/stop calls from another
thread.  The patch adds a spinlock and converts the former atomic flag
into a normal bool flag.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
---
 sound/core/hrtimer.c | 51 ++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 38 insertions(+), 13 deletions(-)

diff --git a/sound/core/hrtimer.c b/sound/core/hrtimer.c
index 656d9a9032dc..8a1b51473fd5 100644
--- a/sound/core/hrtimer.c
+++ b/sound/core/hrtimer.c
@@ -38,7 +38,8 @@ static unsigned int resolution;
 struct snd_hrtimer {
 	struct snd_timer *timer;
 	struct hrtimer hrt;
-	atomic_t running;
+	spinlock_t lock;
+	bool running;
 };
 
 static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
@@ -46,16 +47,33 @@ static enum hrtimer_restart snd_hrtimer_callback(struct hrtimer *hrt)
 	struct snd_hrtimer *stime = container_of(hrt, struct snd_hrtimer, hrt);
 	struct snd_timer *t = stime->timer;
 	unsigned long oruns;
+	int irq_ret;
+	enum hrtimer_restart ret = HRTIMER_NORESTART;
+	unsigned long flags;
 
-	if (!atomic_read(&stime->running))
-		return HRTIMER_NORESTART;
+	spin_lock_irqsave(&stime->lock, flags);
+	if (!stime->running)
+		goto unlock;
 
 	oruns = hrtimer_forward_now(hrt, ns_to_ktime(t->sticks * resolution));
-	snd_timer_interrupt(stime->timer, t->sticks * oruns);
+	irq_ret = snd_timer_interrupt(stime->timer, t->sticks * oruns);
+
+	switch (irq_ret) {
+	case SNDRV_TIMER_RET_START:
+		hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
+			      HRTIMER_MODE_REL);
+		/* fallthru */
+	case SNDRV_TIMER_RET_NONE:
+		ret = HRTIMER_RESTART;
+		break;
+	default:
+		stime->running = false;
+		break; /* HRTIMER_NORESTART */
+	}
 
-	if (!atomic_read(&stime->running))
-		return HRTIMER_NORESTART;
-	return HRTIMER_RESTART;
+ unlock:
+	spin_unlock_irqrestore(&stime->lock, flags);
+	return  ret;
 }
 
 static int snd_hrtimer_open(struct snd_timer *t)
@@ -68,7 +86,8 @@ static int snd_hrtimer_open(struct snd_timer *t)
 	hrtimer_init(&stime->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
 	stime->timer = t;
 	stime->hrt.function = snd_hrtimer_callback;
-	atomic_set(&stime->running, 0);
+	spin_lock_init(&stime->lock);
+	stime->running = false;
 	t->private_data = stime;
 	return 0;
 }
@@ -88,25 +107,31 @@ static int snd_hrtimer_close(struct snd_timer *t)
 static int snd_hrtimer_start(struct snd_timer *t)
 {
 	struct snd_hrtimer *stime = t->private_data;
+	unsigned long flags;
 
-	atomic_set(&stime->running, 0);
-	hrtimer_try_to_cancel(&stime->hrt);
+	spin_lock_irqsave(&stime->lock, flags);
 	hrtimer_start(&stime->hrt, ns_to_ktime(t->sticks * resolution),
 		      HRTIMER_MODE_REL);
-	atomic_set(&stime->running, 1);
+	stime->running = true;
+	spin_unlock_irqrestore(&stime->lock, flags);
 	return 0;
 }
 
 static int snd_hrtimer_stop(struct snd_timer *t)
 {
 	struct snd_hrtimer *stime = t->private_data;
-	atomic_set(&stime->running, 0);
+	unsigned long flags;
+
+	spin_lock_irqsave(&stime->lock, flags);
 	hrtimer_try_to_cancel(&stime->hrt);
+	stime->running = false;
+	spin_unlock_irqrestore(&stime->lock, flags);
 	return 0;
 }
 
 static struct snd_timer_hardware hrtimer_hw = {
-	.flags =	SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET,
+	.flags =	SNDRV_TIMER_HW_AUTO | SNDRV_TIMER_HW_TASKLET |
+			SNDRV_TIMER_HW_RET_CTRL,
 	.open =		snd_hrtimer_open,
 	.close =	snd_hrtimer_close,
 	.start =	snd_hrtimer_start,
-- 
2.8.1


  reply	other threads:[~2016-04-20 10:31 UTC|newest]

Thread overview: 37+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-04-02  9:08 Dmitry Vyukov
2016-04-02 16:30 ` Takashi Iwai
2016-04-03  6:06   ` Dmitry Vyukov
2016-04-03  6:33     ` Takashi Iwai
2016-04-20  7:56       ` Dmitry Vyukov
2016-04-20  8:08         ` Takashi Iwai
2016-04-20 10:31           ` Takashi Iwai [this message]
2016-04-21  8:14             ` Dmitry Vyukov
2016-04-21  8:31               ` Takashi Iwai
  -- strict thread matches above, loose matches on Subject: below --
2016-01-13 15:00 Dmitry Vyukov
2016-01-13 16:53 ` Takashi Iwai
2016-01-13 18:34   ` Dmitry Vyukov
2016-01-13 19:05     ` Takashi Iwai
2016-01-13 19:30       ` Dmitry Vyukov
2016-01-13 19:41         ` Dmitry Vyukov
2016-01-13 20:30           ` Takashi Iwai
2016-01-13 20:48             ` Dmitry Vyukov
2016-01-13 20:54               ` Takashi Iwai
2016-01-14 16:09                 ` Takashi Iwai
2016-01-15  8:06                   ` Dmitry Vyukov
2016-01-15 11:00                     ` Takashi Iwai
2016-01-15 11:03                       ` Dmitry Vyukov
2016-01-15 13:51                         ` Takashi Iwai
2016-01-15 14:38                           ` Dmitry Vyukov
2016-01-15 15:21                             ` Takashi Iwai
2016-01-15 15:28                               ` Dmitry Vyukov
2016-01-15 15:39                                 ` Takashi Iwai
2016-01-15 19:13                             ` Dmitry Vyukov
2016-01-15 19:18                               ` Takashi Iwai
2016-01-15 19:47                                 ` Dmitry Vyukov
2016-01-15 21:22                                   ` Takashi Iwai
2016-01-15 21:44                                     ` Takashi Iwai
2016-01-18 10:53                                       ` Dmitry Vyukov
2016-01-18 13:06                                         ` Takashi Iwai
2016-01-18 13:30                                           ` Dmitry Vyukov
2016-01-18 13:36                                             ` Takashi Iwai
2016-01-13 20:45         ` Takashi Iwai

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=s5h37qg372z.wl-tiwai@suse.de \
    --to=tiwai@suse.de \
    --cc=alsa-devel@alsa-project.org \
    --cc=dvyukov@google.com \
    --cc=glider@google.com \
    --cc=kcc@google.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=perex@perex.cz \
    --cc=peterz@infradead.org \
    --cc=sasha.levin@oracle.com \
    --cc=syzkaller@googlegroups.com \
    --cc=tglx@linutronix.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®