aboutsummaryrefslogtreecommitdiffstats
path: root/sound/pci/hda
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2012-05-19 11:21:25 -0400
committerTakashi Iwai <tiwai@suse.de>2012-05-19 11:25:23 -0400
commit09cf03b80c593b08e8b630a145e14f485200b5af (patch)
tree21a5d5b26f68bf3a4076c76e29d9eb3dc47e1bc5 /sound/pci/hda
parentc882246d840073a3dd0533ca164dfcbd7f6bd207 (diff)
ALSA: hda - Fix possible races of accesses to connection list array
Like the previous fixes for cache hash accesses, a protection over accesses to the widget connection list array must be provided. Together with this action, remove snd_hda_get_conn_list() which can be always race, and replace it with either snd_hda_get_num_conns() or snd_hda_get_connections() calls. Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/hda_codec.c82
-rw-r--r--sound/pci/hda/hda_codec.h7
-rw-r--r--sound/pci/hda/patch_realtek.c17
-rw-r--r--sound/pci/hda/patch_via.c2
4 files changed, 52 insertions, 56 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index c556fe1c25eb..eb09a3348325 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -334,78 +334,67 @@ static hda_nid_t *lookup_conn_list(struct snd_array *array, hda_nid_t nid)
334 return NULL; 334 return NULL;
335} 335}
336 336
337/* read the connection and add to the cache */
338static int read_and_add_raw_conns(struct hda_codec *codec, hda_nid_t nid)
339{
340 hda_nid_t list[HDA_MAX_CONNECTIONS];
341 int len;
342
343 len = snd_hda_get_raw_connections(codec, nid, list, ARRAY_SIZE(list));
344 if (len < 0)
345 return len;
346 return snd_hda_override_conn_list(codec, nid, len, list);
347}
348
337/** 349/**
338 * snd_hda_get_conn_list - get connection list 350 * snd_hda_get_connections - copy connection list
339 * @codec: the HDA codec 351 * @codec: the HDA codec
340 * @nid: NID to parse 352 * @nid: NID to parse
341 * @listp: the pointer to store NID list 353 * @conn_list: connection list array; when NULL, checks only the size
354 * @max_conns: max. number of connections to store
342 * 355 *
343 * Parses the connection list of the given widget and stores the list 356 * Parses the connection list of the given widget and stores the list
344 * of NIDs. 357 * of NIDs.
345 * 358 *
346 * Returns the number of connections, or a negative error code. 359 * Returns the number of connections, or a negative error code.
347 */ 360 */
348int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, 361int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
349 const hda_nid_t **listp) 362 hda_nid_t *conn_list, int max_conns)
350{ 363{
351 struct snd_array *array = &codec->conn_lists; 364 struct snd_array *array = &codec->conn_lists;
352 int len, err; 365 int len;
353 hda_nid_t list[HDA_MAX_CONNECTIONS];
354 hda_nid_t *p; 366 hda_nid_t *p;
355 bool added = false; 367 bool added = false;
356 368
357 again: 369 again:
370 mutex_lock(&codec->hash_mutex);
371 len = -1;
358 /* if the connection-list is already cached, read it */ 372 /* if the connection-list is already cached, read it */
359 p = lookup_conn_list(array, nid); 373 p = lookup_conn_list(array, nid);
360 if (p) { 374 if (p) {
361 if (listp) 375 len = p[1];
362 *listp = p + 2; 376 if (conn_list && len > max_conns) {
363 return p[1]; 377 snd_printk(KERN_ERR "hda_codec: "
378 "Too many connections %d for NID 0x%x\n",
379 len, nid);
380 mutex_unlock(&codec->hash_mutex);
381 return -EINVAL;
382 }
383 if (conn_list && len)
384 memcpy(conn_list, p + 2, len * sizeof(hda_nid_t));
364 } 385 }
386 mutex_unlock(&codec->hash_mutex);
387 if (len >= 0)
388 return len;
365 if (snd_BUG_ON(added)) 389 if (snd_BUG_ON(added))
366 return -EINVAL; 390 return -EINVAL;
367 391
368 /* read the connection and add to the cache */ 392 len = read_and_add_raw_conns(codec, nid);
369 len = snd_hda_get_raw_connections(codec, nid, list, HDA_MAX_CONNECTIONS);
370 if (len < 0) 393 if (len < 0)
371 return len; 394 return len;
372 err = snd_hda_override_conn_list(codec, nid, len, list);
373 if (err < 0)
374 return err;
375 added = true; 395 added = true;
376 goto again; 396 goto again;
377} 397}
378EXPORT_SYMBOL_HDA(snd_hda_get_conn_list);
379
380/**
381 * snd_hda_get_connections - copy connection list
382 * @codec: the HDA codec
383 * @nid: NID to parse
384 * @conn_list: connection list array
385 * @max_conns: max. number of connections to store
386 *
387 * Parses the connection list of the given widget and stores the list
388 * of NIDs.
389 *
390 * Returns the number of connections, or a negative error code.
391 */
392int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
393 hda_nid_t *conn_list, int max_conns)
394{
395 const hda_nid_t *list;
396 int len = snd_hda_get_conn_list(codec, nid, &list);
397
398 if (len <= 0)
399 return len;
400 if (len > max_conns) {
401 snd_printk(KERN_ERR "hda_codec: "
402 "Too many connections %d for NID 0x%x\n",
403 len, nid);
404 return -EINVAL;
405 }
406 memcpy(conn_list, list, len * sizeof(hda_nid_t));
407 return len;
408}
409EXPORT_SYMBOL_HDA(snd_hda_get_connections); 398EXPORT_SYMBOL_HDA(snd_hda_get_connections);
410 399
411/** 400/**
@@ -543,6 +532,7 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
543 hda_nid_t *p; 532 hda_nid_t *p;
544 int i, old_used; 533 int i, old_used;
545 534
535 mutex_lock(&codec->hash_mutex);
546 p = lookup_conn_list(array, nid); 536 p = lookup_conn_list(array, nid);
547 if (p) 537 if (p)
548 *p = -1; /* invalidate the old entry */ 538 *p = -1; /* invalidate the old entry */
@@ -553,10 +543,12 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int len,
553 for (i = 0; i < len; i++) 543 for (i = 0; i < len; i++)
554 if (!add_conn_list(array, list[i])) 544 if (!add_conn_list(array, list[i]))
555 goto error_add; 545 goto error_add;
546 mutex_unlock(&codec->hash_mutex);
556 return 0; 547 return 0;
557 548
558 error_add: 549 error_add:
559 array->used = old_used; 550 array->used = old_used;
551 mutex_unlock(&codec->hash_mutex);
560 return -ENOMEM; 552 return -ENOMEM;
561} 553}
562EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); 554EXPORT_SYMBOL_HDA(snd_hda_override_conn_list);
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index 29a311b05f2d..54b52819fb47 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -911,10 +911,13 @@ int snd_hda_get_sub_nodes(struct hda_codec *codec, hda_nid_t nid,
911 hda_nid_t *start_id); 911 hda_nid_t *start_id);
912int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, 912int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid,
913 hda_nid_t *conn_list, int max_conns); 913 hda_nid_t *conn_list, int max_conns);
914static inline int
915snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid)
916{
917 return snd_hda_get_connections(codec, nid, NULL, 0);
918}
914int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, 919int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid,
915 hda_nid_t *conn_list, int max_conns); 920 hda_nid_t *conn_list, int max_conns);
916int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid,
917 const hda_nid_t **listp);
918int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, 921int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
919 const hda_nid_t *list); 922 const hda_nid_t *list);
920int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, 923int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 152f458afd2b..0d68bb0ff376 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -349,7 +349,7 @@ static int alc_mux_select(struct hda_codec *codec, unsigned int adc_idx,
349 nid = get_capsrc(spec, adc_idx); 349 nid = get_capsrc(spec, adc_idx);
350 350
351 /* no selection? */ 351 /* no selection? */
352 num_conns = snd_hda_get_conn_list(codec, nid, NULL); 352 num_conns = snd_hda_get_num_conns(codec, nid);
353 if (num_conns <= 1) 353 if (num_conns <= 1)
354 return 1; 354 return 1;
355 355
@@ -2543,7 +2543,6 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
2543 nid = codec->start_nid; 2543 nid = codec->start_nid;
2544 for (i = 0; i < codec->num_nodes; i++, nid++) { 2544 for (i = 0; i < codec->num_nodes; i++, nid++) {
2545 hda_nid_t src; 2545 hda_nid_t src;
2546 const hda_nid_t *list;
2547 unsigned int caps = get_wcaps(codec, nid); 2546 unsigned int caps = get_wcaps(codec, nid);
2548 int type = get_wcaps_type(caps); 2547 int type = get_wcaps_type(caps);
2549 2548
@@ -2554,6 +2553,7 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
2554 src = nid; 2553 src = nid;
2555 for (;;) { 2554 for (;;) {
2556 int n; 2555 int n;
2556 hda_nid_t conn_nid;
2557 type = get_wcaps_type(get_wcaps(codec, src)); 2557 type = get_wcaps_type(get_wcaps(codec, src));
2558 if (type == AC_WID_PIN) 2558 if (type == AC_WID_PIN)
2559 break; 2559 break;
@@ -2561,13 +2561,14 @@ static int alc_auto_fill_adc_caps(struct hda_codec *codec)
2561 cap_nids[nums] = src; 2561 cap_nids[nums] = src;
2562 break; 2562 break;
2563 } 2563 }
2564 n = snd_hda_get_conn_list(codec, src, &list); 2564 n = snd_hda_get_num_conns(codec, src);
2565 if (n > 1) { 2565 if (n > 1) {
2566 cap_nids[nums] = src; 2566 cap_nids[nums] = src;
2567 break; 2567 break;
2568 } else if (n != 1) 2568 } else if (n != 1)
2569 break; 2569 break;
2570 src = *list; 2570 if (snd_hda_get_connections(codec, src, &src, 1) != 1)
2571 break;
2571 } 2572 }
2572 if (++nums >= max_nums) 2573 if (++nums >= max_nums)
2573 break; 2574 break;
@@ -2708,7 +2709,7 @@ static void alc_auto_init_analog_input(struct hda_codec *codec)
2708 2709
2709 /* mute all loopback inputs */ 2710 /* mute all loopback inputs */
2710 if (spec->mixer_nid) { 2711 if (spec->mixer_nid) {
2711 int nums = snd_hda_get_conn_list(codec, spec->mixer_nid, NULL); 2712 int nums = snd_hda_get_num_conns(codec, spec->mixer_nid);
2712 for (i = 0; i < nums; i++) 2713 for (i = 0; i < nums; i++)
2713 snd_hda_codec_write(codec, spec->mixer_nid, 0, 2714 snd_hda_codec_write(codec, spec->mixer_nid, 0,
2714 AC_VERB_SET_AMP_GAIN_MUTE, 2715 AC_VERB_SET_AMP_GAIN_MUTE,
@@ -3338,7 +3339,7 @@ static int alc_auto_add_sw_ctl(struct hda_codec *codec,
3338 if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) { 3339 if (wid_type == AC_WID_PIN || wid_type == AC_WID_AUD_OUT) {
3339 type = ALC_CTL_WIDGET_MUTE; 3340 type = ALC_CTL_WIDGET_MUTE;
3340 val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT); 3341 val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_OUTPUT);
3341 } else if (snd_hda_get_conn_list(codec, nid, NULL) == 1) { 3342 } else if (snd_hda_get_num_conns(codec, nid) == 1) {
3342 type = ALC_CTL_WIDGET_MUTE; 3343 type = ALC_CTL_WIDGET_MUTE;
3343 val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT); 3344 val = HDA_COMPOSE_AMP_VAL(nid, chs, 0, HDA_INPUT);
3344 } else { 3345 } else {
@@ -3898,7 +3899,7 @@ static void alc_remove_invalid_adc_nids(struct hda_codec *codec)
3898 nums = 0; 3899 nums = 0;
3899 for (n = 0; n < spec->num_adc_nids; n++) { 3900 for (n = 0; n < spec->num_adc_nids; n++) {
3900 hda_nid_t cap = spec->private_capsrc_nids[n]; 3901 hda_nid_t cap = spec->private_capsrc_nids[n];
3901 int num_conns = snd_hda_get_conn_list(codec, cap, NULL); 3902 int num_conns = snd_hda_get_num_conns(codec, cap);
3902 for (i = 0; i < imux->num_items; i++) { 3903 for (i = 0; i < imux->num_items; i++) {
3903 hda_nid_t pin = spec->imux_pins[i]; 3904 hda_nid_t pin = spec->imux_pins[i];
3904 if (pin) { 3905 if (pin) {
@@ -4027,7 +4028,7 @@ static void select_or_unmute_capsrc(struct hda_codec *codec, hda_nid_t cap,
4027 if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) { 4028 if (get_wcaps_type(get_wcaps(codec, cap)) == AC_WID_AUD_MIX) {
4028 snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx, 4029 snd_hda_codec_amp_stereo(codec, cap, HDA_INPUT, idx,
4029 HDA_AMP_MUTE, 0); 4030 HDA_AMP_MUTE, 0);
4030 } else if (snd_hda_get_conn_list(codec, cap, NULL) > 1) { 4031 } else if (snd_hda_get_num_conns(codec, cap) > 1) {
4031 snd_hda_codec_write_cache(codec, cap, 0, 4032 snd_hda_codec_write_cache(codec, cap, 0,
4032 AC_VERB_SET_CONNECT_SEL, idx); 4033 AC_VERB_SET_CONNECT_SEL, idx);
4033 } 4034 }
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index db272fb5e579..82b368068e08 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -485,7 +485,7 @@ static void activate_output_mix(struct hda_codec *codec, struct nid_path *path,
485 485
486 if (!path) 486 if (!path)
487 return; 487 return;
488 num = snd_hda_get_conn_list(codec, mix_nid, NULL); 488 num = snd_hda_get_num_conns(codec, mix_nid);
489 for (i = 0; i < num; i++) { 489 for (i = 0; i < num; i++) {
490 if (i == idx) 490 if (i == idx)
491 val = AMP_IN_UNMUTE(i); 491 val = AMP_IN_UNMUTE(i);