From: Greg Kroah-Hartman Date: Tue, 24 Oct 2017 11:59:32 +0000 (+0200) Subject: 4.13-stable patches X-Git-Tag: v3.18.78~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d8624b4a75d8d4edff040a58eb484af57babb710;p=thirdparty%2Fkernel%2Fstable-queue.git 4.13-stable patches added patches: alsa-hda-fix-incorrect-tlv-callback-check-introduced-during-set_fs-removal.patch --- diff --git a/queue-4.13/alsa-hda-fix-incorrect-tlv-callback-check-introduced-during-set_fs-removal.patch b/queue-4.13/alsa-hda-fix-incorrect-tlv-callback-check-introduced-during-set_fs-removal.patch new file mode 100644 index 00000000000..eb7b0865fbf --- /dev/null +++ b/queue-4.13/alsa-hda-fix-incorrect-tlv-callback-check-introduced-during-set_fs-removal.patch @@ -0,0 +1,226 @@ +From a91d66129fb9bcead12af3ed2008d6ddbf179509 Mon Sep 17 00:00:00 2001 +From: Takashi Iwai +Date: Mon, 16 Oct 2017 11:39:28 +0200 +Subject: ALSA: hda - Fix incorrect TLV callback check introduced during set_fs() removal + +From: Takashi Iwai + +commit a91d66129fb9bcead12af3ed2008d6ddbf179509 upstream. + +The commit 99b5c5bb9a54 ("ALSA: hda - Remove the use of set_fs()") +converted the get_kctl_0dB_offset() call for killing set_fs() usage in +HD-audio codec code. The conversion assumed that the TLV callback +used in HD-audio code is only snd_hda_mixer_amp() and applies the TLV +calculation locally. + +Although this assumption is correct, and all slave kctls are actually +with that callback, the current code is still utterly buggy; it +doesn't hit this condition and falls back to the next check. It's +because the function gets called after adding slave kctls to vmaster. +By assigning a slave kctl, the slave kctl object is faked inside +vmaster code, and the whole kctl ops are overridden. Thus the +callback op points to a different value from what we've assumed. + +More badly, as reported by the KERNEXEC and UDEREF features of PaX, +the code flow turns into the unexpected pitfall. The next fallback +check is SNDRV_CTL_ELEM_ACCESS_TLV_READ access bit, and this always +hits for each kctl with TLV. Then it evaluates the callback function +pointer wrongly as if it were a TLV array. Although currently its +side-effect is fairly limited, this incorrect reference may lead to an +unpleasant result. + +For addressing the regression, this patch introduces a new helper to +vmaster code, snd_ctl_apply_vmaster_slaves(). This works similarly +like the existing map_slaves() in hda_codec.c: it loops over the slave +list of the given master, and applies the given function to each +slave. Then the initializer function receives the right kctl object +and we can compare the correct pointer instead of the faked one. + +Also, for catching the similar breakage in future, give an error +message when the unexpected TLV callback is found and bail out +immediately. + +Fixes: 99b5c5bb9a54 ("ALSA: hda - Remove the use of set_fs()") +Reported-by: PaX Team +Signed-off-by: Takashi Iwai +Signed-off-by: Greg Kroah-Hartman + +--- + include/sound/control.h | 3 + + sound/core/vmaster.c | 31 ++++++++++++++ + sound/pci/hda/hda_codec.c | 97 ++++++++++++++++++++++++++-------------------- + 3 files changed, 89 insertions(+), 42 deletions(-) + +--- a/include/sound/control.h ++++ b/include/sound/control.h +@@ -248,6 +248,9 @@ int snd_ctl_add_vmaster_hook(struct snd_ + void *private_data); + void snd_ctl_sync_vmaster(struct snd_kcontrol *kctl, bool hook_only); + #define snd_ctl_sync_vmaster_hook(kctl) snd_ctl_sync_vmaster(kctl, true) ++int snd_ctl_apply_vmaster_slaves(struct snd_kcontrol *kctl, ++ int (*func)(struct snd_kcontrol *, void *), ++ void *arg); + + /* + * Helper functions for jack-detection controls +--- a/sound/core/vmaster.c ++++ b/sound/core/vmaster.c +@@ -484,3 +484,34 @@ void snd_ctl_sync_vmaster(struct snd_kco + master->hook(master->hook_private_data, master->val); + } + EXPORT_SYMBOL_GPL(snd_ctl_sync_vmaster); ++ ++/** ++ * snd_ctl_apply_vmaster_slaves - Apply function to each vmaster slave ++ * @kctl: vmaster kctl element ++ * @func: function to apply ++ * @arg: optional function argument ++ * ++ * Apply the function @func to each slave kctl of the given vmaster kctl. ++ * Returns 0 if successful, or a negative error code. ++ */ ++int snd_ctl_apply_vmaster_slaves(struct snd_kcontrol *kctl, ++ int (*func)(struct snd_kcontrol *, void *), ++ void *arg) ++{ ++ struct link_master *master; ++ struct link_slave *slave; ++ int err; ++ ++ master = snd_kcontrol_chip(kctl); ++ err = master_init(master); ++ if (err < 0) ++ return err; ++ list_for_each_entry(slave, &master->slaves, list) { ++ err = func(&slave->slave, arg); ++ if (err < 0) ++ return err; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(snd_ctl_apply_vmaster_slaves); +--- a/sound/pci/hda/hda_codec.c ++++ b/sound/pci/hda/hda_codec.c +@@ -1803,36 +1803,6 @@ static int check_slave_present(struct hd + return 1; + } + +-/* guess the value corresponding to 0dB */ +-static int get_kctl_0dB_offset(struct hda_codec *codec, +- struct snd_kcontrol *kctl, int *step_to_check) +-{ +- int _tlv[4]; +- const int *tlv = NULL; +- int val = -1; +- +- if ((kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) && +- kctl->tlv.c == snd_hda_mixer_amp_tlv) { +- get_ctl_amp_tlv(kctl, _tlv); +- tlv = _tlv; +- } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) +- tlv = kctl->tlv.p; +- if (tlv && tlv[0] == SNDRV_CTL_TLVT_DB_SCALE) { +- int step = tlv[3]; +- step &= ~TLV_DB_SCALE_MUTE; +- if (!step) +- return -1; +- if (*step_to_check && *step_to_check != step) { +- codec_err(codec, "Mismatching dB step for vmaster slave (%d!=%d)\n", +- *step_to_check, step); +- return -1; +- } +- *step_to_check = step; +- val = -tlv[2] / step; +- } +- return val; +-} +- + /* call kctl->put with the given value(s) */ + static int put_kctl_with_value(struct snd_kcontrol *kctl, int val) + { +@@ -1847,19 +1817,58 @@ static int put_kctl_with_value(struct sn + return 0; + } + +-/* initialize the slave volume with 0dB */ +-static int init_slave_0dB(struct hda_codec *codec, +- void *data, struct snd_kcontrol *slave) ++struct slave_init_arg { ++ struct hda_codec *codec; ++ int step; ++}; ++ ++/* initialize the slave volume with 0dB via snd_ctl_apply_vmaster_slaves() */ ++static int init_slave_0dB(struct snd_kcontrol *kctl, void *_arg) + { +- int offset = get_kctl_0dB_offset(codec, slave, data); +- if (offset > 0) +- put_kctl_with_value(slave, offset); ++ struct slave_init_arg *arg = _arg; ++ int _tlv[4]; ++ const int *tlv = NULL; ++ int step; ++ int val; ++ ++ if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) { ++ if (kctl->tlv.c != snd_hda_mixer_amp_tlv) { ++ codec_err(arg->codec, ++ "Unexpected TLV callback for slave %s:%d\n", ++ kctl->id.name, kctl->id.index); ++ return 0; /* ignore */ ++ } ++ get_ctl_amp_tlv(kctl, _tlv); ++ tlv = _tlv; ++ } else if (kctl->vd[0].access & SNDRV_CTL_ELEM_ACCESS_TLV_READ) ++ tlv = kctl->tlv.p; ++ ++ if (!tlv || tlv[0] != SNDRV_CTL_TLVT_DB_SCALE) ++ return 0; ++ ++ step = tlv[3]; ++ step &= ~TLV_DB_SCALE_MUTE; ++ if (!step) ++ return 0; ++ if (arg->step && arg->step != step) { ++ codec_err(arg->codec, ++ "Mismatching dB step for vmaster slave (%d!=%d)\n", ++ arg->step, step); ++ return 0; ++ } ++ ++ arg->step = step; ++ val = -tlv[2] / step; ++ if (val > 0) { ++ put_kctl_with_value(kctl, val); ++ return val; ++ } ++ + return 0; + } + +-/* unmute the slave */ +-static int init_slave_unmute(struct hda_codec *codec, +- void *data, struct snd_kcontrol *slave) ++/* unmute the slave via snd_ctl_apply_vmaster_slaves() */ ++static int init_slave_unmute(struct snd_kcontrol *slave, void *_arg) + { + return put_kctl_with_value(slave, 1); + } +@@ -1919,9 +1928,13 @@ int __snd_hda_add_vmaster(struct hda_cod + /* init with master mute & zero volume */ + put_kctl_with_value(kctl, 0); + if (init_slave_vol) { +- int step = 0; +- map_slaves(codec, slaves, suffix, +- tlv ? init_slave_0dB : init_slave_unmute, &step); ++ struct slave_init_arg arg = { ++ .codec = codec, ++ .step = 0, ++ }; ++ snd_ctl_apply_vmaster_slaves(kctl, ++ tlv ? init_slave_0dB : init_slave_unmute, ++ &arg); + } + + if (ctl_ret) diff --git a/queue-4.13/series b/queue-4.13/series index 3863222ee0b..1e2f3a18b30 100644 --- a/queue-4.13/series +++ b/queue-4.13/series @@ -64,3 +64,4 @@ fs-cache-fix-dereference-of-null-user_key_payload.patch keys-don-t-let-add_key-update-an-uninstantiated-key.patch pkcs7-prevent-null-pointer-dereference-since-sinfo-is-not-always-set.patch arm64-dts-rockchip-correct-vqmmc-voltage-for-rk3399-platforms.patch +alsa-hda-fix-incorrect-tlv-callback-check-introduced-during-set_fs-removal.patch