Skip to content

Commit a0830db

Browse files
committed
ALSA: Add a reference counter to card instance
For more strict protection for wild disconnections, a refcount is introduced to the card instance, and let it up/down when an object is referred via snd_lookup_*() in the open ops. The free-after-last-close check is also changed to check this refcount instead of the empty list, too. Reported-by: Matthieu CASTET <[email protected]> Cc: <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 888ea7d commit a0830db

File tree

11 files changed

+86
-32
lines changed

11 files changed

+86
-32
lines changed

include/sound/core.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ struct snd_card {
132132
int shutdown; /* this card is going down */
133133
int free_on_last_close; /* free in context of file_release */
134134
wait_queue_head_t shutdown_sleep;
135+
atomic_t refcount; /* refcount for disconnection */
135136
struct device *dev; /* device assigned to this card */
136137
struct device *card_dev; /* cardX object for sysfs */
137138

@@ -189,6 +190,7 @@ struct snd_minor {
189190
const struct file_operations *f_ops; /* file operations */
190191
void *private_data; /* private data for f_ops->open */
191192
struct device *dev; /* device for sysfs */
193+
struct snd_card *card_ptr; /* assigned card instance */
192194
};
193195

194196
/* return a device pointer linked to each sound device as a parent */
@@ -295,6 +297,7 @@ int snd_card_info_done(void);
295297
int snd_component_add(struct snd_card *card, const char *component);
296298
int snd_card_file_add(struct snd_card *card, struct file *file);
297299
int snd_card_file_remove(struct snd_card *card, struct file *file);
300+
void snd_card_unref(struct snd_card *card);
298301

299302
#define snd_card_set_dev(card, devptr) ((card)->dev = (devptr))
300303

sound/core/compress_offload.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,19 +100,23 @@ static int snd_compr_open(struct inode *inode, struct file *f)
100100

101101
if (dirn != compr->direction) {
102102
pr_err("this device doesn't support this direction\n");
103+
snd_card_unref(compr->card);
103104
return -EINVAL;
104105
}
105106

106107
data = kzalloc(sizeof(*data), GFP_KERNEL);
107-
if (!data)
108+
if (!data) {
109+
snd_card_unref(compr->card);
108110
return -ENOMEM;
111+
}
109112
data->stream.ops = compr->ops;
110113
data->stream.direction = dirn;
111114
data->stream.private_data = compr->private_data;
112115
data->stream.device = compr;
113116
runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
114117
if (!runtime) {
115118
kfree(data);
119+
snd_card_unref(compr->card);
116120
return -ENOMEM;
117121
}
118122
runtime->state = SNDRV_PCM_STATE_OPEN;
@@ -126,7 +130,8 @@ static int snd_compr_open(struct inode *inode, struct file *f)
126130
kfree(runtime);
127131
kfree(data);
128132
}
129-
return ret;
133+
snd_card_unref(compr->card);
134+
return 0;
130135
}
131136

132137
static int snd_compr_free(struct inode *inode, struct file *f)

sound/core/control.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,13 +86,16 @@ static int snd_ctl_open(struct inode *inode, struct file *file)
8686
write_lock_irqsave(&card->ctl_files_rwlock, flags);
8787
list_add_tail(&ctl->list, &card->ctl_files);
8888
write_unlock_irqrestore(&card->ctl_files_rwlock, flags);
89+
snd_card_unref(card);
8990
return 0;
9091

9192
__error:
9293
module_put(card->module);
9394
__error2:
9495
snd_card_file_remove(card, file);
9596
__error1:
97+
if (card)
98+
snd_card_unref(card);
9699
return err;
97100
}
98101

sound/core/hwdep.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,10 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
100100
if (hw == NULL)
101101
return -ENODEV;
102102

103-
if (!try_module_get(hw->card->module))
103+
if (!try_module_get(hw->card->module)) {
104+
snd_card_unref(hw->card);
104105
return -EFAULT;
106+
}
105107

106108
init_waitqueue_entry(&wait, current);
107109
add_wait_queue(&hw->open_wait, &wait);
@@ -148,6 +150,7 @@ static int snd_hwdep_open(struct inode *inode, struct file * file)
148150
mutex_unlock(&hw->open_mutex);
149151
if (err < 0)
150152
module_put(hw->card->module);
153+
snd_card_unref(hw->card);
151154
return err;
152155
}
153156

sound/core/init.c

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ int snd_card_create(int idx, const char *xid,
213213
spin_lock_init(&card->files_lock);
214214
INIT_LIST_HEAD(&card->files_list);
215215
init_waitqueue_head(&card->shutdown_sleep);
216+
atomic_set(&card->refcount, 0);
216217
#ifdef CONFIG_PM
217218
mutex_init(&card->power_lock);
218219
init_waitqueue_head(&card->power_sleep);
@@ -446,21 +447,36 @@ static int snd_card_do_free(struct snd_card *card)
446447
return 0;
447448
}
448449

450+
/**
451+
* snd_card_unref - release the reference counter
452+
* @card: the card instance
453+
*
454+
* Decrements the reference counter. When it reaches to zero, wake up
455+
* the sleeper and call the destructor if needed.
456+
*/
457+
void snd_card_unref(struct snd_card *card)
458+
{
459+
if (atomic_dec_and_test(&card->refcount)) {
460+
wake_up(&card->shutdown_sleep);
461+
if (card->free_on_last_close)
462+
snd_card_do_free(card);
463+
}
464+
}
465+
EXPORT_SYMBOL(snd_card_unref);
466+
449467
int snd_card_free_when_closed(struct snd_card *card)
450468
{
451-
int free_now = 0;
452-
int ret = snd_card_disconnect(card);
453-
if (ret)
454-
return ret;
469+
int ret;
455470

456-
spin_lock(&card->files_lock);
457-
if (list_empty(&card->files_list))
458-
free_now = 1;
459-
else
460-
card->free_on_last_close = 1;
461-
spin_unlock(&card->files_lock);
471+
atomic_inc(&card->refcount);
472+
ret = snd_card_disconnect(card);
473+
if (ret) {
474+
atomic_dec(&card->refcount);
475+
return ret;
476+
}
462477

463-
if (free_now)
478+
card->free_on_last_close = 1;
479+
if (atomic_dec_and_test(&card->refcount))
464480
snd_card_do_free(card);
465481
return 0;
466482
}
@@ -474,7 +490,7 @@ int snd_card_free(struct snd_card *card)
474490
return ret;
475491

476492
/* wait, until all devices are ready for the free operation */
477-
wait_event(card->shutdown_sleep, list_empty(&card->files_list));
493+
wait_event(card->shutdown_sleep, !atomic_read(&card->refcount));
478494
snd_card_do_free(card);
479495
return 0;
480496
}
@@ -886,6 +902,7 @@ int snd_card_file_add(struct snd_card *card, struct file *file)
886902
return -ENODEV;
887903
}
888904
list_add(&mfile->list, &card->files_list);
905+
atomic_inc(&card->refcount);
889906
spin_unlock(&card->files_lock);
890907
return 0;
891908
}
@@ -908,7 +925,6 @@ EXPORT_SYMBOL(snd_card_file_add);
908925
int snd_card_file_remove(struct snd_card *card, struct file *file)
909926
{
910927
struct snd_monitor_file *mfile, *found = NULL;
911-
int last_close = 0;
912928

913929
spin_lock(&card->files_lock);
914930
list_for_each_entry(mfile, &card->files_list, list) {
@@ -923,19 +939,13 @@ int snd_card_file_remove(struct snd_card *card, struct file *file)
923939
break;
924940
}
925941
}
926-
if (list_empty(&card->files_list))
927-
last_close = 1;
928942
spin_unlock(&card->files_lock);
929-
if (last_close) {
930-
wake_up(&card->shutdown_sleep);
931-
if (card->free_on_last_close)
932-
snd_card_do_free(card);
933-
}
934943
if (!found) {
935944
snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file);
936945
return -ENOENT;
937946
}
938947
kfree(found);
948+
snd_card_unref(card);
939949
return 0;
940950
}
941951

sound/core/oss/mixer_oss.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,19 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
5252
SNDRV_OSS_DEVICE_TYPE_MIXER);
5353
if (card == NULL)
5454
return -ENODEV;
55-
if (card->mixer_oss == NULL)
55+
if (card->mixer_oss == NULL) {
56+
snd_card_unref(card);
5657
return -ENODEV;
58+
}
5759
err = snd_card_file_add(card, file);
58-
if (err < 0)
60+
if (err < 0) {
61+
snd_card_unref(card);
5962
return err;
63+
}
6064
fmixer = kzalloc(sizeof(*fmixer), GFP_KERNEL);
6165
if (fmixer == NULL) {
6266
snd_card_file_remove(card, file);
67+
snd_card_unref(card);
6368
return -ENOMEM;
6469
}
6570
fmixer->card = card;
@@ -68,6 +73,7 @@ static int snd_mixer_oss_open(struct inode *inode, struct file *file)
6873
if (!try_module_get(card->module)) {
6974
kfree(fmixer);
7075
snd_card_file_remove(card, file);
76+
snd_card_unref(card);
7177
return -EFAULT;
7278
}
7379
return 0;

sound/core/oss/pcm_oss.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2457,6 +2457,8 @@ static int snd_pcm_oss_open(struct inode *inode, struct file *file)
24572457
__error2:
24582458
snd_card_file_remove(pcm->card, file);
24592459
__error1:
2460+
if (pcm)
2461+
snd_card_unref(pcm->card);
24602462
return err;
24612463
}
24622464

sound/core/pcm_native.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,7 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
16421642
write_unlock_irq(&snd_pcm_link_rwlock);
16431643
up_write(&snd_pcm_link_rwsem);
16441644
_nolock:
1645+
snd_card_unref(substream1->pcm->card);
16451646
fput_light(file, fput_needed);
16461647
if (res < 0)
16471648
kfree(group);
@@ -2116,7 +2117,9 @@ static int snd_pcm_playback_open(struct inode *inode, struct file *file)
21162117
return err;
21172118
pcm = snd_lookup_minor_data(iminor(inode),
21182119
SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
2119-
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
2120+
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_PLAYBACK);
2121+
snd_card_unref(pcm->card);
2122+
return err;
21202123
}
21212124

21222125
static int snd_pcm_capture_open(struct inode *inode, struct file *file)
@@ -2127,7 +2130,9 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file)
21272130
return err;
21282131
pcm = snd_lookup_minor_data(iminor(inode),
21292132
SNDRV_DEVICE_TYPE_PCM_CAPTURE);
2130-
return snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
2133+
err = snd_pcm_open(file, pcm, SNDRV_PCM_STREAM_CAPTURE);
2134+
snd_card_unref(pcm->card);
2135+
return err;
21312136
}
21322137

21332138
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)

sound/core/rawmidi.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,10 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
379379
if (rmidi == NULL)
380380
return -ENODEV;
381381

382-
if (!try_module_get(rmidi->card->module))
382+
if (!try_module_get(rmidi->card->module)) {
383+
snd_card_unref(rmidi->card);
383384
return -ENXIO;
385+
}
384386

385387
mutex_lock(&rmidi->open_mutex);
386388
card = rmidi->card;
@@ -440,13 +442,15 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
440442
#endif
441443
file->private_data = rawmidi_file;
442444
mutex_unlock(&rmidi->open_mutex);
445+
snd_card_unref(rmidi->card);
443446
return 0;
444447

445448
__error:
446449
snd_card_file_remove(card, file);
447450
__error_card:
448451
mutex_unlock(&rmidi->open_mutex);
449452
module_put(rmidi->card->module);
453+
snd_card_unref(rmidi->card);
450454
return err;
451455
}
452456

sound/core/sound.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ static void snd_request_other(int minor)
9898
*
9999
* Checks that a minor device with the specified type is registered, and returns
100100
* its user data pointer.
101+
*
102+
* This function increments the reference counter of the card instance
103+
* if an associated instance with the given minor number and type is found.
104+
* The caller must call snd_card_unref() appropriately later.
101105
*/
102106
void *snd_lookup_minor_data(unsigned int minor, int type)
103107
{
@@ -108,9 +112,11 @@ void *snd_lookup_minor_data(unsigned int minor, int type)
108112
return NULL;
109113
mutex_lock(&sound_mutex);
110114
mreg = snd_minors[minor];
111-
if (mreg && mreg->type == type)
115+
if (mreg && mreg->type == type) {
112116
private_data = mreg->private_data;
113-
else
117+
if (mreg->card_ptr)
118+
atomic_inc(&mreg->card_ptr->refcount);
119+
} else
114120
private_data = NULL;
115121
mutex_unlock(&sound_mutex);
116122
return private_data;
@@ -275,6 +281,7 @@ int snd_register_device_for_dev(int type, struct snd_card *card, int dev,
275281
preg->device = dev;
276282
preg->f_ops = f_ops;
277283
preg->private_data = private_data;
284+
preg->card_ptr = card;
278285
mutex_lock(&sound_mutex);
279286
#ifdef CONFIG_SND_DYNAMIC_MINORS
280287
minor = snd_find_free_minor(type);

0 commit comments

Comments
 (0)