Commit 59866da9e4ae54819e3c4e0a8f426bdb0c2ef993
1 parent
467b103505
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
ALSA: usb-audio: Avoid autopm calls after disconnection
Add a similar protection against the disconnection race and the invalid use of usb instance after disconnection, as well as we've done for the USB audio PCM. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=51201 Reviewd-by: Clemens Ladisch <clemens@ladisch.de> Tested-by: Clemens Ladisch <clemens@ladisch.de> Cc: <stable@vger.kernel.org> Signed-off-by: Takashi Iwai <tiwai@suse.de>
Showing 1 changed file with 22 additions and 1 deletions Side-by-side Diff
sound/usb/midi.c
... | ... | @@ -116,6 +116,7 @@ |
116 | 116 | struct list_head list; |
117 | 117 | struct timer_list error_timer; |
118 | 118 | spinlock_t disc_lock; |
119 | + struct rw_semaphore disc_rwsem; | |
119 | 120 | struct mutex mutex; |
120 | 121 | u32 usb_id; |
121 | 122 | int next_midi_device; |
... | ... | @@ -1038,6 +1039,12 @@ |
1038 | 1039 | struct snd_usb_midi* umidi = substream->rmidi->private_data; |
1039 | 1040 | struct snd_kcontrol *ctl; |
1040 | 1041 | |
1042 | + down_read(&umidi->disc_rwsem); | |
1043 | + if (umidi->disconnected) { | |
1044 | + up_read(&umidi->disc_rwsem); | |
1045 | + return; | |
1046 | + } | |
1047 | + | |
1041 | 1048 | mutex_lock(&umidi->mutex); |
1042 | 1049 | if (open) { |
1043 | 1050 | if (umidi->opened++ == 0 && umidi->roland_load_ctl) { |
... | ... | @@ -1056,6 +1063,7 @@ |
1056 | 1063 | } |
1057 | 1064 | } |
1058 | 1065 | mutex_unlock(&umidi->mutex); |
1066 | + up_read(&umidi->disc_rwsem); | |
1059 | 1067 | } |
1060 | 1068 | |
1061 | 1069 | static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) |
1062 | 1070 | |
... | ... | @@ -1076,8 +1084,15 @@ |
1076 | 1084 | snd_BUG(); |
1077 | 1085 | return -ENXIO; |
1078 | 1086 | } |
1087 | + | |
1088 | + down_read(&umidi->disc_rwsem); | |
1089 | + if (umidi->disconnected) { | |
1090 | + up_read(&umidi->disc_rwsem); | |
1091 | + return -ENODEV; | |
1092 | + } | |
1079 | 1093 | err = usb_autopm_get_interface(umidi->iface); |
1080 | 1094 | port->autopm_reference = err >= 0; |
1095 | + up_read(&umidi->disc_rwsem); | |
1081 | 1096 | if (err < 0 && err != -EACCES) |
1082 | 1097 | return -EIO; |
1083 | 1098 | substream->runtime->private_data = port; |
1084 | 1099 | |
... | ... | @@ -1092,8 +1107,10 @@ |
1092 | 1107 | struct usbmidi_out_port *port = substream->runtime->private_data; |
1093 | 1108 | |
1094 | 1109 | substream_open(substream, 0); |
1095 | - if (port->autopm_reference) | |
1110 | + down_read(&umidi->disc_rwsem); | |
1111 | + if (!umidi->disconnected && port->autopm_reference) | |
1096 | 1112 | usb_autopm_put_interface(umidi->iface); |
1113 | + up_read(&umidi->disc_rwsem); | |
1097 | 1114 | return 0; |
1098 | 1115 | } |
1099 | 1116 | |
1100 | 1117 | |
... | ... | @@ -1403,9 +1420,12 @@ |
1403 | 1420 | * a timer may submit an URB. To reliably break the cycle |
1404 | 1421 | * a flag under lock must be used |
1405 | 1422 | */ |
1423 | + down_write(&umidi->disc_rwsem); | |
1406 | 1424 | spin_lock_irq(&umidi->disc_lock); |
1407 | 1425 | umidi->disconnected = 1; |
1408 | 1426 | spin_unlock_irq(&umidi->disc_lock); |
1427 | + up_write(&umidi->disc_rwsem); | |
1428 | + | |
1409 | 1429 | for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { |
1410 | 1430 | struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; |
1411 | 1431 | if (ep->out) |
... | ... | @@ -2117,6 +2137,7 @@ |
2117 | 2137 | umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; |
2118 | 2138 | init_timer(&umidi->error_timer); |
2119 | 2139 | spin_lock_init(&umidi->disc_lock); |
2140 | + init_rwsem(&umidi->disc_rwsem); | |
2120 | 2141 | mutex_init(&umidi->mutex); |
2121 | 2142 | umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), |
2122 | 2143 | le16_to_cpu(umidi->dev->descriptor.idProduct)); |