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 | ||
