]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.13-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Oct 2017 11:59:32 +0000 (13:59 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 24 Oct 2017 11:59:32 +0000 (13:59 +0200)
added patches:
alsa-hda-fix-incorrect-tlv-callback-check-introduced-during-set_fs-removal.patch

queue-4.13/alsa-hda-fix-incorrect-tlv-callback-check-introduced-during-set_fs-removal.patch [new file with mode: 0644]
queue-4.13/series

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 (file)
index 0000000..eb7b086
--- /dev/null
@@ -0,0 +1,226 @@
+From a91d66129fb9bcead12af3ed2008d6ddbf179509 Mon Sep 17 00:00:00 2001
+From: Takashi Iwai <tiwai@suse.de>
+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 <tiwai@suse.de>
+
+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 <pageexec@freemail.hu>
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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)
index 3863222ee0b73060b25a1fb198d503e0057aaf6f..1e2f3a18b3011134f53fbd3d01faff9b2beff301 100644 (file)
@@ -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