Skip to content

Commit 7f0973e

Browse files
committed
ALSA: seq: Fix lockdep warnings due to double mutex locks
The port subscription code uses double mutex locks for source and destination ports, and this may become racy once when wrongly set up. It leads to lockdep warning splat, typically triggered by fuzzer like syzkaller, although the actual deadlock hasn't been seen, so far. This patch simplifies the handling by reducing to two single locks, so that no lockdep warning will be trigger any longer. By splitting to two actions, a still-in-progress element shall be added in one list while handling another. For ignoring this element, a new check is added in deliver_to_subscribers(). Along with it, the code to add/remove the subscribers list element was cleaned up and refactored. BugLink: http://lkml.kernel.org/r/CACT4Y+aKQXV7xkBW9hpQbzaDO7LrUvohxWh-UwMxXjDy-yBD=A@mail.gmail.com Reported-by: Dmitry Vyukov <[email protected]> Tested-by: Dmitry Vyukov <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 81f5775 commit 7f0973e

File tree

2 files changed

+133
-103
lines changed

2 files changed

+133
-103
lines changed

sound/core/seq/seq_clientmgr.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
678678
else
679679
down_read(&grp->list_mutex);
680680
list_for_each_entry(subs, &grp->list_head, src_list) {
681+
/* both ports ready? */
682+
if (atomic_read(&subs->ref_count) != 2)
683+
continue;
681684
event->dest = subs->info.dest;
682685
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
683686
/* convert time according to flag with subscription */

sound/core/seq/seq_ports.c

Lines changed: 130 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,6 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
173173
}
174174

175175
/* */
176-
enum group_type {
177-
SRC_LIST, DEST_LIST
178-
};
179-
180176
static int subscribe_port(struct snd_seq_client *client,
181177
struct snd_seq_client_port *port,
182178
struct snd_seq_port_subs_info *grp,
@@ -203,14 +199,28 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
203199
return NULL;
204200
}
205201

202+
static void delete_and_unsubscribe_port(struct snd_seq_client *client,
203+
struct snd_seq_client_port *port,
204+
struct snd_seq_subscribers *subs,
205+
bool is_src, bool ack);
206+
207+
static inline struct snd_seq_subscribers *
208+
get_subscriber(struct list_head *p, bool is_src)
209+
{
210+
if (is_src)
211+
return list_entry(p, struct snd_seq_subscribers, src_list);
212+
else
213+
return list_entry(p, struct snd_seq_subscribers, dest_list);
214+
}
215+
206216
/*
207217
* remove all subscribers on the list
208218
* this is called from port_delete, for each src and dest list.
209219
*/
210220
static void clear_subscriber_list(struct snd_seq_client *client,
211221
struct snd_seq_client_port *port,
212222
struct snd_seq_port_subs_info *grp,
213-
int grptype)
223+
int is_src)
214224
{
215225
struct list_head *p, *n;
216226

@@ -219,37 +229,28 @@ static void clear_subscriber_list(struct snd_seq_client *client,
219229
struct snd_seq_client *c;
220230
struct snd_seq_client_port *aport;
221231

222-
if (grptype == SRC_LIST) {
223-
subs = list_entry(p, struct snd_seq_subscribers, src_list);
232+
subs = get_subscriber(p, is_src);
233+
if (is_src)
224234
aport = get_client_port(&subs->info.dest, &c);
225-
} else {
226-
subs = list_entry(p, struct snd_seq_subscribers, dest_list);
235+
else
227236
aport = get_client_port(&subs->info.sender, &c);
228-
}
229-
list_del(p);
230-
unsubscribe_port(client, port, grp, &subs->info, 0);
237+
delete_and_unsubscribe_port(client, port, subs, is_src, false);
238+
231239
if (!aport) {
232240
/* looks like the connected port is being deleted.
233241
* we decrease the counter, and when both ports are deleted
234242
* remove the subscriber info
235243
*/
236244
if (atomic_dec_and_test(&subs->ref_count))
237245
kfree(subs);
238-
} else {
239-
/* ok we got the connected port */
240-
struct snd_seq_port_subs_info *agrp;
241-
agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
242-
down_write(&agrp->list_mutex);
243-
if (grptype == SRC_LIST)
244-
list_del(&subs->dest_list);
245-
else
246-
list_del(&subs->src_list);
247-
up_write(&agrp->list_mutex);
248-
unsubscribe_port(c, aport, agrp, &subs->info, 1);
249-
kfree(subs);
250-
snd_seq_port_unlock(aport);
251-
snd_seq_client_unlock(c);
246+
continue;
252247
}
248+
249+
/* ok we got the connected port */
250+
delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
251+
kfree(subs);
252+
snd_seq_port_unlock(aport);
253+
snd_seq_client_unlock(c);
253254
}
254255
}
255256

@@ -262,8 +263,8 @@ static int port_delete(struct snd_seq_client *client,
262263
snd_use_lock_sync(&port->use_lock);
263264

264265
/* clear subscribers info */
265-
clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
266-
clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
266+
clear_subscriber_list(client, port, &port->c_src, true);
267+
clear_subscriber_list(client, port, &port->c_dest, false);
267268

268269
if (port->private_free)
269270
port->private_free(port->private_data);
@@ -479,85 +480,120 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
479480
return 0;
480481
}
481482

482-
483-
/* connect two ports */
484-
int snd_seq_port_connect(struct snd_seq_client *connector,
485-
struct snd_seq_client *src_client,
486-
struct snd_seq_client_port *src_port,
487-
struct snd_seq_client *dest_client,
488-
struct snd_seq_client_port *dest_port,
489-
struct snd_seq_port_subscribe *info)
483+
static int check_and_subscribe_port(struct snd_seq_client *client,
484+
struct snd_seq_client_port *port,
485+
struct snd_seq_subscribers *subs,
486+
bool is_src, bool exclusive, bool ack)
490487
{
491-
struct snd_seq_port_subs_info *src = &src_port->c_src;
492-
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
493-
struct snd_seq_subscribers *subs, *s;
494-
int err, src_called = 0;
495-
unsigned long flags;
496-
int exclusive;
488+
struct snd_seq_port_subs_info *grp;
489+
struct list_head *p;
490+
struct snd_seq_subscribers *s;
491+
int err;
497492

498-
subs = kzalloc(sizeof(*subs), GFP_KERNEL);
499-
if (! subs)
500-
return -ENOMEM;
501-
502-
subs->info = *info;
503-
atomic_set(&subs->ref_count, 2);
504-
505-
down_write(&src->list_mutex);
506-
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
507-
508-
exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
493+
grp = is_src ? &port->c_src : &port->c_dest;
509494
err = -EBUSY;
495+
down_write(&grp->list_mutex);
510496
if (exclusive) {
511-
if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
497+
if (!list_empty(&grp->list_head))
512498
goto __error;
513499
} else {
514-
if (src->exclusive || dest->exclusive)
500+
if (grp->exclusive)
515501
goto __error;
516502
/* check whether already exists */
517-
list_for_each_entry(s, &src->list_head, src_list) {
518-
if (match_subs_info(info, &s->info))
519-
goto __error;
520-
}
521-
list_for_each_entry(s, &dest->list_head, dest_list) {
522-
if (match_subs_info(info, &s->info))
503+
list_for_each(p, &grp->list_head) {
504+
s = get_subscriber(p, is_src);
505+
if (match_subs_info(&subs->info, &s->info))
523506
goto __error;
524507
}
525508
}
526509

527-
if ((err = subscribe_port(src_client, src_port, src, info,
528-
connector->number != src_client->number)) < 0)
529-
goto __error;
530-
src_called = 1;
531-
532-
if ((err = subscribe_port(dest_client, dest_port, dest, info,
533-
connector->number != dest_client->number)) < 0)
510+
err = subscribe_port(client, port, grp, &subs->info, ack);
511+
if (err < 0) {
512+
grp->exclusive = 0;
534513
goto __error;
514+
}
535515

536516
/* add to list */
537-
write_lock_irqsave(&src->list_lock, flags);
538-
// write_lock(&dest->list_lock); // no other lock yet
539-
list_add_tail(&subs->src_list, &src->list_head);
540-
list_add_tail(&subs->dest_list, &dest->list_head);
541-
// write_unlock(&dest->list_lock); // no other lock yet
542-
write_unlock_irqrestore(&src->list_lock, flags);
517+
write_lock_irq(&grp->list_lock);
518+
if (is_src)
519+
list_add_tail(&subs->src_list, &grp->list_head);
520+
else
521+
list_add_tail(&subs->dest_list, &grp->list_head);
522+
grp->exclusive = exclusive;
523+
atomic_inc(&subs->ref_count);
524+
write_unlock_irq(&grp->list_lock);
525+
err = 0;
526+
527+
__error:
528+
up_write(&grp->list_mutex);
529+
return err;
530+
}
543531

544-
src->exclusive = dest->exclusive = exclusive;
532+
static void delete_and_unsubscribe_port(struct snd_seq_client *client,
533+
struct snd_seq_client_port *port,
534+
struct snd_seq_subscribers *subs,
535+
bool is_src, bool ack)
536+
{
537+
struct snd_seq_port_subs_info *grp;
538+
539+
grp = is_src ? &port->c_src : &port->c_dest;
540+
down_write(&grp->list_mutex);
541+
write_lock_irq(&grp->list_lock);
542+
if (is_src)
543+
list_del(&subs->src_list);
544+
else
545+
list_del(&subs->dest_list);
546+
grp->exclusive = 0;
547+
write_unlock_irq(&grp->list_lock);
548+
up_write(&grp->list_mutex);
549+
550+
unsubscribe_port(client, port, grp, &subs->info, ack);
551+
}
552+
553+
/* connect two ports */
554+
int snd_seq_port_connect(struct snd_seq_client *connector,
555+
struct snd_seq_client *src_client,
556+
struct snd_seq_client_port *src_port,
557+
struct snd_seq_client *dest_client,
558+
struct snd_seq_client_port *dest_port,
559+
struct snd_seq_port_subscribe *info)
560+
{
561+
struct snd_seq_subscribers *subs;
562+
bool exclusive;
563+
int err;
564+
565+
subs = kzalloc(sizeof(*subs), GFP_KERNEL);
566+
if (!subs)
567+
return -ENOMEM;
568+
569+
subs->info = *info;
570+
atomic_set(&subs->ref_count, 0);
571+
INIT_LIST_HEAD(&subs->src_list);
572+
INIT_LIST_HEAD(&subs->dest_list);
573+
574+
exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
575+
576+
err = check_and_subscribe_port(src_client, src_port, subs, true,
577+
exclusive,
578+
connector->number != src_client->number);
579+
if (err < 0)
580+
goto error;
581+
err = check_and_subscribe_port(dest_client, dest_port, subs, false,
582+
exclusive,
583+
connector->number != dest_client->number);
584+
if (err < 0)
585+
goto error_dest;
545586

546-
up_write(&dest->list_mutex);
547-
up_write(&src->list_mutex);
548587
return 0;
549588

550-
__error:
551-
if (src_called)
552-
unsubscribe_port(src_client, src_port, src, info,
553-
connector->number != src_client->number);
589+
error_dest:
590+
delete_and_unsubscribe_port(src_client, src_port, subs, true,
591+
connector->number != src_client->number);
592+
error:
554593
kfree(subs);
555-
up_write(&dest->list_mutex);
556-
up_write(&src->list_mutex);
557594
return err;
558595
}
559596

560-
561597
/* remove the connection */
562598
int snd_seq_port_disconnect(struct snd_seq_client *connector,
563599
struct snd_seq_client *src_client,
@@ -567,37 +603,28 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
567603
struct snd_seq_port_subscribe *info)
568604
{
569605
struct snd_seq_port_subs_info *src = &src_port->c_src;
570-
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
571606
struct snd_seq_subscribers *subs;
572607
int err = -ENOENT;
573-
unsigned long flags;
574608

575609
down_write(&src->list_mutex);
576-
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
577-
578610
/* look for the connection */
579611
list_for_each_entry(subs, &src->list_head, src_list) {
580612
if (match_subs_info(info, &subs->info)) {
581-
write_lock_irqsave(&src->list_lock, flags);
582-
// write_lock(&dest->list_lock); // no lock yet
583-
list_del(&subs->src_list);
584-
list_del(&subs->dest_list);
585-
// write_unlock(&dest->list_lock);
586-
write_unlock_irqrestore(&src->list_lock, flags);
587-
src->exclusive = dest->exclusive = 0;
588-
unsubscribe_port(src_client, src_port, src, info,
589-
connector->number != src_client->number);
590-
unsubscribe_port(dest_client, dest_port, dest, info,
591-
connector->number != dest_client->number);
592-
kfree(subs);
613+
atomic_dec(&subs->ref_count); /* mark as not ready */
593614
err = 0;
594615
break;
595616
}
596617
}
597-
598-
up_write(&dest->list_mutex);
599618
up_write(&src->list_mutex);
600-
return err;
619+
if (err < 0)
620+
return err;
621+
622+
delete_and_unsubscribe_port(src_client, src_port, subs, true,
623+
connector->number != src_client->number);
624+
delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
625+
connector->number != dest_client->number);
626+
kfree(subs);
627+
return 0;
601628
}
602629

603630

0 commit comments

Comments
 (0)