Skip to content

Commit 9b7d869

Browse files
committed
ALSA: timer: Limit max instances per timer
Currently we allow unlimited number of timer instances, and it may bring the system hogging way too much CPU when too many timer instances are opened and processed concurrently. This may end up with a soft-lockup report as triggered by syzkaller, especially when hrtimer backend is deployed. Since such insane number of instances aren't demanded by the normal use case of ALSA sequencer and it merely opens a risk only for abuse, this patch introduces the upper limit for the number of instances per timer backend. As default, it's set to 1000, but for the fine-grained timer like hrtimer, it's set to 100. Reported-by: syzbot Tested-by: Jérôme Glisse <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent f5ce817 commit 9b7d869

File tree

3 files changed

+57
-13
lines changed

3 files changed

+57
-13
lines changed

include/sound/timer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,8 @@ struct snd_timer {
9090
struct list_head ack_list_head;
9191
struct list_head sack_list_head; /* slow ack list head */
9292
struct tasklet_struct task_queue;
93+
int max_instances; /* upper limit of timer instances */
94+
int num_instances; /* current number of timer instances */
9395
};
9496

9597
struct snd_timer_instance {

sound/core/hrtimer.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ static int __init snd_hrtimer_init(void)
159159
timer->hw = hrtimer_hw;
160160
timer->hw.resolution = resolution;
161161
timer->hw.ticks = NANO_SEC / resolution;
162+
timer->max_instances = 100; /* lower the limit */
162163

163164
err = snd_timer_global_register(timer);
164165
if (err < 0) {

sound/core/timer.c

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ static void snd_timer_request(struct snd_timer_id *tid)
180180
*
181181
* call this with register_mutex down.
182182
*/
183-
static void snd_timer_check_slave(struct snd_timer_instance *slave)
183+
static int snd_timer_check_slave(struct snd_timer_instance *slave)
184184
{
185185
struct snd_timer *timer;
186186
struct snd_timer_instance *master;
@@ -190,16 +190,21 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
190190
list_for_each_entry(master, &timer->open_list_head, open_list) {
191191
if (slave->slave_class == master->slave_class &&
192192
slave->slave_id == master->slave_id) {
193+
if (master->timer->num_instances >=
194+
master->timer->max_instances)
195+
return -EBUSY;
193196
list_move_tail(&slave->open_list,
194197
&master->slave_list_head);
198+
master->timer->num_instances++;
195199
spin_lock_irq(&slave_active_lock);
196200
slave->master = master;
197201
slave->timer = master->timer;
198202
spin_unlock_irq(&slave_active_lock);
199-
return;
203+
return 0;
200204
}
201205
}
202206
}
207+
return 0;
203208
}
204209

205210
/*
@@ -208,15 +213,19 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
208213
*
209214
* call this with register_mutex down.
210215
*/
211-
static void snd_timer_check_master(struct snd_timer_instance *master)
216+
static int snd_timer_check_master(struct snd_timer_instance *master)
212217
{
213218
struct snd_timer_instance *slave, *tmp;
214219

215220
/* check all pending slaves */
216221
list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
217222
if (slave->slave_class == master->slave_class &&
218223
slave->slave_id == master->slave_id) {
224+
if (master->timer->num_instances >=
225+
master->timer->max_instances)
226+
return -EBUSY;
219227
list_move_tail(&slave->open_list, &master->slave_list_head);
228+
master->timer->num_instances++;
220229
spin_lock_irq(&slave_active_lock);
221230
spin_lock(&master->timer->lock);
222231
slave->master = master;
@@ -228,8 +237,11 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
228237
spin_unlock_irq(&slave_active_lock);
229238
}
230239
}
240+
return 0;
231241
}
232242

243+
static int snd_timer_close_locked(struct snd_timer_instance *timeri);
244+
233245
/*
234246
* open a timer instance
235247
* when opening a master, the slave id must be here given.
@@ -240,6 +252,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
240252
{
241253
struct snd_timer *timer;
242254
struct snd_timer_instance *timeri = NULL;
255+
int err;
243256

244257
if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
245258
/* open a slave instance */
@@ -259,10 +272,14 @@ int snd_timer_open(struct snd_timer_instance **ti,
259272
timeri->slave_id = tid->device;
260273
timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
261274
list_add_tail(&timeri->open_list, &snd_timer_slave_list);
262-
snd_timer_check_slave(timeri);
275+
err = snd_timer_check_slave(timeri);
276+
if (err < 0) {
277+
snd_timer_close_locked(timeri);
278+
timeri = NULL;
279+
}
263280
mutex_unlock(&register_mutex);
264281
*ti = timeri;
265-
return 0;
282+
return err;
266283
}
267284

268285
/* open a master instance */
@@ -288,6 +305,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
288305
return -EBUSY;
289306
}
290307
}
308+
if (timer->num_instances >= timer->max_instances) {
309+
mutex_unlock(&register_mutex);
310+
return -EBUSY;
311+
}
291312
timeri = snd_timer_instance_new(owner, timer);
292313
if (!timeri) {
293314
mutex_unlock(&register_mutex);
@@ -314,32 +335,35 @@ int snd_timer_open(struct snd_timer_instance **ti,
314335
}
315336

316337
list_add_tail(&timeri->open_list, &timer->open_list_head);
317-
snd_timer_check_master(timeri);
338+
timer->num_instances++;
339+
err = snd_timer_check_master(timeri);
340+
if (err < 0) {
341+
snd_timer_close_locked(timeri);
342+
timeri = NULL;
343+
}
318344
mutex_unlock(&register_mutex);
319345
*ti = timeri;
320-
return 0;
346+
return err;
321347
}
322348
EXPORT_SYMBOL(snd_timer_open);
323349

324350
/*
325351
* close a timer instance
352+
* call this with register_mutex down.
326353
*/
327-
int snd_timer_close(struct snd_timer_instance *timeri)
354+
static int snd_timer_close_locked(struct snd_timer_instance *timeri)
328355
{
329356
struct snd_timer *timer = NULL;
330357
struct snd_timer_instance *slave, *tmp;
331358

332-
if (snd_BUG_ON(!timeri))
333-
return -ENXIO;
334-
335-
mutex_lock(&register_mutex);
336359
list_del(&timeri->open_list);
337360

338361
/* force to stop the timer */
339362
snd_timer_stop(timeri);
340363

341364
timer = timeri->timer;
342365
if (timer) {
366+
timer->num_instances--;
343367
/* wait, until the active callback is finished */
344368
spin_lock_irq(&timer->lock);
345369
while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -355,6 +379,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
355379
list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
356380
open_list) {
357381
list_move_tail(&slave->open_list, &snd_timer_slave_list);
382+
timer->num_instances--;
358383
slave->master = NULL;
359384
slave->timer = NULL;
360385
list_del_init(&slave->ack_list);
@@ -382,9 +407,24 @@ int snd_timer_close(struct snd_timer_instance *timeri)
382407
module_put(timer->module);
383408
}
384409

385-
mutex_unlock(&register_mutex);
386410
return 0;
387411
}
412+
413+
/*
414+
* close a timer instance
415+
*/
416+
int snd_timer_close(struct snd_timer_instance *timeri)
417+
{
418+
int err;
419+
420+
if (snd_BUG_ON(!timeri))
421+
return -ENXIO;
422+
423+
mutex_lock(&register_mutex);
424+
err = snd_timer_close_locked(timeri);
425+
mutex_unlock(&register_mutex);
426+
return err;
427+
}
388428
EXPORT_SYMBOL(snd_timer_close);
389429

390430
unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
@@ -856,6 +896,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
856896
spin_lock_init(&timer->lock);
857897
tasklet_init(&timer->task_queue, snd_timer_tasklet,
858898
(unsigned long)timer);
899+
timer->max_instances = 1000; /* default limit per timer */
859900
if (card != NULL) {
860901
timer->module = card->module;
861902
err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);

0 commit comments

Comments
 (0)