aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2018-03-23 03:03:26 -0400
committerTakashi Iwai <tiwai@suse.de>2018-03-23 17:18:05 -0400
commit40cab6e88cb0b6c56d3f30b7491a20e803f948f6 (patch)
tree229da33d93b78fe63bab43877449301d32855af2
parent02a5d6925cd34c3b774bdb8eefb057c40a30e870 (diff)
ALSA: pcm: Return -EBUSY for OSS ioctls changing busy streams
OSS PCM stream management isn't modal but it allows ioctls issued at any time for changing the parameters. In the previous hardening patch ("ALSA: pcm: Avoid potential races between OSS ioctls and read/write"), we covered these races and prevent the corruption by protecting the concurrent accesses via params_lock mutex. However, this means that some ioctls that try to change the stream parameter (e.g. channels or format) would be blocked until the read/write finishes, and it may take really long. Basically changing the parameter while reading/writing is an invalid operation, hence it's even more user-friendly from the API POV if it returns -EBUSY in such a situation. This patch adds such checks in the relevant ioctls with the addition of read/write access refcount. Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
-rw-r--r--include/sound/pcm_oss.h1
-rw-r--r--sound/core/oss/pcm_oss.c36
2 files changed, 28 insertions, 9 deletions
diff --git a/include/sound/pcm_oss.h b/include/sound/pcm_oss.h
index 760c969d885d..12bbf8c81112 100644
--- a/include/sound/pcm_oss.h
+++ b/include/sound/pcm_oss.h
@@ -57,6 +57,7 @@ struct snd_pcm_oss_runtime {
57 char *buffer; /* vmallocated period */ 57 char *buffer; /* vmallocated period */
58 size_t buffer_used; /* used length from period buffer */ 58 size_t buffer_used; /* used length from period buffer */
59 struct mutex params_lock; 59 struct mutex params_lock;
60 atomic_t rw_ref; /* concurrent read/write accesses */
60#ifdef CONFIG_SND_PCM_OSS_PLUGINS 61#ifdef CONFIG_SND_PCM_OSS_PLUGINS
61 struct snd_pcm_plugin *plugin_first; 62 struct snd_pcm_plugin *plugin_first;
62 struct snd_pcm_plugin *plugin_last; 63 struct snd_pcm_plugin *plugin_last;
diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c
index f8bfee8022e0..a9082f219561 100644
--- a/sound/core/oss/pcm_oss.c
+++ b/sound/core/oss/pcm_oss.c
@@ -1370,6 +1370,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
1370 if (atomic_read(&substream->mmap_count)) 1370 if (atomic_read(&substream->mmap_count))
1371 return -ENXIO; 1371 return -ENXIO;
1372 1372
1373 atomic_inc(&runtime->oss.rw_ref);
1373 while (bytes > 0) { 1374 while (bytes > 0) {
1374 if (mutex_lock_interruptible(&runtime->oss.params_lock)) { 1375 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
1375 tmp = -ERESTARTSYS; 1376 tmp = -ERESTARTSYS;
@@ -1433,6 +1434,7 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
1433 } 1434 }
1434 tmp = 0; 1435 tmp = 0;
1435 } 1436 }
1437 atomic_dec(&runtime->oss.rw_ref);
1436 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1438 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
1437} 1439}
1438 1440
@@ -1478,6 +1480,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
1478 if (atomic_read(&substream->mmap_count)) 1480 if (atomic_read(&substream->mmap_count))
1479 return -ENXIO; 1481 return -ENXIO;
1480 1482
1483 atomic_inc(&runtime->oss.rw_ref);
1481 while (bytes > 0) { 1484 while (bytes > 0) {
1482 if (mutex_lock_interruptible(&runtime->oss.params_lock)) { 1485 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
1483 tmp = -ERESTARTSYS; 1486 tmp = -ERESTARTSYS;
@@ -1526,6 +1529,7 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
1526 } 1529 }
1527 tmp = 0; 1530 tmp = 0;
1528 } 1531 }
1532 atomic_dec(&runtime->oss.rw_ref);
1529 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1533 return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp;
1530} 1534}
1531 1535
@@ -1632,8 +1636,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
1632 goto __direct; 1636 goto __direct;
1633 if ((err = snd_pcm_oss_make_ready(substream)) < 0) 1637 if ((err = snd_pcm_oss_make_ready(substream)) < 0)
1634 return err; 1638 return err;
1635 if (mutex_lock_interruptible(&runtime->oss.params_lock)) 1639 atomic_inc(&runtime->oss.rw_ref);
1640 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
1641 atomic_dec(&runtime->oss.rw_ref);
1636 return -ERESTARTSYS; 1642 return -ERESTARTSYS;
1643 }
1637 format = snd_pcm_oss_format_from(runtime->oss.format); 1644 format = snd_pcm_oss_format_from(runtime->oss.format);
1638 width = snd_pcm_format_physical_width(format); 1645 width = snd_pcm_format_physical_width(format);
1639 if (runtime->oss.buffer_used > 0) { 1646 if (runtime->oss.buffer_used > 0) {
@@ -1645,10 +1652,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
1645 runtime->oss.buffer + runtime->oss.buffer_used, 1652 runtime->oss.buffer + runtime->oss.buffer_used,
1646 size); 1653 size);
1647 err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes); 1654 err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes);
1648 if (err < 0) { 1655 if (err < 0)
1649 mutex_unlock(&runtime->oss.params_lock); 1656 goto unlock;
1650 return err;
1651 }
1652 } else if (runtime->oss.period_ptr > 0) { 1657 } else if (runtime->oss.period_ptr > 0) {
1653#ifdef OSS_DEBUG 1658#ifdef OSS_DEBUG
1654 pcm_dbg(substream->pcm, "sync: period_ptr\n"); 1659 pcm_dbg(substream->pcm, "sync: period_ptr\n");
@@ -1658,10 +1663,8 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
1658 runtime->oss.buffer, 1663 runtime->oss.buffer,
1659 size * 8 / width); 1664 size * 8 / width);
1660 err = snd_pcm_oss_sync1(substream, size); 1665 err = snd_pcm_oss_sync1(substream, size);
1661 if (err < 0) { 1666 if (err < 0)
1662 mutex_unlock(&runtime->oss.params_lock); 1667 goto unlock;
1663 return err;
1664 }
1665 } 1668 }
1666 /* 1669 /*
1667 * The ALSA's period might be a bit large than OSS one. 1670 * The ALSA's period might be a bit large than OSS one.
@@ -1675,7 +1678,11 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
1675 else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED) 1678 else if (runtime->access == SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)
1676 snd_pcm_lib_writev(substream, NULL, size); 1679 snd_pcm_lib_writev(substream, NULL, size);
1677 } 1680 }
1681unlock:
1678 mutex_unlock(&runtime->oss.params_lock); 1682 mutex_unlock(&runtime->oss.params_lock);
1683 atomic_dec(&runtime->oss.rw_ref);
1684 if (err < 0)
1685 return err;
1679 /* 1686 /*
1680 * finish sync: drain the buffer 1687 * finish sync: drain the buffer
1681 */ 1688 */
@@ -1723,6 +1730,8 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
1723 rate = 192000; 1730 rate = 192000;
1724 if (mutex_lock_interruptible(&runtime->oss.params_lock)) 1731 if (mutex_lock_interruptible(&runtime->oss.params_lock))
1725 return -ERESTARTSYS; 1732 return -ERESTARTSYS;
1733 if (atomic_read(&runtime->oss.rw_ref))
1734 return -EBUSY;
1726 if (runtime->oss.rate != rate) { 1735 if (runtime->oss.rate != rate) {
1727 runtime->oss.params = 1; 1736 runtime->oss.params = 1;
1728 runtime->oss.rate = rate; 1737 runtime->oss.rate = rate;
@@ -1757,6 +1766,8 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig
1757 runtime = substream->runtime; 1766 runtime = substream->runtime;
1758 if (mutex_lock_interruptible(&runtime->oss.params_lock)) 1767 if (mutex_lock_interruptible(&runtime->oss.params_lock))
1759 return -ERESTARTSYS; 1768 return -ERESTARTSYS;
1769 if (atomic_read(&runtime->oss.rw_ref))
1770 return -EBUSY;
1760 if (runtime->oss.channels != channels) { 1771 if (runtime->oss.channels != channels) {
1761 runtime->oss.params = 1; 1772 runtime->oss.params = 1;
1762 runtime->oss.channels = channels; 1773 runtime->oss.channels = channels;
@@ -1847,6 +1858,8 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for
1847 if (substream == NULL) 1858 if (substream == NULL)
1848 continue; 1859 continue;
1849 runtime = substream->runtime; 1860 runtime = substream->runtime;
1861 if (atomic_read(&runtime->oss.rw_ref))
1862 return -EBUSY;
1850 if (mutex_lock_interruptible(&runtime->oss.params_lock)) 1863 if (mutex_lock_interruptible(&runtime->oss.params_lock))
1851 return -ERESTARTSYS; 1864 return -ERESTARTSYS;
1852 if (runtime->oss.format != format) { 1865 if (runtime->oss.format != format) {
@@ -1901,6 +1914,8 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
1901 if (substream == NULL) 1914 if (substream == NULL)
1902 continue; 1915 continue;
1903 runtime = substream->runtime; 1916 runtime = substream->runtime;
1917 if (atomic_read(&runtime->oss.rw_ref))
1918 return -EBUSY;
1904 if (mutex_lock_interruptible(&runtime->oss.params_lock)) 1919 if (mutex_lock_interruptible(&runtime->oss.params_lock))
1905 return -ERESTARTSYS; 1920 return -ERESTARTSYS;
1906 err = snd_pcm_oss_set_subdivide1(substream, subdivide); 1921 err = snd_pcm_oss_set_subdivide1(substream, subdivide);
@@ -1939,6 +1954,8 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
1939 if (substream == NULL) 1954 if (substream == NULL)
1940 continue; 1955 continue;
1941 runtime = substream->runtime; 1956 runtime = substream->runtime;
1957 if (atomic_read(&runtime->oss.rw_ref))
1958 return -EBUSY;
1942 if (mutex_lock_interruptible(&runtime->oss.params_lock)) 1959 if (mutex_lock_interruptible(&runtime->oss.params_lock))
1943 return -ERESTARTSYS; 1960 return -ERESTARTSYS;
1944 err = snd_pcm_oss_set_fragment1(substream, val); 1961 err = snd_pcm_oss_set_fragment1(substream, val);
@@ -2333,6 +2350,7 @@ static void snd_pcm_oss_init_substream(struct snd_pcm_substream *substream,
2333 runtime->oss.maxfrags = 0; 2350 runtime->oss.maxfrags = 0;
2334 runtime->oss.subdivision = 0; 2351 runtime->oss.subdivision = 0;
2335 substream->pcm_release = snd_pcm_oss_release_substream; 2352 substream->pcm_release = snd_pcm_oss_release_substream;
2353 atomic_set(&runtime->oss.rw_ref, 0);
2336} 2354}
2337 2355
2338static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file) 2356static int snd_pcm_oss_release_file(struct snd_pcm_oss_file *pcm_oss_file)