diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-04-28 11:11:44 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-04-28 11:45:45 -0400 |
commit | 1c94e65c668f44d2c69ae7e7fc268ab3268fba3e (patch) | |
tree | 14e8a5875c542973577f789d47a1f7d9419ed6fa /sound | |
parent | 30e5f003ff4b2be86f71733b6c9b11355d66584c (diff) |
ALSA: emux: Fix mutex deadlock in OSS emulation
The OSS emulation in synth-emux helper has a potential AB/BA deadlock
at the simultaneous closing and opening:
close ->
snd_seq_release() ->
sne_seq_free_client() ->
snd_seq_delete_all_ports(): takes client->ports_mutex ->
port_delete() ->
snd_emux_unuse(): takes emux->register_mutex
open ->
snd_seq_oss_open() ->
snd_emux_open_seq_oss(): takes emux->register_mutex ->
snd_seq_event_port_attach() ->
snd_seq_create_port(): takes client->ports_mutex
This patch addresses the deadlock by reducing the rance taking
emux->register_mutex in snd_emux_open_seq_oss(). The lock is needed
for the refcount handling, so move it locally. The calls in
emux_seq.c are already with the mutex, thus they are replaced with the
version without mutex lock/unlock.
Cc: <stable@vger.kernel.org>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound')
-rw-r--r-- | sound/synth/emux/emux_oss.c | 11 | ||||
-rw-r--r-- | sound/synth/emux/emux_seq.c | 27 |
2 files changed, 22 insertions, 16 deletions
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c index ab37add269ae..82e350e9501c 100644 --- a/sound/synth/emux/emux_oss.c +++ b/sound/synth/emux/emux_oss.c | |||
@@ -118,12 +118,8 @@ snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure) | |||
118 | if (snd_BUG_ON(!arg || !emu)) | 118 | if (snd_BUG_ON(!arg || !emu)) |
119 | return -ENXIO; | 119 | return -ENXIO; |
120 | 120 | ||
121 | mutex_lock(&emu->register_mutex); | 121 | if (!snd_emux_inc_count(emu)) |
122 | |||
123 | if (!snd_emux_inc_count(emu)) { | ||
124 | mutex_unlock(&emu->register_mutex); | ||
125 | return -EFAULT; | 122 | return -EFAULT; |
126 | } | ||
127 | 123 | ||
128 | memset(&callback, 0, sizeof(callback)); | 124 | memset(&callback, 0, sizeof(callback)); |
129 | callback.owner = THIS_MODULE; | 125 | callback.owner = THIS_MODULE; |
@@ -135,7 +131,6 @@ snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure) | |||
135 | if (p == NULL) { | 131 | if (p == NULL) { |
136 | snd_printk(KERN_ERR "can't create port\n"); | 132 | snd_printk(KERN_ERR "can't create port\n"); |
137 | snd_emux_dec_count(emu); | 133 | snd_emux_dec_count(emu); |
138 | mutex_unlock(&emu->register_mutex); | ||
139 | return -ENOMEM; | 134 | return -ENOMEM; |
140 | } | 135 | } |
141 | 136 | ||
@@ -148,8 +143,6 @@ snd_emux_open_seq_oss(struct snd_seq_oss_arg *arg, void *closure) | |||
148 | reset_port_mode(p, arg->seq_mode); | 143 | reset_port_mode(p, arg->seq_mode); |
149 | 144 | ||
150 | snd_emux_reset_port(p); | 145 | snd_emux_reset_port(p); |
151 | |||
152 | mutex_unlock(&emu->register_mutex); | ||
153 | return 0; | 146 | return 0; |
154 | } | 147 | } |
155 | 148 | ||
@@ -195,13 +188,11 @@ snd_emux_close_seq_oss(struct snd_seq_oss_arg *arg) | |||
195 | if (snd_BUG_ON(!emu)) | 188 | if (snd_BUG_ON(!emu)) |
196 | return -ENXIO; | 189 | return -ENXIO; |
197 | 190 | ||
198 | mutex_lock(&emu->register_mutex); | ||
199 | snd_emux_sounds_off_all(p); | 191 | snd_emux_sounds_off_all(p); |
200 | snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port)); | 192 | snd_soundfont_close_check(emu->sflist, SF_CLIENT_NO(p->chset.port)); |
201 | snd_seq_event_port_detach(p->chset.client, p->chset.port); | 193 | snd_seq_event_port_detach(p->chset.client, p->chset.port); |
202 | snd_emux_dec_count(emu); | 194 | snd_emux_dec_count(emu); |
203 | 195 | ||
204 | mutex_unlock(&emu->register_mutex); | ||
205 | return 0; | 196 | return 0; |
206 | } | 197 | } |
207 | 198 | ||
diff --git a/sound/synth/emux/emux_seq.c b/sound/synth/emux/emux_seq.c index 188fda0effb0..a0209204ae48 100644 --- a/sound/synth/emux/emux_seq.c +++ b/sound/synth/emux/emux_seq.c | |||
@@ -267,8 +267,8 @@ snd_emux_event_input(struct snd_seq_event *ev, int direct, void *private_data, | |||
267 | /* | 267 | /* |
268 | * increment usage count | 268 | * increment usage count |
269 | */ | 269 | */ |
270 | int | 270 | static int |
271 | snd_emux_inc_count(struct snd_emux *emu) | 271 | __snd_emux_inc_count(struct snd_emux *emu) |
272 | { | 272 | { |
273 | emu->used++; | 273 | emu->used++; |
274 | if (!try_module_get(emu->ops.owner)) | 274 | if (!try_module_get(emu->ops.owner)) |
@@ -282,12 +282,21 @@ snd_emux_inc_count(struct snd_emux *emu) | |||
282 | return 1; | 282 | return 1; |
283 | } | 283 | } |
284 | 284 | ||
285 | int snd_emux_inc_count(struct snd_emux *emu) | ||
286 | { | ||
287 | int ret; | ||
288 | |||
289 | mutex_lock(&emu->register_mutex); | ||
290 | ret = __snd_emux_inc_count(emu); | ||
291 | mutex_unlock(&emu->register_mutex); | ||
292 | return ret; | ||
293 | } | ||
285 | 294 | ||
286 | /* | 295 | /* |
287 | * decrease usage count | 296 | * decrease usage count |
288 | */ | 297 | */ |
289 | void | 298 | static void |
290 | snd_emux_dec_count(struct snd_emux *emu) | 299 | __snd_emux_dec_count(struct snd_emux *emu) |
291 | { | 300 | { |
292 | module_put(emu->card->module); | 301 | module_put(emu->card->module); |
293 | emu->used--; | 302 | emu->used--; |
@@ -296,6 +305,12 @@ snd_emux_dec_count(struct snd_emux *emu) | |||
296 | module_put(emu->ops.owner); | 305 | module_put(emu->ops.owner); |
297 | } | 306 | } |
298 | 307 | ||
308 | void snd_emux_dec_count(struct snd_emux *emu) | ||
309 | { | ||
310 | mutex_lock(&emu->register_mutex); | ||
311 | __snd_emux_dec_count(emu); | ||
312 | mutex_unlock(&emu->register_mutex); | ||
313 | } | ||
299 | 314 | ||
300 | /* | 315 | /* |
301 | * Routine that is called upon a first use of a particular port | 316 | * Routine that is called upon a first use of a particular port |
@@ -315,7 +330,7 @@ snd_emux_use(void *private_data, struct snd_seq_port_subscribe *info) | |||
315 | 330 | ||
316 | mutex_lock(&emu->register_mutex); | 331 | mutex_lock(&emu->register_mutex); |
317 | snd_emux_init_port(p); | 332 | snd_emux_init_port(p); |
318 | snd_emux_inc_count(emu); | 333 | __snd_emux_inc_count(emu); |
319 | mutex_unlock(&emu->register_mutex); | 334 | mutex_unlock(&emu->register_mutex); |
320 | return 0; | 335 | return 0; |
321 | } | 336 | } |
@@ -338,7 +353,7 @@ snd_emux_unuse(void *private_data, struct snd_seq_port_subscribe *info) | |||
338 | 353 | ||
339 | mutex_lock(&emu->register_mutex); | 354 | mutex_lock(&emu->register_mutex); |
340 | snd_emux_sounds_off_all(p); | 355 | snd_emux_sounds_off_all(p); |
341 | snd_emux_dec_count(emu); | 356 | __snd_emux_dec_count(emu); |
342 | mutex_unlock(&emu->register_mutex); | 357 | mutex_unlock(&emu->register_mutex); |
343 | return 0; | 358 | return 0; |
344 | } | 359 | } |