Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ALSA] Fix races in PCM OSS emulation

Fixed the race among multiple threads accessing the OSS PCM
instance concurrently by simply introducing a mutex for protecting
a setup of the PCM.

Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Jaroslav Kysela <perex@suse.cz>

authored by

Takashi Iwai and committed by
Jaroslav Kysela
e3a5d59a ba8bdf85

+41 -12
+1
include/sound/pcm_oss.h
··· 56 56 size_t mmap_bytes; 57 57 char *buffer; /* vmallocated period */ 58 58 size_t buffer_used; /* used length from period buffer */ 59 + struct mutex params_lock; 59 60 #ifdef CONFIG_SND_PCM_OSS_PLUGINS 60 61 struct snd_pcm_plugin *plugin_first; 61 62 struct snd_pcm_plugin *plugin_last;
+40 -12
sound/core/oss/pcm_oss.c
··· 810 810 struct snd_mask sformat_mask; 811 811 struct snd_mask mask; 812 812 813 + if (mutex_lock_interruptible(&runtime->oss.params_lock)) 814 + return -EINTR; 813 815 sw_params = kmalloc(sizeof(*sw_params), GFP_KERNEL); 814 816 params = kmalloc(sizeof(*params), GFP_KERNEL); 815 817 sparams = kmalloc(sizeof(*sparams), GFP_KERNEL); ··· 1022 1020 kfree(sw_params); 1023 1021 kfree(params); 1024 1022 kfree(sparams); 1023 + mutex_unlock(&runtime->oss.params_lock); 1025 1024 return err; 1026 1025 } 1027 1026 ··· 1310 1307 1311 1308 if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) 1312 1309 return tmp; 1310 + mutex_lock(&runtime->oss.params_lock); 1313 1311 while (bytes > 0) { 1314 1312 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { 1315 1313 tmp = bytes; 1316 1314 if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes) 1317 1315 tmp = runtime->oss.period_bytes - runtime->oss.buffer_used; 1318 1316 if (tmp > 0) { 1319 - if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) 1320 - return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; 1317 + if (copy_from_user(runtime->oss.buffer + runtime->oss.buffer_used, buf, tmp)) { 1318 + tmp = -EFAULT; 1319 + goto err; 1320 + } 1321 1321 } 1322 1322 runtime->oss.buffer_used += tmp; 1323 1323 buf += tmp; ··· 1331 1325 tmp = snd_pcm_oss_write2(substream, runtime->oss.buffer + runtime->oss.period_ptr, 1332 1326 runtime->oss.buffer_used - runtime->oss.period_ptr, 1); 1333 1327 if (tmp <= 0) 1334 - return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1328 + goto err; 1335 1329 runtime->oss.bytes += tmp; 1336 1330 runtime->oss.period_ptr += tmp; 1337 1331 runtime->oss.period_ptr %= runtime->oss.period_bytes; 1338 1332 if (runtime->oss.period_ptr == 0 || 1339 1333 runtime->oss.period_ptr == runtime->oss.buffer_used) 1340 1334 runtime->oss.buffer_used = 0; 1341 - else if ((substream->f_flags & O_NONBLOCK) != 0) 1342 - return xfer > 0 ? xfer : -EAGAIN; 1335 + else if ((substream->f_flags & O_NONBLOCK) != 0) { 1336 + tmp = -EAGAIN; 1337 + goto err; 1338 + } 1343 1339 } 1344 1340 } else { 1345 1341 tmp = snd_pcm_oss_write2(substream, 1346 1342 (const char __force *)buf, 1347 1343 runtime->oss.period_bytes, 0); 1348 1344 if (tmp <= 0) 1349 - return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1345 + goto err; 1350 1346 runtime->oss.bytes += tmp; 1351 1347 buf += tmp; 1352 1348 bytes -= tmp; ··· 1358 1350 break; 1359 1351 } 1360 1352 } 1353 + mutex_unlock(&runtime->oss.params_lock); 1361 1354 return xfer; 1355 + 1356 + err: 1357 + mutex_unlock(&runtime->oss.params_lock); 1358 + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1362 1359 } 1363 1360 1364 1361 static ssize_t snd_pcm_oss_read2(struct snd_pcm_substream *substream, char *buf, size_t bytes, int in_kernel) ··· 1410 1397 1411 1398 if ((tmp = snd_pcm_oss_make_ready(substream)) < 0) 1412 1399 return tmp; 1400 + mutex_lock(&runtime->oss.params_lock); 1413 1401 while (bytes > 0) { 1414 1402 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) { 1415 1403 if (runtime->oss.buffer_used == 0) { 1416 1404 tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1); 1417 1405 if (tmp <= 0) 1418 - return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1406 + goto err; 1419 1407 runtime->oss.bytes += tmp; 1420 1408 runtime->oss.period_ptr = tmp; 1421 1409 runtime->oss.buffer_used = tmp; ··· 1424 1410 tmp = bytes; 1425 1411 if ((size_t) tmp > runtime->oss.buffer_used) 1426 1412 tmp = runtime->oss.buffer_used; 1427 - if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) 1428 - return xfer > 0 ? (snd_pcm_sframes_t)xfer : -EFAULT; 1413 + if (copy_to_user(buf, runtime->oss.buffer + (runtime->oss.period_ptr - runtime->oss.buffer_used), tmp)) { 1414 + tmp = -EFAULT; 1415 + goto err; 1416 + } 1429 1417 buf += tmp; 1430 1418 bytes -= tmp; 1431 1419 xfer += tmp; ··· 1436 1420 tmp = snd_pcm_oss_read2(substream, (char __force *)buf, 1437 1421 runtime->oss.period_bytes, 0); 1438 1422 if (tmp <= 0) 1439 - return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1423 + goto err; 1440 1424 runtime->oss.bytes += tmp; 1441 1425 buf += tmp; 1442 1426 bytes -= tmp; 1443 1427 xfer += tmp; 1444 1428 } 1445 1429 } 1430 + mutex_unlock(&runtime->oss.params_lock); 1446 1431 return xfer; 1432 + 1433 + err: 1434 + mutex_unlock(&runtime->oss.params_lock); 1435 + return xfer > 0 ? (snd_pcm_sframes_t)xfer : tmp; 1447 1436 } 1448 1437 1449 1438 static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file) ··· 1549 1528 return err; 1550 1529 format = snd_pcm_oss_format_from(runtime->oss.format); 1551 1530 width = snd_pcm_format_physical_width(format); 1531 + mutex_lock(&runtime->oss.params_lock); 1552 1532 if (runtime->oss.buffer_used > 0) { 1553 1533 #ifdef OSS_DEBUG 1554 1534 printk("sync: buffer_used\n"); ··· 1559 1537 runtime->oss.buffer + runtime->oss.buffer_used, 1560 1538 size); 1561 1539 err = snd_pcm_oss_sync1(substream, runtime->oss.period_bytes); 1562 - if (err < 0) 1540 + if (err < 0) { 1541 + mutex_unlock(&runtime->oss.params_lock); 1563 1542 return err; 1543 + } 1564 1544 } else if (runtime->oss.period_ptr > 0) { 1565 1545 #ifdef OSS_DEBUG 1566 1546 printk("sync: period_ptr\n"); ··· 1572 1548 runtime->oss.buffer, 1573 1549 size * 8 / width); 1574 1550 err = snd_pcm_oss_sync1(substream, size); 1575 - if (err < 0) 1551 + if (err < 0) { 1552 + mutex_unlock(&runtime->oss.params_lock); 1576 1553 return err; 1554 + } 1577 1555 } 1578 1556 /* 1579 1557 * The ALSA's period might be a bit large than OSS one. ··· 1605 1579 snd_pcm_lib_writev(substream, buffers, size); 1606 1580 } 1607 1581 } 1582 + mutex_unlock(&runtime->oss.params_lock); 1608 1583 /* 1609 1584 * finish sync: drain the buffer 1610 1585 */ ··· 2199 2172 runtime->oss.params = 1; 2200 2173 runtime->oss.trigger = 1; 2201 2174 runtime->oss.rate = 8000; 2175 + mutex_init(&runtime->oss.params_lock); 2202 2176 switch (SNDRV_MINOR_OSS_DEVICE(minor)) { 2203 2177 case SNDRV_MINOR_OSS_PCM_8: 2204 2178 runtime->oss.format = AFMT_U8;