]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blame - releases/4.16.4/alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch
fix up queue-5.15/mm-fix-race-between-__split_huge_pmd_locked-and-gup-.patch
[thirdparty/kernel/stable-queue.git] / releases / 4.16.4 / alsa-pcm-avoid-potential-races-between-oss-ioctls-and-read-write.patch
CommitLineData
7f30898e
GKH
1From 02a5d6925cd34c3b774bdb8eefb057c40a30e870 Mon Sep 17 00:00:00 2001
2From: Takashi Iwai <tiwai@suse.de>
3Date: Thu, 22 Mar 2018 18:10:14 +0100
4Subject: ALSA: pcm: Avoid potential races between OSS ioctls and read/write
5
6From: Takashi Iwai <tiwai@suse.de>
7
8commit 02a5d6925cd34c3b774bdb8eefb057c40a30e870 upstream.
9
10Although we apply the params_lock mutex to the whole read and write
11operations as well as snd_pcm_oss_change_params(), we may still face
12some races.
13
14First off, the params_lock is taken inside the read and write loop.
15This is intentional for avoiding the too long locking, but it allows
16the in-between parameter change, which might lead to invalid
17pointers. We check the readiness of the stream and set up via
18snd_pcm_oss_make_ready() at the beginning of read and write, but it's
19called only once, by assuming that it remains ready in the rest.
20
21Second, many ioctls that may change the actual parameters
22(i.e. setting runtime->oss.params=1) aren't protected, hence they can
23be processed in a half-baked state.
24
25This patch is an attempt to plug these holes. The stream readiness
26check is moved inside the read/write inner loop, so that the stream is
27always set up in a proper state before further processing. Also, each
28ioctl that may change the parameter is wrapped with the params_lock
29for avoiding the races.
30
31The issues were triggered by syzkaller in a few different scenarios,
32particularly the one below appearing as GPF in loopback_pos_update.
33
34Reported-by: syzbot+c4227aec125487ec3efa@syzkaller.appspotmail.com
35Cc: <stable@vger.kernel.org>
36Signed-off-by: Takashi Iwai <tiwai@suse.de>
37Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
38
39---
40 sound/core/oss/pcm_oss.c | 134 +++++++++++++++++++++++++++++++++++++----------
41 1 file changed, 106 insertions(+), 28 deletions(-)
42
43--- a/sound/core/oss/pcm_oss.c
44+++ b/sound/core/oss/pcm_oss.c
45@@ -823,8 +823,8 @@ static int choose_rate(struct snd_pcm_su
46 return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
47 }
48
49-static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
50- bool trylock)
51+/* call with params_lock held */
52+static int snd_pcm_oss_change_params_locked(struct snd_pcm_substream *substream)
53 {
54 struct snd_pcm_runtime *runtime = substream->runtime;
55 struct snd_pcm_hw_params *params, *sparams;
56@@ -838,11 +838,8 @@ static int snd_pcm_oss_change_params(str
57 const struct snd_mask *sformat_mask;
58 struct snd_mask mask;
59
60- if (trylock) {
61- if (!(mutex_trylock(&runtime->oss.params_lock)))
62- return -EAGAIN;
63- } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
64- return -ERESTARTSYS;
65+ if (!runtime->oss.params)
66+ return 0;
67 sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
68 params = kmalloc(sizeof(*params), GFP_KERNEL);
69 sparams = kmalloc(sizeof(*sparams), GFP_KERNEL);
70@@ -1068,6 +1065,23 @@ failure:
71 kfree(sw_params);
72 kfree(params);
73 kfree(sparams);
74+ return err;
75+}
76+
77+/* this one takes the lock by itself */
78+static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
79+ bool trylock)
80+{
81+ struct snd_pcm_runtime *runtime = substream->runtime;
82+ int err;
83+
84+ if (trylock) {
85+ if (!(mutex_trylock(&runtime->oss.params_lock)))
86+ return -EAGAIN;
87+ } else if (mutex_lock_interruptible(&runtime->oss.params_lock))
88+ return -ERESTARTSYS;
89+
90+ err = snd_pcm_oss_change_params_locked(substream);
91 mutex_unlock(&runtime->oss.params_lock);
92 return err;
93 }
94@@ -1096,11 +1110,14 @@ static int snd_pcm_oss_get_active_substr
95 return 0;
96 }
97
98+/* call with params_lock held */
99 static int snd_pcm_oss_prepare(struct snd_pcm_substream *substream)
100 {
101 int err;
102 struct snd_pcm_runtime *runtime = substream->runtime;
103
104+ if (!runtime->oss.prepare)
105+ return 0;
106 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_PREPARE, NULL);
107 if (err < 0) {
108 pcm_dbg(substream->pcm,
109@@ -1120,8 +1137,6 @@ static int snd_pcm_oss_make_ready(struct
110 struct snd_pcm_runtime *runtime;
111 int err;
112
113- if (substream == NULL)
114- return 0;
115 runtime = substream->runtime;
116 if (runtime->oss.params) {
117 err = snd_pcm_oss_change_params(substream, false);
118@@ -1129,6 +1144,29 @@ static int snd_pcm_oss_make_ready(struct
119 return err;
120 }
121 if (runtime->oss.prepare) {
122+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
123+ return -ERESTARTSYS;
124+ err = snd_pcm_oss_prepare(substream);
125+ mutex_unlock(&runtime->oss.params_lock);
126+ if (err < 0)
127+ return err;
128+ }
129+ return 0;
130+}
131+
132+/* call with params_lock held */
133+static int snd_pcm_oss_make_ready_locked(struct snd_pcm_substream *substream)
134+{
135+ struct snd_pcm_runtime *runtime;
136+ int err;
137+
138+ runtime = substream->runtime;
139+ if (runtime->oss.params) {
140+ err = snd_pcm_oss_change_params_locked(substream);
141+ if (err < 0)
142+ return err;
143+ }
144+ if (runtime->oss.prepare) {
145 err = snd_pcm_oss_prepare(substream);
146 if (err < 0)
147 return err;
148@@ -1332,13 +1370,14 @@ static ssize_t snd_pcm_oss_write1(struct
149 if (atomic_read(&substream->mmap_count))
150 return -ENXIO;
151
152- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
153- return tmp;
154 while (bytes > 0) {
155 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
156 tmp = -ERESTARTSYS;
157 break;
158 }
159+ tmp = snd_pcm_oss_make_ready_locked(substream);
160+ if (tmp < 0)
161+ goto err;
162 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
163 tmp = bytes;
164 if (tmp + runtime->oss.buffer_used > runtime->oss.period_bytes)
165@@ -1439,13 +1478,14 @@ static ssize_t snd_pcm_oss_read1(struct
166 if (atomic_read(&substream->mmap_count))
167 return -ENXIO;
168
169- if ((tmp = snd_pcm_oss_make_ready(substream)) < 0)
170- return tmp;
171 while (bytes > 0) {
172 if (mutex_lock_interruptible(&runtime->oss.params_lock)) {
173 tmp = -ERESTARTSYS;
174 break;
175 }
176+ tmp = snd_pcm_oss_make_ready_locked(substream);
177+ if (tmp < 0)
178+ goto err;
179 if (bytes < runtime->oss.period_bytes || runtime->oss.buffer_used > 0) {
180 if (runtime->oss.buffer_used == 0) {
181 tmp = snd_pcm_oss_read2(substream, runtime->oss.buffer, runtime->oss.period_bytes, 1);
182@@ -1501,10 +1541,12 @@ static int snd_pcm_oss_reset(struct snd_
183 continue;
184 runtime = substream->runtime;
185 snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
186+ mutex_lock(&runtime->oss.params_lock);
187 runtime->oss.prepare = 1;
188 runtime->oss.buffer_used = 0;
189 runtime->oss.prev_hw_ptr_period = 0;
190 runtime->oss.period_ptr = 0;
191+ mutex_unlock(&runtime->oss.params_lock);
192 }
193 return 0;
194 }
195@@ -1590,9 +1632,10 @@ static int snd_pcm_oss_sync(struct snd_p
196 goto __direct;
197 if ((err = snd_pcm_oss_make_ready(substream)) < 0)
198 return err;
199+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
200+ return -ERESTARTSYS;
201 format = snd_pcm_oss_format_from(runtime->oss.format);
202 width = snd_pcm_format_physical_width(format);
203- mutex_lock(&runtime->oss.params_lock);
204 if (runtime->oss.buffer_used > 0) {
205 #ifdef OSS_DEBUG
206 pcm_dbg(substream->pcm, "sync: buffer_used\n");
207@@ -1643,7 +1686,9 @@ static int snd_pcm_oss_sync(struct snd_p
208 substream->f_flags = saved_f_flags;
209 if (err < 0)
210 return err;
211+ mutex_lock(&runtime->oss.params_lock);
212 runtime->oss.prepare = 1;
213+ mutex_unlock(&runtime->oss.params_lock);
214 }
215
216 substream = pcm_oss_file->streams[SNDRV_PCM_STREAM_CAPTURE];
217@@ -1654,8 +1699,10 @@ static int snd_pcm_oss_sync(struct snd_p
218 err = snd_pcm_kernel_ioctl(substream, SNDRV_PCM_IOCTL_DROP, NULL);
219 if (err < 0)
220 return err;
221+ mutex_lock(&runtime->oss.params_lock);
222 runtime->oss.buffer_used = 0;
223 runtime->oss.prepare = 1;
224+ mutex_unlock(&runtime->oss.params_lock);
225 }
226 return 0;
227 }
228@@ -1674,10 +1721,13 @@ static int snd_pcm_oss_set_rate(struct s
229 rate = 1000;
230 else if (rate > 192000)
231 rate = 192000;
232+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
233+ return -ERESTARTSYS;
234 if (runtime->oss.rate != rate) {
235 runtime->oss.params = 1;
236 runtime->oss.rate = rate;
237 }
238+ mutex_unlock(&runtime->oss.params_lock);
239 }
240 return snd_pcm_oss_get_rate(pcm_oss_file);
241 }
242@@ -1705,10 +1755,13 @@ static int snd_pcm_oss_set_channels(stru
243 if (substream == NULL)
244 continue;
245 runtime = substream->runtime;
246+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
247+ return -ERESTARTSYS;
248 if (runtime->oss.channels != channels) {
249 runtime->oss.params = 1;
250 runtime->oss.channels = channels;
251 }
252+ mutex_unlock(&runtime->oss.params_lock);
253 }
254 return snd_pcm_oss_get_channels(pcm_oss_file);
255 }
256@@ -1794,10 +1847,13 @@ static int snd_pcm_oss_set_format(struct
257 if (substream == NULL)
258 continue;
259 runtime = substream->runtime;
260+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
261+ return -ERESTARTSYS;
262 if (runtime->oss.format != format) {
263 runtime->oss.params = 1;
264 runtime->oss.format = format;
265 }
266+ mutex_unlock(&runtime->oss.params_lock);
267 }
268 }
269 return snd_pcm_oss_get_format(pcm_oss_file);
270@@ -1817,8 +1873,6 @@ static int snd_pcm_oss_set_subdivide1(st
271 {
272 struct snd_pcm_runtime *runtime;
273
274- if (substream == NULL)
275- return 0;
276 runtime = substream->runtime;
277 if (subdivide == 0) {
278 subdivide = runtime->oss.subdivision;
279@@ -1842,9 +1896,16 @@ static int snd_pcm_oss_set_subdivide(str
280
281 for (idx = 1; idx >= 0; --idx) {
282 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
283+ struct snd_pcm_runtime *runtime;
284+
285 if (substream == NULL)
286 continue;
287- if ((err = snd_pcm_oss_set_subdivide1(substream, subdivide)) < 0)
288+ runtime = substream->runtime;
289+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
290+ return -ERESTARTSYS;
291+ err = snd_pcm_oss_set_subdivide1(substream, subdivide);
292+ mutex_unlock(&runtime->oss.params_lock);
293+ if (err < 0)
294 return err;
295 }
296 return err;
297@@ -1854,8 +1915,6 @@ static int snd_pcm_oss_set_fragment1(str
298 {
299 struct snd_pcm_runtime *runtime;
300
301- if (substream == NULL)
302- return 0;
303 runtime = substream->runtime;
304 if (runtime->oss.subdivision || runtime->oss.fragshift)
305 return -EINVAL;
306@@ -1875,9 +1934,16 @@ static int snd_pcm_oss_set_fragment(stru
307
308 for (idx = 1; idx >= 0; --idx) {
309 struct snd_pcm_substream *substream = pcm_oss_file->streams[idx];
310+ struct snd_pcm_runtime *runtime;
311+
312 if (substream == NULL)
313 continue;
314- if ((err = snd_pcm_oss_set_fragment1(substream, val)) < 0)
315+ runtime = substream->runtime;
316+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
317+ return -ERESTARTSYS;
318+ err = snd_pcm_oss_set_fragment1(substream, val);
319+ mutex_unlock(&runtime->oss.params_lock);
320+ if (err < 0)
321 return err;
322 }
323 return err;
324@@ -1961,6 +2027,9 @@ static int snd_pcm_oss_set_trigger(struc
325 }
326 if (psubstream) {
327 runtime = psubstream->runtime;
328+ cmd = 0;
329+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
330+ return -ERESTARTSYS;
331 if (trigger & PCM_ENABLE_OUTPUT) {
332 if (runtime->oss.trigger)
333 goto _skip1;
334@@ -1978,13 +2047,19 @@ static int snd_pcm_oss_set_trigger(struc
335 cmd = SNDRV_PCM_IOCTL_DROP;
336 runtime->oss.prepare = 1;
337 }
338- err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
339- if (err < 0)
340- return err;
341- }
342 _skip1:
343+ mutex_unlock(&runtime->oss.params_lock);
344+ if (cmd) {
345+ err = snd_pcm_kernel_ioctl(psubstream, cmd, NULL);
346+ if (err < 0)
347+ return err;
348+ }
349+ }
350 if (csubstream) {
351 runtime = csubstream->runtime;
352+ cmd = 0;
353+ if (mutex_lock_interruptible(&runtime->oss.params_lock))
354+ return -ERESTARTSYS;
355 if (trigger & PCM_ENABLE_INPUT) {
356 if (runtime->oss.trigger)
357 goto _skip2;
358@@ -1999,11 +2074,14 @@ static int snd_pcm_oss_set_trigger(struc
359 cmd = SNDRV_PCM_IOCTL_DROP;
360 runtime->oss.prepare = 1;
361 }
362- err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
363- if (err < 0)
364- return err;
365- }
366 _skip2:
367+ mutex_unlock(&runtime->oss.params_lock);
368+ if (cmd) {
369+ err = snd_pcm_kernel_ioctl(csubstream, cmd, NULL);
370+ if (err < 0)
371+ return err;
372+ }
373+ }
374 return 0;
375 }
376