]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.12-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Apr 2025 07:43:48 +0000 (09:43 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 22 Apr 2025 07:43:48 +0000 (09:43 +0200)
added patches:
md-fix-mddev-uaf-while-iterating-all_mddevs-list.patch
platform-x86-msi-wmi-platform-rename-data-variable.patch
platform-x86-msi-wmi-platform-workaround-a-acpi-firmware-bug.patch

queue-6.12/md-fix-mddev-uaf-while-iterating-all_mddevs-list.patch [new file with mode: 0644]
queue-6.12/platform-x86-msi-wmi-platform-rename-data-variable.patch [new file with mode: 0644]
queue-6.12/platform-x86-msi-wmi-platform-workaround-a-acpi-firmware-bug.patch [new file with mode: 0644]
queue-6.12/series

diff --git a/queue-6.12/md-fix-mddev-uaf-while-iterating-all_mddevs-list.patch b/queue-6.12/md-fix-mddev-uaf-while-iterating-all_mddevs-list.patch
new file mode 100644 (file)
index 0000000..f79e5e3
--- /dev/null
@@ -0,0 +1,131 @@
+From 8542870237c3a48ff049b6c5df5f50c8728284fa Mon Sep 17 00:00:00 2001
+From: Yu Kuai <yukuai3@huawei.com>
+Date: Thu, 20 Feb 2025 20:43:48 +0800
+Subject: md: fix mddev uaf while iterating all_mddevs list
+
+From: Yu Kuai <yukuai3@huawei.com>
+
+commit 8542870237c3a48ff049b6c5df5f50c8728284fa upstream.
+
+While iterating all_mddevs list from md_notify_reboot() and md_exit(),
+list_for_each_entry_safe is used, and this can race with deletint the
+next mddev, causing UAF:
+
+t1:
+spin_lock
+//list_for_each_entry_safe(mddev, n, ...)
+ mddev_get(mddev1)
+ // assume mddev2 is the next entry
+ spin_unlock
+            t2:
+            //remove mddev2
+            ...
+            mddev_free
+            spin_lock
+            list_del
+            spin_unlock
+            kfree(mddev2)
+ mddev_put(mddev1)
+ spin_lock
+ //continue dereference mddev2->all_mddevs
+
+The old helper for_each_mddev() actually grab the reference of mddev2
+while holding the lock, to prevent from being freed. This problem can be
+fixed the same way, however, the code will be complex.
+
+Hence switch to use list_for_each_entry, in this case mddev_put() can free
+the mddev1 and it's not safe as well. Refer to md_seq_show(), also factor
+out a helper mddev_put_locked() to fix this problem.
+
+Cc: Christoph Hellwig <hch@lst.de>
+Link: https://lore.kernel.org/linux-raid/20250220124348.845222-1-yukuai1@huaweicloud.com
+Fixes: f26514342255 ("md: stop using for_each_mddev in md_notify_reboot")
+Fixes: 16648bac862f ("md: stop using for_each_mddev in md_exit")
+Reported-and-tested-by: Guillaume Morin <guillaume@morinfr.org>
+Closes: https://lore.kernel.org/all/Z7Y0SURoA8xwg7vn@bender.morinfr.org/
+Signed-off-by: Yu Kuai <yukuai3@huawei.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+Cc: Salvatore Bonaccorso <carnil@debian.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/md/md.c |   22 +++++++++++++---------
+ 1 file changed, 13 insertions(+), 9 deletions(-)
+
+--- a/drivers/md/md.c
++++ b/drivers/md/md.c
+@@ -629,6 +629,12 @@ static void __mddev_put(struct mddev *md
+       queue_work(md_misc_wq, &mddev->del_work);
+ }
++static void mddev_put_locked(struct mddev *mddev)
++{
++      if (atomic_dec_and_test(&mddev->active))
++              __mddev_put(mddev);
++}
++
+ void mddev_put(struct mddev *mddev)
+ {
+       if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock))
+@@ -8461,9 +8467,7 @@ static int md_seq_show(struct seq_file *
+       if (mddev == list_last_entry(&all_mddevs, struct mddev, all_mddevs))
+               status_unused(seq);
+-      if (atomic_dec_and_test(&mddev->active))
+-              __mddev_put(mddev);
+-
++      mddev_put_locked(mddev);
+       return 0;
+ }
+@@ -9886,11 +9890,11 @@ EXPORT_SYMBOL_GPL(rdev_clear_badblocks);
+ static int md_notify_reboot(struct notifier_block *this,
+                           unsigned long code, void *x)
+ {
+-      struct mddev *mddev, *n;
++      struct mddev *mddev;
+       int need_delay = 0;
+       spin_lock(&all_mddevs_lock);
+-      list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
++      list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
+               if (!mddev_get(mddev))
+                       continue;
+               spin_unlock(&all_mddevs_lock);
+@@ -9902,8 +9906,8 @@ static int md_notify_reboot(struct notif
+                       mddev_unlock(mddev);
+               }
+               need_delay = 1;
+-              mddev_put(mddev);
+               spin_lock(&all_mddevs_lock);
++              mddev_put_locked(mddev);
+       }
+       spin_unlock(&all_mddevs_lock);
+@@ -10236,7 +10240,7 @@ void md_autostart_arrays(int part)
+ static __exit void md_exit(void)
+ {
+-      struct mddev *mddev, *n;
++      struct mddev *mddev;
+       int delay = 1;
+       unregister_blkdev(MD_MAJOR,"md");
+@@ -10257,7 +10261,7 @@ static __exit void md_exit(void)
+       remove_proc_entry("mdstat", NULL);
+       spin_lock(&all_mddevs_lock);
+-      list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) {
++      list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
+               if (!mddev_get(mddev))
+                       continue;
+               spin_unlock(&all_mddevs_lock);
+@@ -10269,8 +10273,8 @@ static __exit void md_exit(void)
+                * the mddev for destruction by a workqueue, and the
+                * destroy_workqueue() below will wait for that to complete.
+                */
+-              mddev_put(mddev);
+               spin_lock(&all_mddevs_lock);
++              mddev_put_locked(mddev);
+       }
+       spin_unlock(&all_mddevs_lock);
diff --git a/queue-6.12/platform-x86-msi-wmi-platform-rename-data-variable.patch b/queue-6.12/platform-x86-msi-wmi-platform-rename-data-variable.patch
new file mode 100644 (file)
index 0000000..dd510ce
--- /dev/null
@@ -0,0 +1,51 @@
+From 912d614ac99e137fd2016777e4b090c46ce84898 Mon Sep 17 00:00:00 2001
+From: Armin Wolf <W_Armin@gmx.de>
+Date: Mon, 14 Apr 2025 16:04:52 +0200
+Subject: platform/x86: msi-wmi-platform: Rename "data" variable
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Armin Wolf <W_Armin@gmx.de>
+
+commit 912d614ac99e137fd2016777e4b090c46ce84898 upstream.
+
+Rename the "data" variable inside msi_wmi_platform_read() to avoid
+a name collision when the driver adds support for a state container
+struct (that is to be called "data" too) in the future.
+
+Signed-off-by: Armin Wolf <W_Armin@gmx.de>
+Link: https://lore.kernel.org/r/20250414140453.7691-1-W_Armin@gmx.de
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/platform/x86/msi-wmi-platform.c |    8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/platform/x86/msi-wmi-platform.c
++++ b/drivers/platform/x86/msi-wmi-platform.c
+@@ -173,7 +173,7 @@ static int msi_wmi_platform_read(struct
+       struct wmi_device *wdev = dev_get_drvdata(dev);
+       u8 input[32] = { 0 };
+       u8 output[32];
+-      u16 data;
++      u16 value;
+       int ret;
+       ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_FAN, input, sizeof(input), output,
+@@ -181,11 +181,11 @@ static int msi_wmi_platform_read(struct
+       if (ret < 0)
+               return ret;
+-      data = get_unaligned_be16(&output[channel * 2 + 1]);
+-      if (!data)
++      value = get_unaligned_be16(&output[channel * 2 + 1]);
++      if (!value)
+               *val = 0;
+       else
+-              *val = 480000 / data;
++              *val = 480000 / value;
+       return 0;
+ }
diff --git a/queue-6.12/platform-x86-msi-wmi-platform-workaround-a-acpi-firmware-bug.patch b/queue-6.12/platform-x86-msi-wmi-platform-workaround-a-acpi-firmware-bug.patch
new file mode 100644 (file)
index 0000000..ff13b13
--- /dev/null
@@ -0,0 +1,302 @@
+From baf2f2c2b4c8e1d398173acd4d2fa9131a86b84e Mon Sep 17 00:00:00 2001
+From: Armin Wolf <W_Armin@gmx.de>
+Date: Mon, 14 Apr 2025 16:04:53 +0200
+Subject: platform/x86: msi-wmi-platform: Workaround a ACPI firmware bug
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Armin Wolf <W_Armin@gmx.de>
+
+commit baf2f2c2b4c8e1d398173acd4d2fa9131a86b84e upstream.
+
+The ACPI byte code inside the ACPI control method responsible for
+handling the WMI method calls uses a global buffer for constructing
+the return value, yet the ACPI control method itself is not marked
+as "Serialized".
+This means that calling WMI methods on this WMI device is not
+thread-safe, as concurrent WMI method calls will corrupt the global
+buffer.
+
+Fix this by serializing the WMI method calls using a mutex.
+
+Cc: stable@vger.kernel.org # 6.x.x: 912d614ac99e: platform/x86: msi-wmi-platform: Rename "data" variable
+Fixes: 9c0beb6b29e7 ("platform/x86: wmi: Add MSI WMI Platform driver")
+Tested-by: Antheas Kapenekakis <lkml@antheas.dev>
+Signed-off-by: Armin Wolf <W_Armin@gmx.de>
+Link: https://lore.kernel.org/r/20250414140453.7691-2-W_Armin@gmx.de
+Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ Documentation/wmi/devices/msi-wmi-platform.rst |    4 +
+ drivers/platform/x86/msi-wmi-platform.c        |   91 ++++++++++++++++---------
+ 2 files changed, 63 insertions(+), 32 deletions(-)
+
+--- a/Documentation/wmi/devices/msi-wmi-platform.rst
++++ b/Documentation/wmi/devices/msi-wmi-platform.rst
+@@ -138,6 +138,10 @@ input data, the meaning of which depends
+ The output buffer contains a single byte which signals success or failure (``0x00`` on failure)
+ and 31 bytes of output data, the meaning if which depends on the subfeature being accessed.
++.. note::
++   The ACPI control method responsible for handling the WMI method calls is not thread-safe.
++   This is a firmware bug that needs to be handled inside the driver itself.
++
+ WMI method Get_EC()
+ -------------------
+--- a/drivers/platform/x86/msi-wmi-platform.c
++++ b/drivers/platform/x86/msi-wmi-platform.c
+@@ -10,6 +10,7 @@
+ #include <linux/acpi.h>
+ #include <linux/bits.h>
+ #include <linux/bitfield.h>
++#include <linux/cleanup.h>
+ #include <linux/debugfs.h>
+ #include <linux/device.h>
+ #include <linux/device/driver.h>
+@@ -17,6 +18,7 @@
+ #include <linux/hwmon.h>
+ #include <linux/kernel.h>
+ #include <linux/module.h>
++#include <linux/mutex.h>
+ #include <linux/printk.h>
+ #include <linux/rwsem.h>
+ #include <linux/types.h>
+@@ -76,8 +78,13 @@ enum msi_wmi_platform_method {
+       MSI_PLATFORM_GET_WMI            = 0x1d,
+ };
+-struct msi_wmi_platform_debugfs_data {
++struct msi_wmi_platform_data {
+       struct wmi_device *wdev;
++      struct mutex wmi_lock;  /* Necessary when calling WMI methods */
++};
++
++struct msi_wmi_platform_debugfs_data {
++      struct msi_wmi_platform_data *data;
+       enum msi_wmi_platform_method method;
+       struct rw_semaphore buffer_lock;        /* Protects debugfs buffer */
+       size_t length;
+@@ -132,8 +139,9 @@ static int msi_wmi_platform_parse_buffer
+       return 0;
+ }
+-static int msi_wmi_platform_query(struct wmi_device *wdev, enum msi_wmi_platform_method method,
+-                                u8 *input, size_t input_length, u8 *output, size_t output_length)
++static int msi_wmi_platform_query(struct msi_wmi_platform_data *data,
++                                enum msi_wmi_platform_method method, u8 *input,
++                                size_t input_length, u8 *output, size_t output_length)
+ {
+       struct acpi_buffer out = { ACPI_ALLOCATE_BUFFER, NULL };
+       struct acpi_buffer in = {
+@@ -147,9 +155,15 @@ static int msi_wmi_platform_query(struct
+       if (!input_length || !output_length)
+               return -EINVAL;
+-      status = wmidev_evaluate_method(wdev, 0x0, method, &in, &out);
+-      if (ACPI_FAILURE(status))
+-              return -EIO;
++      /*
++       * The ACPI control method responsible for handling the WMI method calls
++       * is not thread-safe. Because of this we have to do the locking ourself.
++       */
++      scoped_guard(mutex, &data->wmi_lock) {
++              status = wmidev_evaluate_method(data->wdev, 0x0, method, &in, &out);
++              if (ACPI_FAILURE(status))
++                      return -EIO;
++      }
+       obj = out.pointer;
+       if (!obj)
+@@ -170,13 +184,13 @@ static umode_t msi_wmi_platform_is_visib
+ static int msi_wmi_platform_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+                                int channel, long *val)
+ {
+-      struct wmi_device *wdev = dev_get_drvdata(dev);
++      struct msi_wmi_platform_data *data = dev_get_drvdata(dev);
+       u8 input[32] = { 0 };
+       u8 output[32];
+       u16 value;
+       int ret;
+-      ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_FAN, input, sizeof(input), output,
++      ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_FAN, input, sizeof(input), output,
+                                    sizeof(output));
+       if (ret < 0)
+               return ret;
+@@ -231,7 +245,7 @@ static ssize_t msi_wmi_platform_write(st
+               return ret;
+       down_write(&data->buffer_lock);
+-      ret = msi_wmi_platform_query(data->wdev, data->method, payload, data->length, data->buffer,
++      ret = msi_wmi_platform_query(data->data, data->method, payload, data->length, data->buffer,
+                                    data->length);
+       up_write(&data->buffer_lock);
+@@ -277,17 +291,17 @@ static void msi_wmi_platform_debugfs_rem
+       debugfs_remove_recursive(dir);
+ }
+-static void msi_wmi_platform_debugfs_add(struct wmi_device *wdev, struct dentry *dir,
++static void msi_wmi_platform_debugfs_add(struct msi_wmi_platform_data *drvdata, struct dentry *dir,
+                                        const char *name, enum msi_wmi_platform_method method)
+ {
+       struct msi_wmi_platform_debugfs_data *data;
+       struct dentry *entry;
+-      data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
++      data = devm_kzalloc(&drvdata->wdev->dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return;
+-      data->wdev = wdev;
++      data->data = drvdata;
+       data->method = method;
+       init_rwsem(&data->buffer_lock);
+@@ -298,82 +312,82 @@ static void msi_wmi_platform_debugfs_add
+       entry = debugfs_create_file(name, 0600, dir, data, &msi_wmi_platform_debugfs_fops);
+       if (IS_ERR(entry))
+-              devm_kfree(&wdev->dev, data);
++              devm_kfree(&drvdata->wdev->dev, data);
+ }
+-static void msi_wmi_platform_debugfs_init(struct wmi_device *wdev)
++static void msi_wmi_platform_debugfs_init(struct msi_wmi_platform_data *data)
+ {
+       struct dentry *dir;
+       char dir_name[64];
+       int ret, method;
+-      scnprintf(dir_name, ARRAY_SIZE(dir_name), "%s-%s", DRIVER_NAME, dev_name(&wdev->dev));
++      scnprintf(dir_name, ARRAY_SIZE(dir_name), "%s-%s", DRIVER_NAME, dev_name(&data->wdev->dev));
+       dir = debugfs_create_dir(dir_name, NULL);
+       if (IS_ERR(dir))
+               return;
+-      ret = devm_add_action_or_reset(&wdev->dev, msi_wmi_platform_debugfs_remove, dir);
++      ret = devm_add_action_or_reset(&data->wdev->dev, msi_wmi_platform_debugfs_remove, dir);
+       if (ret < 0)
+               return;
+       for (method = MSI_PLATFORM_GET_PACKAGE; method <= MSI_PLATFORM_GET_WMI; method++)
+-              msi_wmi_platform_debugfs_add(wdev, dir, msi_wmi_platform_debugfs_names[method - 1],
++              msi_wmi_platform_debugfs_add(data, dir, msi_wmi_platform_debugfs_names[method - 1],
+                                            method);
+ }
+-static int msi_wmi_platform_hwmon_init(struct wmi_device *wdev)
++static int msi_wmi_platform_hwmon_init(struct msi_wmi_platform_data *data)
+ {
+       struct device *hdev;
+-      hdev = devm_hwmon_device_register_with_info(&wdev->dev, "msi_wmi_platform", wdev,
++      hdev = devm_hwmon_device_register_with_info(&data->wdev->dev, "msi_wmi_platform", data,
+                                                   &msi_wmi_platform_chip_info, NULL);
+       return PTR_ERR_OR_ZERO(hdev);
+ }
+-static int msi_wmi_platform_ec_init(struct wmi_device *wdev)
++static int msi_wmi_platform_ec_init(struct msi_wmi_platform_data *data)
+ {
+       u8 input[32] = { 0 };
+       u8 output[32];
+       u8 flags;
+       int ret;
+-      ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_EC, input, sizeof(input), output,
++      ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_EC, input, sizeof(input), output,
+                                    sizeof(output));
+       if (ret < 0)
+               return ret;
+       flags = output[MSI_PLATFORM_EC_FLAGS_OFFSET];
+-      dev_dbg(&wdev->dev, "EC RAM version %lu.%lu\n",
++      dev_dbg(&data->wdev->dev, "EC RAM version %lu.%lu\n",
+               FIELD_GET(MSI_PLATFORM_EC_MAJOR_MASK, flags),
+               FIELD_GET(MSI_PLATFORM_EC_MINOR_MASK, flags));
+-      dev_dbg(&wdev->dev, "EC firmware version %.28s\n",
++      dev_dbg(&data->wdev->dev, "EC firmware version %.28s\n",
+               &output[MSI_PLATFORM_EC_VERSION_OFFSET]);
+       if (!(flags & MSI_PLATFORM_EC_IS_TIGERLAKE)) {
+               if (!force)
+                       return -ENODEV;
+-              dev_warn(&wdev->dev, "Loading on a non-Tigerlake platform\n");
++              dev_warn(&data->wdev->dev, "Loading on a non-Tigerlake platform\n");
+       }
+       return 0;
+ }
+-static int msi_wmi_platform_init(struct wmi_device *wdev)
++static int msi_wmi_platform_init(struct msi_wmi_platform_data *data)
+ {
+       u8 input[32] = { 0 };
+       u8 output[32];
+       int ret;
+-      ret = msi_wmi_platform_query(wdev, MSI_PLATFORM_GET_WMI, input, sizeof(input), output,
++      ret = msi_wmi_platform_query(data, MSI_PLATFORM_GET_WMI, input, sizeof(input), output,
+                                    sizeof(output));
+       if (ret < 0)
+               return ret;
+-      dev_dbg(&wdev->dev, "WMI interface version %u.%u\n",
++      dev_dbg(&data->wdev->dev, "WMI interface version %u.%u\n",
+               output[MSI_PLATFORM_WMI_MAJOR_OFFSET],
+               output[MSI_PLATFORM_WMI_MINOR_OFFSET]);
+@@ -381,7 +395,8 @@ static int msi_wmi_platform_init(struct
+               if (!force)
+                       return -ENODEV;
+-              dev_warn(&wdev->dev, "Loading despite unsupported WMI interface version (%u.%u)\n",
++              dev_warn(&data->wdev->dev,
++                       "Loading despite unsupported WMI interface version (%u.%u)\n",
+                        output[MSI_PLATFORM_WMI_MAJOR_OFFSET],
+                        output[MSI_PLATFORM_WMI_MINOR_OFFSET]);
+       }
+@@ -391,19 +406,31 @@ static int msi_wmi_platform_init(struct
+ static int msi_wmi_platform_probe(struct wmi_device *wdev, const void *context)
+ {
++      struct msi_wmi_platform_data *data;
+       int ret;
+-      ret = msi_wmi_platform_init(wdev);
++      data = devm_kzalloc(&wdev->dev, sizeof(*data), GFP_KERNEL);
++      if (!data)
++              return -ENOMEM;
++
++      data->wdev = wdev;
++      dev_set_drvdata(&wdev->dev, data);
++
++      ret = devm_mutex_init(&wdev->dev, &data->wmi_lock);
++      if (ret < 0)
++              return ret;
++
++      ret = msi_wmi_platform_init(data);
+       if (ret < 0)
+               return ret;
+-      ret = msi_wmi_platform_ec_init(wdev);
++      ret = msi_wmi_platform_ec_init(data);
+       if (ret < 0)
+               return ret;
+-      msi_wmi_platform_debugfs_init(wdev);
++      msi_wmi_platform_debugfs_init(data);
+-      return msi_wmi_platform_hwmon_init(wdev);
++      return msi_wmi_platform_hwmon_init(data);
+ }
+ static const struct wmi_device_id msi_wmi_platform_id_table[] = {
index f2516db0b855a9e4337b678ec28fd34295611162..1822ce9fcb93274b580c94aa175b199ed0a6edcd 100644 (file)
@@ -193,3 +193,6 @@ arm64-boot-enable-el2-requirements-for-feat_pmuv3p9.patch
 cpufreq-reference-count-policy-in-cpufreq_update_limits.patch
 scripts-generate_rust_analyzer-add-ffi-crate.patch
 kbuild-add-fno-builtin-wcslen.patch
+platform-x86-msi-wmi-platform-rename-data-variable.patch
+platform-x86-msi-wmi-platform-workaround-a-acpi-firmware-bug.patch
+md-fix-mddev-uaf-while-iterating-all_mddevs-list.patch