Skip to content

Commit 02a5d69

Browse files
committed
ALSA: pcm: Avoid potential races between OSS ioctls and read/write
Although we apply the params_lock mutex to the whole read and write operations as well as snd_pcm_oss_change_params(), we may still face some races. First off, the params_lock is taken inside the read and write loop. This is intentional for avoiding the too long locking, but it allows the in-between parameter change, which might lead to invalid pointers. We check the readiness of the stream and set up via snd_pcm_oss_make_ready() at the beginning of read and write, but it's called only once, by assuming that it remains ready in the rest. Second, many ioctls that may change the actual parameters (i.e. setting runtime->oss.params=1) aren't protected, hence they can be processed in a half-baked state. This patch is an attempt to plug these holes. The stream readiness check is moved inside the read/write inner loop, so that the stream is always set up in a proper state before further processing. Also, each ioctl that may change the parameter is wrapped with the params_lock for avoiding the races. The issues were triggered by syzkaller in a few different scenarios, particularly the one below appearing as GPF in loopback_pos_update. Reported-by: [email protected] Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent f3b906d commit 02a5d69

File tree

1 file changed

+106
-28
lines changed

1 file changed

+106
-28
lines changed

sound/core/oss/pcm_oss.c

Lines changed: 106 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -823,8 +823,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
823823
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
824824
}
825825

826-
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
827-
bool trylock)
826+
/* call with params_lock held */
827+
static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
828828
{
829829
struct snd_pcm_runtime *runtime = substream->runtime;
830830
struct snd_pcm_hw_params *params, *sparams;
@@ -838,11 +838,8 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
838838
const struct snd_mask *sformat_mask;
839839
struct snd_mask mask;
840840

841-
if (trylock) {
842-
if (!(mutex_trylock(&runtime->oss.params_lock)))
843-
return -EAGAIN;
844-
} else if (mutex_lock_interruptible(&runtime->oss.params_lock))
845-
return -ERESTARTSYS;
841+
if (!runtime->oss.params)
842+
return 0;
846843
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
847844
params = kmalloc(sizeof(*params), GFP_KERNEL);
848845
sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
@@ -1068,6 +1065,23 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
10681065
kfree(sw_params);
10691066
kfree(params);
10701067
kfree(sparams);
1068+
return err;
1069+
}
1070+
1071+
/* this one takes the lock by itself */
1072+
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
1073+
bool trylock)
1074+
{
1075+
struct snd_pcm_runtime *runtime = substream->runtime;
1076+
int err;
1077+
1078+
if (trylock) {
1079+
if (!(mutex_trylock(&runtime->oss.params_lock)))
1080+
return -EAGAIN;
1081+
} else if (mutex_lock_interruptible(&runtime->oss.params_lock))
1082+
return -ERESTARTSYS;
1083+
1084+
err = snd_pcm_oss_change_params_locked(substream);
10711085
mutex_unlock(&runtime->oss.params_lock);
10721086
return err;
10731087
}
@@ -1096,11 +1110,14 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
10961110
return 0;
10971111
}
10981112

1113+
/* call with params_lock held */
10991114
static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
11001115
{
11011116
int err;
11021117
struct snd_pcm_runtime *runtime = substream->runtime;
11031118

1119+
if (!runtime->oss.prepare)
1120+
return 0;
11041121
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
11051122
if (err < 0) {
11061123
pcm_dbg(substream->pcm,
@@ -1120,14 +1137,35 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
11201137
struct snd_pcm_runtime *runtime;
11211138
int err;
11221139

1123-
if (substream == NULL)
1124-
return 0;
11251140
runtime = substream->runtime;
11261141
if (runtime->oss.params) {
11271142
err = snd_pcm_oss_change_params(substream, false);
11281143
if (err < 0)
11291144
return err;
11301145
}
1146+
if (runtime->oss.prepare) {
1147+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
1148+
return -ERESTARTSYS;
1149+
err = snd_pcm_oss_prepare(substream);
1150+
mutex_unlock(&runtime->oss.params_lock);
1151+
if (err < 0)
1152+
return err;
1153+
}
1154+
return 0;
1155+
}
1156+
1157+
/* call with params_lock held */
1158+
static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)
1159+
{
1160+
struct snd_pcm_runtime *runtime;
1161+
int err;
1162+
1163+
runtime = substream->runtime;
1164+
if (runtime->oss.params) {
1165+
err = snd_pcm_oss_change_params_locked(substream);
1166+
if (err < 0)
1167+
return err;
1168+
}
11311169
if (runtime->oss.prepare) {
11321170
err = snd_pcm_oss_prepare(substream);
11331171
if (err < 0)
@@ -1332,13 +1370,14 @@ static ssize_t snd_pcm_oss_write1(struct snd_pcm_substream *substream, const cha
13321370
if (atomic_read(&substream->mmap_count))
13331371
return -ENXIO;
13341372

1335-
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
1336-
return tmp;
13371373
while (bytes > 0) {
13381374
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
13391375
tmp = -ERESTARTSYS;
13401376
break;
13411377
}
1378+
tmp = snd_pcm_oss_make_ready_locked(substream);
1379+
if (tmp < 0)
1380+
goto err;
13421381
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
13431382
tmp = bytes;
13441383
if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
@@ -1439,13 +1478,14 @@ static ssize_t snd_pcm_oss_read1(struct snd_pcm_substream *substream, char __use
14391478
if (atomic_read(&substream->mmap_count))
14401479
return -ENXIO;
14411480

1442-
if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
1443-
return tmp;
14441481
while (bytes > 0) {
14451482
if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
14461483
tmp = -ERESTARTSYS;
14471484
break;
14481485
}
1486+
tmp = snd_pcm_oss_make_ready_locked(substream);
1487+
if (tmp < 0)
1488+
goto err;
14491489
if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
14501490
if (runtime->oss.buffer_used == 0) {
14511491
tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
@@ -1501,10 +1541,12 @@ static int snd_pcm_oss_reset(struct snd_pcm_oss_file *pcm_oss_file)
15011541
continue;
15021542
runtime = substream->runtime;
15031543
snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
1544+
mutex_lock(&runtime->oss.params_lock);
15041545
runtime->oss.prepare = 1;
15051546
runtime->oss.buffer_used = 0;
15061547
runtime->oss.prev_hw_ptr_period = 0;
15071548
runtime->oss.period_ptr = 0;
1549+
mutex_unlock(&runtime->oss.params_lock);
15081550
}
15091551
return 0;
15101552
}
@@ -1590,9 +1632,10 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
15901632
goto __direct;
15911633
if ((err = snd_pcm_oss_make_ready(substream)) < 0)
15921634
return err;
1635+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
1636+
return -ERESTARTSYS;
15931637
format = snd_pcm_oss_format_from(runtime->oss.format);
15941638
width = snd_pcm_format_physical_width(format);
1595-
mutex_lock(&runtime->oss.params_lock);
15961639
if (runtime->oss.buffer_used > 0) {
15971640
#ifdef OSS_DEBUG
15981641
pcm_dbg(substream->pcm, "sync: buffer_used\n");
@@ -1643,7 +1686,9 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
16431686
substream->f_flags = saved_f_flags;
16441687
if (err < 0)
16451688
return err;
1689+
mutex_lock(&runtime->oss.params_lock);
16461690
runtime->oss.prepare = 1;
1691+
mutex_unlock(&runtime->oss.params_lock);
16471692
}
16481693

16491694
substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
@@ -1654,8 +1699,10 @@ static int snd_pcm_oss_sync(struct snd_pcm_oss_file *pcm_oss_file)
16541699
err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
16551700
if (err < 0)
16561701
return err;
1702+
mutex_lock(&runtime->oss.params_lock);
16571703
runtime->oss.buffer_used = 0;
16581704
runtime->oss.prepare = 1;
1705+
mutex_unlock(&runtime->oss.params_lock);
16591706
}
16601707
return 0;
16611708
}
@@ -1674,10 +1721,13 @@ static int snd_pcm_oss_set_rate(struct snd_pcm_oss_file *pcm_oss_file, int rate)
16741721
rate = 1000;
16751722
else if (rate > 192000)
16761723
rate = 192000;
1724+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
1725+
return -ERESTARTSYS;
16771726
if (runtime->oss.rate != rate) {
16781727
runtime->oss.params = 1;
16791728
runtime->oss.rate = rate;
16801729
}
1730+
mutex_unlock(&runtime->oss.params_lock);
16811731
}
16821732
return snd_pcm_oss_get_rate(pcm_oss_file);
16831733
}
@@ -1705,10 +1755,13 @@ static int snd_pcm_oss_set_channels(struct snd_pcm_oss_file *pcm_oss_file, unsig
17051755
if (substream == NULL)
17061756
continue;
17071757
runtime = substream->runtime;
1758+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
1759+
return -ERESTARTSYS;
17081760
if (runtime->oss.channels != channels) {
17091761
runtime->oss.params = 1;
17101762
runtime->oss.channels = channels;
17111763
}
1764+
mutex_unlock(&runtime->oss.params_lock);
17121765
}
17131766
return snd_pcm_oss_get_channels(pcm_oss_file);
17141767
}
@@ -1794,10 +1847,13 @@ static int snd_pcm_oss_set_format(struct snd_pcm_oss_file *pcm_oss_file, int for
17941847
if (substream == NULL)
17951848
continue;
17961849
runtime = substream->runtime;
1850+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
1851+
return -ERESTARTSYS;
17971852
if (runtime->oss.format != format) {
17981853
runtime->oss.params = 1;
17991854
runtime->oss.format = format;
18001855
}
1856+
mutex_unlock(&runtime->oss.params_lock);
18011857
}
18021858
}
18031859
return snd_pcm_oss_get_format(pcm_oss_file);
@@ -1817,8 +1873,6 @@ static int snd_pcm_oss_set_subdivide1(struct snd_pcm_substream *substream, int s
18171873
{
18181874
struct snd_pcm_runtime *runtime;
18191875

1820-
if (substream == NULL)
1821-
return 0;
18221876
runtime = substream->runtime;
18231877
if (subdivide == 0) {
18241878
subdivide = runtime->oss.subdivision;
@@ -1842,9 +1896,16 @@ static int snd_pcm_oss_set_subdivide(struct snd_pcm_oss_file *pcm_oss_file, int
18421896

18431897
for (idx = 1; idx >= 0; --idx) {
18441898
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
1899+
struct snd_pcm_runtime *runtime;
1900+
18451901
if (substream == NULL)
18461902
continue;
1847-
if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
1903+
runtime = substream->runtime;
1904+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
1905+
return -ERESTARTSYS;
1906+
err = snd_pcm_oss_set_subdivide1(substream, subdivide);
1907+
mutex_unlock(&runtime->oss.params_lock);
1908+
if (err < 0)
18481909
return err;
18491910
}
18501911
return err;
@@ -1854,8 +1915,6 @@ static int snd_pcm_oss_set_fragment1(struct snd_pcm_substream *substream, unsign
18541915
{
18551916
struct snd_pcm_runtime *runtime;
18561917

1857-
if (substream == NULL)
1858-
return 0;
18591918
runtime = substream->runtime;
18601919
if (runtime->oss.subdivision || runtime->oss.fragshift)
18611920
return -EINVAL;
@@ -1875,9 +1934,16 @@ static int snd_pcm_oss_set_fragment(struct snd_pcm_oss_file *pcm_oss_file, unsig
18751934

18761935
for (idx = 1; idx >= 0; --idx) {
18771936
struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
1937+
struct snd_pcm_runtime *runtime;
1938+
18781939
if (substream == NULL)
18791940
continue;
1880-
if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
1941+
runtime = substream->runtime;
1942+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
1943+
return -ERESTARTSYS;
1944+
err = snd_pcm_oss_set_fragment1(substream, val);
1945+
mutex_unlock(&runtime->oss.params_lock);
1946+
if (err < 0)
18811947
return err;
18821948
}
18831949
return err;
@@ -1961,6 +2027,9 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
19612027
}
19622028
if (psubstream) {
19632029
runtime = psubstream->runtime;
2030+
cmd = 0;
2031+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
2032+
return -ERESTARTSYS;
19642033
if (trigger & PCM_ENABLE_OUTPUT) {
19652034
if (runtime->oss.trigger)
19662035
goto _skip1;
@@ -1978,13 +2047,19 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
19782047
cmd = SNDRV_PCM_IOCTL_DROP;
19792048
runtime->oss.prepare = 1;
19802049
}
1981-
err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
1982-
if (err < 0)
1983-
return err;
1984-
}
19852050
_skip1:
2051+
mutex_unlock(&runtime->oss.params_lock);
2052+
if (cmd) {
2053+
err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
2054+
if (err < 0)
2055+
return err;
2056+
}
2057+
}
19862058
if (csubstream) {
19872059
runtime = csubstream->runtime;
2060+
cmd = 0;
2061+
if (mutex_lock_interruptible(&runtime->oss.params_lock))
2062+
return -ERESTARTSYS;
19882063
if (trigger & PCM_ENABLE_INPUT) {
19892064
if (runtime->oss.trigger)
19902065
goto _skip2;
@@ -1999,11 +2074,14 @@ static int snd_pcm_oss_set_trigger(struct snd_pcm_oss_file *pcm_oss_file, int tr
19992074
cmd = SNDRV_PCM_IOCTL_DROP;
20002075
runtime->oss.prepare = 1;
20012076
}
2002-
err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
2003-
if (err < 0)
2004-
return err;
2005-
}
20062077
_skip2:
2078+
mutex_unlock(&runtime->oss.params_lock);
2079+
if (cmd) {
2080+
err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
2081+
if (err < 0)
2082+
return err;
2083+
}
2084+
}
20072085
return 0;
20082086
}
20092087

0 commit comments

Comments
 (0)