diff options
author | Takashi Iwai <tiwai@suse.de> | 2016-01-30 17:30:25 -0500 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2016-02-01 06:23:29 -0500 |
commit | 2cdc7b636d55cbcf42e1e6c8accd85e62d3e9ae8 (patch) | |
tree | 66f9915961442f22a4cd0e6f8330340b762d6d6b | |
parent | b248371628aad599a48540962f6b85a21a8a0c3f (diff) |
ALSA: seq: Fix yet another races among ALSA timer accesses
ALSA sequencer may open/close and control ALSA timer instance
dynamically either via sequencer events or direct ioctls. These are
done mostly asynchronously, and it may call still some timer action
like snd_timer_start() while another is calling snd_timer_close().
Since the instance gets removed by snd_timer_close(), it may lead to
a use-after-free.
This patch tries to address such a race by protecting each
snd_timer_*() call via the existing spinlock and also by avoiding the
access to timer during close call.
BugLink: http://lkml.kernel.org/r/CACT4Y+Z6RzW5MBr-HUdV-8zwg71WQfKTdPpYGvOeS7v4cyurNQ@mail.gmail.com
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r-- | sound/core/seq/seq_timer.c | 87 |
1 files changed, 67 insertions, 20 deletions
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c index 82b220c769c1..293104926098 100644 --- a/sound/core/seq/seq_timer.c +++ b/sound/core/seq/seq_timer.c | |||
@@ -90,6 +90,9 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr) | |||
90 | 90 | ||
91 | void snd_seq_timer_defaults(struct snd_seq_timer * tmr) | 91 | void snd_seq_timer_defaults(struct snd_seq_timer * tmr) |
92 | { | 92 | { |
93 | unsigned long flags; | ||
94 | |||
95 | spin_lock_irqsave(&tmr->lock, flags); | ||
93 | /* setup defaults */ | 96 | /* setup defaults */ |
94 | tmr->ppq = 96; /* 96 PPQ */ | 97 | tmr->ppq = 96; /* 96 PPQ */ |
95 | tmr->tempo = 500000; /* 120 BPM */ | 98 | tmr->tempo = 500000; /* 120 BPM */ |
@@ -105,21 +108,25 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr) | |||
105 | tmr->preferred_resolution = seq_default_timer_resolution; | 108 | tmr->preferred_resolution = seq_default_timer_resolution; |
106 | 109 | ||
107 | tmr->skew = tmr->skew_base = SKEW_BASE; | 110 | tmr->skew = tmr->skew_base = SKEW_BASE; |
111 | spin_unlock_irqrestore(&tmr->lock, flags); | ||
108 | } | 112 | } |
109 | 113 | ||
110 | void snd_seq_timer_reset(struct snd_seq_timer * tmr) | 114 | static void seq_timer_reset(struct snd_seq_timer *tmr) |
111 | { | 115 | { |
112 | unsigned long flags; | ||
113 | |||
114 | spin_lock_irqsave(&tmr->lock, flags); | ||
115 | |||
116 | /* reset time & songposition */ | 116 | /* reset time & songposition */ |
117 | tmr->cur_time.tv_sec = 0; | 117 | tmr->cur_time.tv_sec = 0; |
118 | tmr->cur_time.tv_nsec = 0; | 118 | tmr->cur_time.tv_nsec = 0; |
119 | 119 | ||
120 | tmr->tick.cur_tick = 0; | 120 | tmr->tick.cur_tick = 0; |
121 | tmr->tick.fraction = 0; | 121 | tmr->tick.fraction = 0; |
122 | } | ||
123 | |||
124 | void snd_seq_timer_reset(struct snd_seq_timer *tmr) | ||
125 | { | ||
126 | unsigned long flags; | ||
122 | 127 | ||
128 | spin_lock_irqsave(&tmr->lock, flags); | ||
129 | seq_timer_reset(tmr); | ||
123 | spin_unlock_irqrestore(&tmr->lock, flags); | 130 | spin_unlock_irqrestore(&tmr->lock, flags); |
124 | } | 131 | } |
125 | 132 | ||
@@ -138,8 +145,11 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, | |||
138 | tmr = q->timer; | 145 | tmr = q->timer; |
139 | if (tmr == NULL) | 146 | if (tmr == NULL) |
140 | return; | 147 | return; |
141 | if (!tmr->running) | 148 | spin_lock_irqsave(&tmr->lock, flags); |
149 | if (!tmr->running) { | ||
150 | spin_unlock_irqrestore(&tmr->lock, flags); | ||
142 | return; | 151 | return; |
152 | } | ||
143 | 153 | ||
144 | resolution *= ticks; | 154 | resolution *= ticks; |
145 | if (tmr->skew != tmr->skew_base) { | 155 | if (tmr->skew != tmr->skew_base) { |
@@ -148,8 +158,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri, | |||
148 | (((resolution & 0xffff) * tmr->skew) >> 16); | 158 | (((resolution & 0xffff) * tmr->skew) >> 16); |
149 | } | 159 | } |
150 | 160 | ||
151 | spin_lock_irqsave(&tmr->lock, flags); | ||
152 | |||
153 | /* update timer */ | 161 | /* update timer */ |
154 | snd_seq_inc_time_nsec(&tmr->cur_time, resolution); | 162 | snd_seq_inc_time_nsec(&tmr->cur_time, resolution); |
155 | 163 | ||
@@ -296,26 +304,30 @@ int snd_seq_timer_open(struct snd_seq_queue *q) | |||
296 | t->callback = snd_seq_timer_interrupt; | 304 | t->callback = snd_seq_timer_interrupt; |
297 | t->callback_data = q; | 305 | t->callback_data = q; |
298 | t->flags |= SNDRV_TIMER_IFLG_AUTO; | 306 | t->flags |= SNDRV_TIMER_IFLG_AUTO; |
307 | spin_lock_irq(&tmr->lock); | ||
299 | tmr->timeri = t; | 308 | tmr->timeri = t; |
309 | spin_unlock_irq(&tmr->lock); | ||
300 | return 0; | 310 | return 0; |
301 | } | 311 | } |
302 | 312 | ||
303 | int snd_seq_timer_close(struct snd_seq_queue *q) | 313 | int snd_seq_timer_close(struct snd_seq_queue *q) |
304 | { | 314 | { |
305 | struct snd_seq_timer *tmr; | 315 | struct snd_seq_timer *tmr; |
316 | struct snd_timer_instance *t; | ||
306 | 317 | ||
307 | tmr = q->timer; | 318 | tmr = q->timer; |
308 | if (snd_BUG_ON(!tmr)) | 319 | if (snd_BUG_ON(!tmr)) |
309 | return -EINVAL; | 320 | return -EINVAL; |
310 | if (tmr->timeri) { | 321 | spin_lock_irq(&tmr->lock); |
311 | snd_timer_stop(tmr->timeri); | 322 | t = tmr->timeri; |
312 | snd_timer_close(tmr->timeri); | 323 | tmr->timeri = NULL; |
313 | tmr->timeri = NULL; | 324 | spin_unlock_irq(&tmr->lock); |
314 | } | 325 | if (t) |
326 | snd_timer_close(t); | ||
315 | return 0; | 327 | return 0; |
316 | } | 328 | } |
317 | 329 | ||
318 | int snd_seq_timer_stop(struct snd_seq_timer * tmr) | 330 | static int seq_timer_stop(struct snd_seq_timer *tmr) |
319 | { | 331 | { |
320 | if (! tmr->timeri) | 332 | if (! tmr->timeri) |
321 | return -EINVAL; | 333 | return -EINVAL; |
@@ -326,6 +338,17 @@ int snd_seq_timer_stop(struct snd_seq_timer * tmr) | |||
326 | return 0; | 338 | return 0; |
327 | } | 339 | } |
328 | 340 | ||
341 | int snd_seq_timer_stop(struct snd_seq_timer *tmr) | ||
342 | { | ||
343 | unsigned long flags; | ||
344 | int err; | ||
345 | |||
346 | spin_lock_irqsave(&tmr->lock, flags); | ||
347 | err = seq_timer_stop(tmr); | ||
348 | spin_unlock_irqrestore(&tmr->lock, flags); | ||
349 | return err; | ||
350 | } | ||
351 | |||
329 | static int initialize_timer(struct snd_seq_timer *tmr) | 352 | static int initialize_timer(struct snd_seq_timer *tmr) |
330 | { | 353 | { |
331 | struct snd_timer *t; | 354 | struct snd_timer *t; |
@@ -358,13 +381,13 @@ static int initialize_timer(struct snd_seq_timer *tmr) | |||
358 | return 0; | 381 | return 0; |
359 | } | 382 | } |
360 | 383 | ||
361 | int snd_seq_timer_start(struct snd_seq_timer * tmr) | 384 | static int seq_timer_start(struct snd_seq_timer *tmr) |
362 | { | 385 | { |
363 | if (! tmr->timeri) | 386 | if (! tmr->timeri) |
364 | return -EINVAL; | 387 | return -EINVAL; |
365 | if (tmr->running) | 388 | if (tmr->running) |
366 | snd_seq_timer_stop(tmr); | 389 | seq_timer_stop(tmr); |
367 | snd_seq_timer_reset(tmr); | 390 | seq_timer_reset(tmr); |
368 | if (initialize_timer(tmr) < 0) | 391 | if (initialize_timer(tmr) < 0) |
369 | return -EINVAL; | 392 | return -EINVAL; |
370 | snd_timer_start(tmr->timeri, tmr->ticks); | 393 | snd_timer_start(tmr->timeri, tmr->ticks); |
@@ -373,14 +396,25 @@ int snd_seq_timer_start(struct snd_seq_timer * tmr) | |||
373 | return 0; | 396 | return 0; |
374 | } | 397 | } |
375 | 398 | ||
376 | int snd_seq_timer_continue(struct snd_seq_timer * tmr) | 399 | int snd_seq_timer_start(struct snd_seq_timer *tmr) |
400 | { | ||
401 | unsigned long flags; | ||
402 | int err; | ||
403 | |||
404 | spin_lock_irqsave(&tmr->lock, flags); | ||
405 | err = seq_timer_start(tmr); | ||
406 | spin_unlock_irqrestore(&tmr->lock, flags); | ||
407 | return err; | ||
408 | } | ||
409 | |||
410 | static int seq_timer_continue(struct snd_seq_timer *tmr) | ||
377 | { | 411 | { |
378 | if (! tmr->timeri) | 412 | if (! tmr->timeri) |
379 | return -EINVAL; | 413 | return -EINVAL; |
380 | if (tmr->running) | 414 | if (tmr->running) |
381 | return -EBUSY; | 415 | return -EBUSY; |
382 | if (! tmr->initialized) { | 416 | if (! tmr->initialized) { |
383 | snd_seq_timer_reset(tmr); | 417 | seq_timer_reset(tmr); |
384 | if (initialize_timer(tmr) < 0) | 418 | if (initialize_timer(tmr) < 0) |
385 | return -EINVAL; | 419 | return -EINVAL; |
386 | } | 420 | } |
@@ -390,11 +424,24 @@ int snd_seq_timer_continue(struct snd_seq_timer * tmr) | |||
390 | return 0; | 424 | return 0; |
391 | } | 425 | } |
392 | 426 | ||
427 | int snd_seq_timer_continue(struct snd_seq_timer *tmr) | ||
428 | { | ||
429 | unsigned long flags; | ||
430 | int err; | ||
431 | |||
432 | spin_lock_irqsave(&tmr->lock, flags); | ||
433 | err = seq_timer_continue(tmr); | ||
434 | spin_unlock_irqrestore(&tmr->lock, flags); | ||
435 | return err; | ||
436 | } | ||
437 | |||
393 | /* return current 'real' time. use timeofday() to get better granularity. */ | 438 | /* return current 'real' time. use timeofday() to get better granularity. */ |
394 | snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) | 439 | snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) |
395 | { | 440 | { |
396 | snd_seq_real_time_t cur_time; | 441 | snd_seq_real_time_t cur_time; |
442 | unsigned long flags; | ||
397 | 443 | ||
444 | spin_lock_irqsave(&tmr->lock, flags); | ||
398 | cur_time = tmr->cur_time; | 445 | cur_time = tmr->cur_time; |
399 | if (tmr->running) { | 446 | if (tmr->running) { |
400 | struct timeval tm; | 447 | struct timeval tm; |
@@ -410,7 +457,7 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr) | |||
410 | } | 457 | } |
411 | snd_seq_sanity_real_time(&cur_time); | 458 | snd_seq_sanity_real_time(&cur_time); |
412 | } | 459 | } |
413 | 460 | spin_unlock_irqrestore(&tmr->lock, flags); | |
414 | return cur_time; | 461 | return cur_time; |
415 | } | 462 | } |
416 | 463 | ||