aboutsummaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2016-01-13 11:48:01 -0500
committerTakashi Iwai <tiwai@suse.de>2016-01-14 08:07:21 -0500
commitaf368027a49a751d6ff4ee9e3f9961f35bb4fede (patch)
treeca51f654c1b2f2d660a211b2a21dacc57f36cb62 /sound
parent91815d8aa7e2f45d30e51caa297061ad893628d9 (diff)
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 <dvyukov@google.com> Tested-by: Dmitry Vyukov <dvyukov@google.com> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r--sound/core/timer.c32
1 files changed, 19 insertions, 13 deletions
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 9241784dfe7d..3810ee8f1205 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -73,7 +73,7 @@ struct snd_timer_user {
73 struct timespec tstamp; /* trigger tstamp */ 73 struct timespec tstamp; /* trigger tstamp */
74 wait_queue_head_t qchange_sleep; 74 wait_queue_head_t qchange_sleep;
75 struct fasync_struct *fasync; 75 struct fasync_struct *fasync;
76 struct mutex tread_sem; 76 struct mutex ioctl_lock;
77}; 77};
78 78
79/* list of timers */ 79/* list of timers */
@@ -1253,7 +1253,7 @@ static int snd_timer_user_open(struct inode *inode, struct file *file)
1253 return -ENOMEM; 1253 return -ENOMEM;
1254 spin_lock_init(&tu->qlock); 1254 spin_lock_init(&tu->qlock);
1255 init_waitqueue_head(&tu->qchange_sleep); 1255 init_waitqueue_head(&tu->qchange_sleep);
1256 mutex_init(&tu->tread_sem); 1256 mutex_init(&tu->ioctl_lock);
1257 tu->ticks = 1; 1257 tu->ticks = 1;
1258 tu->queue_size = 128; 1258 tu->queue_size = 128;
1259 tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read), 1259 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)
1273 if (file->private_data) { 1273 if (file->private_data) {
1274 tu = file->private_data; 1274 tu = file->private_data;
1275 file->private_data = NULL; 1275 file->private_data = NULL;
1276 mutex_lock(&tu->ioctl_lock);
1276 if (tu->timeri) 1277 if (tu->timeri)
1277 snd_timer_close(tu->timeri); 1278 snd_timer_close(tu->timeri);
1279 mutex_unlock(&tu->ioctl_lock);
1278 kfree(tu->queue); 1280 kfree(tu->queue);
1279 kfree(tu->tqueue); 1281 kfree(tu->tqueue);
1280 kfree(tu); 1282 kfree(tu);
@@ -1512,7 +1514,6 @@ static int snd_timer_user_tselect(struct file *file,
1512 int err = 0; 1514 int err = 0;
1513 1515
1514 tu = file->private_data; 1516 tu = file->private_data;
1515 mutex_lock(&tu->tread_sem);
1516 if (tu->timeri) { 1517 if (tu->timeri) {
1517 snd_timer_close(tu->timeri); 1518 snd_timer_close(tu->timeri);
1518 tu->timeri = NULL; 1519 tu->timeri = NULL;
@@ -1556,7 +1557,6 @@ static int snd_timer_user_tselect(struct file *file,
1556 } 1557 }
1557 1558
1558 __err: 1559 __err:
1559 mutex_unlock(&tu->tread_sem);
1560 return err; 1560 return err;
1561} 1561}
1562 1562
@@ -1769,7 +1769,7 @@ enum {
1769 SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), 1769 SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
1770}; 1770};
1771 1771
1772static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, 1772static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
1773 unsigned long arg) 1773 unsigned long arg)
1774{ 1774{
1775 struct snd_timer_user *tu; 1775 struct snd_timer_user *tu;
@@ -1786,17 +1786,11 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
1786 { 1786 {
1787 int xarg; 1787 int xarg;
1788 1788
1789 mutex_lock(&tu->tread_sem); 1789 if (tu->timeri) /* too late */
1790 if (tu->timeri) { /* too late */
1791 mutex_unlock(&tu->tread_sem);
1792 return -EBUSY; 1790 return -EBUSY;
1793 } 1791 if (get_user(xarg, p))
1794 if (get_user(xarg, p)) {
1795 mutex_unlock(&tu->tread_sem);
1796 return -EFAULT; 1792 return -EFAULT;
1797 }
1798 tu->tread = xarg ? 1 : 0; 1793 tu->tread = xarg ? 1 : 0;
1799 mutex_unlock(&tu->tread_sem);
1800 return 0; 1794 return 0;
1801 } 1795 }
1802 case SNDRV_TIMER_IOCTL_GINFO: 1796 case SNDRV_TIMER_IOCTL_GINFO:
@@ -1829,6 +1823,18 @@ static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
1829 return -ENOTTY; 1823 return -ENOTTY;
1830} 1824}
1831 1825
1826static 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
1832static int snd_timer_user_fasync(int fd, struct file * file, int on) 1838static int snd_timer_user_fasync(int fd, struct file * file, int on)
1833{ 1839{
1834 struct snd_timer_user *tu; 1840 struct snd_timer_user *tu;