From 1804498123fca1a7f370534b94c479b411b7aa8a Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 22 Apr 2025 09:43:48 +0200 Subject: [PATCH] 6.12-stable patches 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 --- ...-uaf-while-iterating-all_mddevs-list.patch | 131 ++++++++ ...si-wmi-platform-rename-data-variable.patch | 51 +++ ...tform-workaround-a-acpi-firmware-bug.patch | 302 ++++++++++++++++++ queue-6.12/series | 3 + 4 files changed, 487 insertions(+) create mode 100644 queue-6.12/md-fix-mddev-uaf-while-iterating-all_mddevs-list.patch create mode 100644 queue-6.12/platform-x86-msi-wmi-platform-rename-data-variable.patch create mode 100644 queue-6.12/platform-x86-msi-wmi-platform-workaround-a-acpi-firmware-bug.patch 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 index 0000000000..f79e5e3df6 --- /dev/null +++ b/queue-6.12/md-fix-mddev-uaf-while-iterating-all_mddevs-list.patch @@ -0,0 +1,131 @@ +From 8542870237c3a48ff049b6c5df5f50c8728284fa Mon Sep 17 00:00:00 2001 +From: Yu Kuai +Date: Thu, 20 Feb 2025 20:43:48 +0800 +Subject: md: fix mddev uaf while iterating all_mddevs list + +From: Yu Kuai + +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 +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 +Closes: https://lore.kernel.org/all/Z7Y0SURoA8xwg7vn@bender.morinfr.org/ +Signed-off-by: Yu Kuai +Reviewed-by: Christoph Hellwig +Cc: Salvatore Bonaccorso +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..dd510ce6cf --- /dev/null +++ b/queue-6.12/platform-x86-msi-wmi-platform-rename-data-variable.patch @@ -0,0 +1,51 @@ +From 912d614ac99e137fd2016777e4b090c46ce84898 Mon Sep 17 00:00:00 2001 +From: Armin Wolf +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 + +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 +Link: https://lore.kernel.org/r/20250414140453.7691-1-W_Armin@gmx.de +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..ff13b13644 --- /dev/null +++ b/queue-6.12/platform-x86-msi-wmi-platform-workaround-a-acpi-firmware-bug.patch @@ -0,0 +1,302 @@ +From baf2f2c2b4c8e1d398173acd4d2fa9131a86b84e Mon Sep 17 00:00:00 2001 +From: Armin Wolf +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 + +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 +Signed-off-by: Armin Wolf +Link: https://lore.kernel.org/r/20250414140453.7691-2-W_Armin@gmx.de +Reviewed-by: Ilpo Järvinen +Signed-off-by: Ilpo Järvinen +Signed-off-by: Greg Kroah-Hartman +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -17,6 +18,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -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[] = { diff --git a/queue-6.12/series b/queue-6.12/series index f2516db0b8..1822ce9fcb 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -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 -- 2.47.3