Skip to content

Commit af36802

Browse files
committed
ALSA: timer: Fix race among timer ioctls
ALSA timer ioctls have an open race and this may lead to a use-after-free of timer instance object. A simplistic fix is to make each ioctl exclusive. We have already tread_sem for controlling the tread, and extend this as a global mutex to be applied to each ioctl. The downside is, of course, the worse concurrency. But these ioctls aren't to be parallel accessible, in anyway, so it should be fine to serialize there. Reported-by: Dmitry Vyukov <[email protected]> Tested-by: Dmitry Vyukov <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 91815d8 commit af36802

File tree

1 file changed

+19
-13
lines changed

1 file changed

+19
-13
lines changed

sound/core/timer.c

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ struct snd_timer_user {
7373
struct timespec tstamp; /* trigger tstamp */
7474
wait_queue_head_t qchange_sleep;
7575
struct fasync_struct *fasync;
76-
struct mutex tread_sem;
76+
struct mutex ioctl_lock;
7777
};
7878

7979
/* list of timers */
@@ -1253,7 +1253,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
12531253
return -ENOMEM;
12541254
spin_lock_init(&tu->qlock);
12551255
init_waitqueue_head(&tu->qchange_sleep);
1256-
mutex_init(&tu->tread_sem);
1256+
mutex_init(&tu->ioctl_lock);
12571257
tu->ticks = 1;
12581258
tu->queue_size = 128;
12591259
tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
@@ -1273,8 +1273,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file)
12731273
if (file->private_data) {
12741274
tu = file->private_data;
12751275
file->private_data = NULL;
1276+
mutex_lock(&tu->ioctl_lock);
12761277
if (tu->timeri)
12771278
snd_timer_close(tu->timeri);
1279+
mutex_unlock(&tu->ioctl_lock);
12781280
kfree(tu->queue);
12791281
kfree(tu->tqueue);
12801282
kfree(tu);
@@ -1512,7 +1514,6 @@ static int snd_timer_user_tselect(struct file *file,
15121514
int err = 0;
15131515

15141516
tu = file->private_data;
1515-
mutex_lock(&tu->tread_sem);
15161517
if (tu->timeri) {
15171518
snd_timer_close(tu->timeri);
15181519
tu->timeri = NULL;
@@ -1556,7 +1557,6 @@ static int snd_timer_user_tselect(struct file *file,
15561557
}
15571558

15581559
__err:
1559-
mutex_unlock(&tu->tread_sem);
15601560
return err;
15611561
}
15621562

@@ -1769,7 +1769,7 @@ enum {
17691769
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
17701770
};
17711771

1772-
static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
1772+
static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
17731773
unsigned long arg)
17741774
{
17751775
struct snd_timer_user *tu;
@@ -1786,17 +1786,11 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
17861786
{
17871787
int xarg;
17881788

1789-
mutex_lock(&tu->tread_sem);
1790-
if (tu->timeri) { /* too late */
1791-
mutex_unlock(&tu->tread_sem);
1789+
if (tu->timeri) /* too late */
17921790
return -EBUSY;
1793-
}
1794-
if (get_user(xarg, p)) {
1795-
mutex_unlock(&tu->tread_sem);
1791+
if (get_user(xarg, p))
17961792
return -EFAULT;
1797-
}
17981793
tu->tread = xarg ? 1 : 0;
1799-
mutex_unlock(&tu->tread_sem);
18001794
return 0;
18011795
}
18021796
case SNDRV_TIMER_IOCTL_GINFO:
@@ -1829,6 +1823,18 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
18291823
return -ENOTTY;
18301824
}
18311825

1826+
static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
1827+
unsigned long arg)
1828+
{
1829+
struct snd_timer_user *tu = file->private_data;
1830+
long ret;
1831+
1832+
mutex_lock(&tu->ioctl_lock);
1833+
ret = __snd_timer_user_ioctl(file, cmd, arg);
1834+
mutex_unlock(&tu->ioctl_lock);
1835+
return ret;
1836+
}
1837+
18321838
static int snd_timer_user_fasync(int fd, struct file * file, int on)
18331839
{
18341840
struct snd_timer_user *tu;

0 commit comments

Comments
 (0)