diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-05-19 11:21:25 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-05-19 11:25:23 -0400 |
commit | 09cf03b80c593b08e8b630a145e14f485200b5af (patch) | |
tree | 21a5d5b26f68bf3a4076c76e29d9eb3dc47e1bc5 /sound/pci/hda | |
parent | c882246d840073a3dd0533ca164dfcbd7f6bd207 (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.c | 82 | ||||
-rw-r--r-- | sound/pci/hda/hda_codec.h | 7 | ||||
-rw-r--r-- | sound/pci/hda/patch_realtek.c | 17 | ||||
-rw-r--r-- | sound/pci/hda/patch_via.c | 2 |
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 */ | ||
338 | static 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 | */ |
348 | int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, | 361 | int 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 | } |
378 | EXPORT_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 | */ | ||
392 | int 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 | } | ||
409 | EXPORT_SYMBOL_HDA(snd_hda_get_connections); | 398 | EXPORT_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 | } |
562 | EXPORT_SYMBOL_HDA(snd_hda_override_conn_list); | 554 | EXPORT_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); |
912 | int snd_hda_get_connections(struct hda_codec *codec, hda_nid_t nid, | 912 | int 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); |
914 | static inline int | ||
915 | snd_hda_get_num_conns(struct hda_codec *codec, hda_nid_t nid) | ||
916 | { | ||
917 | return snd_hda_get_connections(codec, nid, NULL, 0); | ||
918 | } | ||
914 | int snd_hda_get_raw_connections(struct hda_codec *codec, hda_nid_t nid, | 919 | int 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); |
916 | int snd_hda_get_conn_list(struct hda_codec *codec, hda_nid_t nid, | ||
917 | const hda_nid_t **listp); | ||
918 | int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums, | 921 | int 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); |
920 | int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, | 923 | int 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); |