linux-gbmc: peci: Systemic fixes for UAF and memory corruption Consolidates the following fixes into a single patch: - peci: core: Check xfer and refcount in peci_command - peci: core: Relocate IDR removal to release callback - peci: npcm: Fix use-after-free and ordering in teardown - peci: dev: Fix use-after-free on adapter detach - peci: core: Fix sysfs new_device race condition Tested: Survived 60x unbind/bind iterations on a machine. Fusion-Link: https://fusion2.corp.google.com/d7994c55-f939-35a7-b32f-0c54d54e61dc (platform11) Fusion-Link: https://fusion2.corp.google.com/165543ab-bc9b-3723-b4f2-9ceeded9c6b8 (platform11-emr) Fusion-Link: https://fusion2.corp.google.com/cc558fb5-4d87-3874-bad0-3b11c1c29715 (platform15) Fusion-Link: https://fusion2.corp.google.com/58873016-06b4-3059-bd4f-91b1875591de (platform17) Fusion-Link: https://fusion2.corp.google.com/61cccd02-779d-3b8a-9910-551ab3a2122b (platform5) Platforms-Affected: peci platforms Google-Bug-Id: 498991349 Change-Id: I9fe6305b83730cb7f33aa1aa1f5b2396268e375e Signed-off-by: William A. Kennington III <wak@google.com> (cherry picked from commit c230fa15710917237f26a6e16be4aeb51ab82457)
diff --git a/recipes-kernel/linux/files/0001-peci-core-Check-xfer-and-refcount-in-peci_command.patch b/recipes-kernel/linux/files/0001-peci-core-Check-xfer-and-refcount-in-peci_command.patch new file mode 100644 index 0000000..f3a832e --- /dev/null +++ b/recipes-kernel/linux/files/0001-peci-core-Check-xfer-and-refcount-in-peci_command.patch
@@ -0,0 +1,60 @@ +From 2b8b81232c6d71192a53a988417a16d396166233 Mon Sep 17 00:00:00 2001 +From: OpenEmbedded <oe.patch@oe> +Date: Fri, 10 Apr 2026 01:21:40 +0000 +Subject: [PATCH] peci: core: Check xfer and refcount in peci_command + +The peci_command function lacked proper synchronization with the teardown of +the adapter. This allowed transfers to continue or start while the adapter was +being removed, leading to NULL pointer dereferences when adapter->xfer is +cleared or when the adapter memory is freed. + +This patch adds a call to get_device to take a reference on the adapter +during the transfer, guaranteeing the adapter memory is not freed mid-transfer. +It also checks if adapter->xfer is still valid under bus_lock. +--- + drivers/peci/peci-core.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index 44c2669..36aaa78 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -1451,19 +1451,33 @@ int peci_command(struct peci_adapter *adapter, enum peci_cmd cmd, uint msg_len, + if (cmd >= PECI_CMD_MAX || cmd < PECI_CMD_XFER) + return -ENOTTY; + ++ if (!get_device(&adapter->dev)) ++ return -ENODEV; ++ + dev_dbg(&adapter->dev, "%s, cmd=0x%02x\n", __func__, cmd); + +- if (!peci_cmd_fn[cmd]) +- return -EINVAL; ++ if (!peci_cmd_fn[cmd]) { ++ ret = -EINVAL; ++ goto out; ++ } + + mutex_lock(&adapter->bus_lock); + ++ if (!adapter->xfer) { ++ mutex_unlock(&adapter->bus_lock); ++ ret = -ENODEV; ++ goto out; ++ } ++ + ret = peci_check_cmd_support(adapter, cmd); + if (!ret) + ret = peci_cmd_fn[cmd](adapter, msg_len, vmsg); + + mutex_unlock(&adapter->bus_lock); + ++out: ++ put_device(&adapter->dev); ++ + return ret; + } + EXPORT_SYMBOL_GPL(peci_command); +-- +2.53.0.1213.gd9a14994de-goog +
diff --git a/recipes-kernel/linux/files/0002-peci-core-Relocate-IDR-removal-to-release-callback.patch b/recipes-kernel/linux/files/0002-peci-core-Relocate-IDR-removal-to-release-callback.patch new file mode 100644 index 0000000..301d07b --- /dev/null +++ b/recipes-kernel/linux/files/0002-peci-core-Relocate-IDR-removal-to-release-callback.patch
@@ -0,0 +1,64 @@ +From 495d4c4c91994152446e0ae1f9c9786e33a4a4de Mon Sep 17 00:00:00 2001 +From: OpenEmbedded <oe.patch@oe> +Date: Fri, 10 Apr 2026 01:22:02 +0000 +Subject: [PATCH] peci: core: Relocate IDR removal to release callback + +The adapter ID was being freed in peci_del_adapter prematurely, before the +peci_adapter was fully released via its release callback. This allowed a new +adapter to be allocated the same ID while the old adapter was still in the +device model (and waiting for async release), leading to ID reuse and slab +corruption. + +This patch moves idr_remove to the peci_adapter_dev_release callback, +ensuring the ID is only reused once the device is completely torn down. +It also clears adapter->xfer under bus_lock in peci_del_adapter to +block new transfers during teardown. +--- + drivers/peci/peci-core.c | 16 ++++++++++------ + 1 file changed, 10 insertions(+), 6 deletions(-) + +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index 36aaa78..9ca259d 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -1765,6 +1765,11 @@ static void peci_adapter_dev_release(struct device *dev) + struct peci_adapter *adapter = to_peci_adapter(dev); + + dev_dbg(dev, "%s: %s\n", __func__, adapter->name); ++ ++ mutex_lock(&core_lock); ++ idr_remove(&peci_adapter_idr, adapter->nr); ++ mutex_unlock(&core_lock); ++ + mutex_destroy(&adapter->userspace_clients_lock); + mutex_destroy(&adapter->bus_lock); + kfree(adapter); +@@ -2259,6 +2264,11 @@ void peci_del_adapter(struct peci_adapter *adapter) + if (found != adapter) + return; + ++ /* Synchronize with and block any new transfers */ ++ mutex_lock(&adapter->bus_lock); ++ adapter->xfer = NULL; ++ mutex_unlock(&adapter->bus_lock); ++ + /* Remove devices instantiated from sysfs */ + mutex_lock(&adapter->userspace_clients_lock); + list_for_each_entry_safe(client, next, &adapter->userspace_clients, +@@ -2280,13 +2290,7 @@ void peci_del_adapter(struct peci_adapter *adapter) + dev_dbg(&adapter->dev, "adapter [%s] unregistered\n", adapter->name); + + pm_runtime_disable(&adapter->dev); +- nr = adapter->nr; + device_unregister(&adapter->dev); +- +- /* free bus id */ +- mutex_lock(&core_lock); +- idr_remove(&peci_adapter_idr, nr); +- mutex_unlock(&core_lock); + } + EXPORT_SYMBOL_GPL(peci_del_adapter); + +-- +2.53.0.1213.gd9a14994de-goog +
diff --git a/recipes-kernel/linux/files/0003-peci-npcm-Fix-use-after-free-and-ordering-in-teardow.patch b/recipes-kernel/linux/files/0003-peci-npcm-Fix-use-after-free-and-ordering-in-teardow.patch new file mode 100644 index 0000000..97c2943 --- /dev/null +++ b/recipes-kernel/linux/files/0003-peci-npcm-Fix-use-after-free-and-ordering-in-teardow.patch
@@ -0,0 +1,54 @@ +From 8694c685fb6c49c26d363317989764898e6e3de8 Mon Sep 17 00:00:00 2001 +From: OpenEmbedded <oe.patch@oe> +Date: Fri, 10 Apr 2026 01:22:27 +0000 +Subject: [PATCH] peci: npcm: Fix use-after-free and ordering in teardown + +The teardown ordering in the peci-npcm driver npcm_peci_remove function +caused use-after-free bugs. It invoked peci_del_adapter(priv->adapter), which +drops the final reference and frees priv (as it is devm-managed by the adapter). +Any access to priv->clk or priv->adapter->dev.of_node afterwards resulted in +UAF kernel crashes. + +This patch fixes the issue by caching clk and of_node in local variables +prior to deletion, explicitly disabling hardware interrupts, and waking up any +waiters via complete_all before peci_del_adapter is called. +--- + drivers/peci/busses/peci-npcm.c | 20 ++++++++++++++++++-- + 1 file changed, 18 insertions(+), 2 deletions(-) + +diff --git a/drivers/peci/busses/peci-npcm.c b/drivers/peci/busses/peci-npcm.c +index 95079cb..3349ff4 100644 +--- a/drivers/peci/busses/peci-npcm.c ++++ b/drivers/peci/busses/peci-npcm.c +@@ -385,10 +385,26 @@ static int npcm_peci_probe(struct platform_device *pdev) + static int npcm_peci_remove(struct platform_device *pdev) + { + struct npcm_peci *priv = dev_get_drvdata(&pdev->dev); ++ struct device_node *of_node = priv->adapter->dev.of_node; ++ struct clk *clk = priv->clk; + +- clk_disable_unprepare(priv->clk); ++ /* 1. Disable hardware interrupts */ ++ regmap_update_bits(priv->regmap, NPCM_PECI_CTL_STS, ++ NPCM_PECI_CTRL_DONE_INT_EN, 0); ++ ++ /* 2. Synchronize with IRQ handler and free it explicitly */ ++ synchronize_irq(priv->irq); ++ devm_free_irq(&pdev->dev, priv->irq, priv); ++ ++ /* 3. Wake up any waiters (they will see -ENODEV or timeout) */ ++ complete_all(&priv->xfer_complete); ++ ++ /* 4. Delete adapter (waits for active transfers via bus_lock) */ + peci_del_adapter(priv->adapter); +- of_node_put(priv->adapter->dev.of_node); ++ ++ /* 5. Disable clocks and release other resources */ ++ clk_disable_unprepare(clk); ++ of_node_put(of_node); + + return 0; + } +-- +2.53.0.1213.gd9a14994de-goog +
diff --git a/recipes-kernel/linux/files/0004-peci-dev-Fix-use-after-free-on-adapter-detach.patch b/recipes-kernel/linux/files/0004-peci-dev-Fix-use-after-free-on-adapter-detach.patch new file mode 100644 index 0000000..0b64fbf --- /dev/null +++ b/recipes-kernel/linux/files/0004-peci-dev-Fix-use-after-free-on-adapter-detach.patch
@@ -0,0 +1,163 @@ +From d1c970ee91d9f8c15da7564de4b1824388cd2a57 Mon Sep 17 00:00:00 2001 +From: OpenEmbedded <oe.patch@oe> +Date: Fri, 10 Apr 2026 01:25:33 +0000 +Subject: [PATCH] peci: dev: Fix use-after-free on adapter detach + +The peci_dev character device wrapper was vulnerable to use-after-free +during adapter removal while a userspace process held an open file descriptor. +When peci_dev_detach_adapter was called, it directly freed peci_dev, +leaving userspace holding dangling pointers when making subsequent ioctl calls. + +This patch introduces a kref reference counting mechanism for peci_dev and a +detached state flag. This guarantees the peci_dev structure and its resources +are only freed when all active userspace file descriptors are closed and the +adapter is detached. +--- + drivers/peci/peci-dev.c | 53 ++++++++++++++++++++++++++++++++--------- + 1 file changed, 42 insertions(+), 11 deletions(-) + +diff --git a/drivers/peci/peci-dev.c b/drivers/peci/peci-dev.c +index f026669..b7ad231 100644 +--- a/drivers/peci/peci-dev.c ++++ b/drivers/peci/peci-dev.c +@@ -3,6 +3,7 @@ + + #include <linux/cdev.h> + #include <linux/fs.h> ++#include <linux/kref.h> + #include <linux/list.h> + #include <linux/module.h> + #include <linux/notifier.h> +@@ -23,6 +24,8 @@ struct peci_dev { + struct peci_adapter *adapter; + struct device *dev; + struct cdev cdev; ++ struct kref ref; ++ bool detached; + }; + + #define PECI_MINORS MINORMASK +@@ -31,14 +34,23 @@ static dev_t peci_devt; + static LIST_HEAD(peci_dev_list); + static DEFINE_SPINLOCK(peci_dev_list_lock); + ++static void peci_dev_release_final(struct kref *ref) ++{ ++ struct peci_dev *peci_dev = container_of(ref, struct peci_dev, ref); ++ ++ kfree(peci_dev); ++} ++ + static struct peci_dev *peci_dev_get_by_minor(uint index) + { + struct peci_dev *peci_dev; + + spin_lock(&peci_dev_list_lock); + list_for_each_entry(peci_dev, &peci_dev_list, list) { +- if (peci_dev->adapter->nr == index) ++ if (peci_dev->adapter->nr == index) { ++ kref_get(&peci_dev->ref); + goto found; ++ } + } + peci_dev = NULL; + found: +@@ -61,6 +73,7 @@ static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter) + if (!peci_dev) + return ERR_PTR(-ENOMEM); + peci_dev->adapter = adapter; ++ kref_init(&peci_dev->ref); + + spin_lock(&peci_dev_list_lock); + list_add_tail(&peci_dev->list, &peci_dev_list); +@@ -71,21 +84,22 @@ static struct peci_dev *peci_dev_alloc(struct peci_adapter *adapter) + + static void peci_dev_put(struct peci_dev *peci_dev) + { +- spin_lock(&peci_dev_list_lock); +- list_del(&peci_dev->list); +- spin_unlock(&peci_dev_list_lock); +- kfree(peci_dev); ++ kref_put(&peci_dev->ref, peci_dev_release_final); + } + + static ssize_t name_show(struct device *dev, + struct device_attribute *attr, char *buf) + { + struct peci_dev *peci_dev = peci_dev_get_by_minor(MINOR(dev->devt)); ++ ssize_t ret; + + if (!peci_dev) + return -ENODEV; + +- return sprintf(buf, "%s\n", peci_dev->adapter->name); ++ ret = sprintf(buf, "%s\n", peci_dev->adapter->name); ++ peci_dev_put(peci_dev); ++ ++ return ret; + } + static DEVICE_ATTR_RO(name); + +@@ -106,6 +120,9 @@ static long peci_dev_ioctl(struct file *file, uint iocmd, ulong arg) + uint msg_len; + int ret; + ++ if (peci_dev->detached) ++ return -ENODEV; ++ + cmd = _IOC_NR(iocmd); + msg_len = _IOC_SIZE(iocmd); + +@@ -188,8 +205,10 @@ static int peci_dev_open(struct inode *inode, struct file *file) + return -ENODEV; + + adapter = peci_get_adapter(peci_dev->adapter->nr); +- if (!adapter) ++ if (!adapter) { ++ peci_dev_put(peci_dev); + return -ENODEV; ++ } + + file->private_data = peci_dev; + +@@ -201,6 +220,7 @@ static int peci_dev_release(struct inode *inode, struct file *file) + struct peci_dev *peci_dev = file->private_data; + + peci_put_adapter(peci_dev->adapter); ++ peci_dev_put(peci_dev); + file->private_data = NULL; + + return 0; +@@ -270,14 +290,25 @@ static int peci_dev_detach_adapter(struct device *dev, void *dummy) + return 0; + + adapter = to_peci_adapter(dev); +- peci_dev = peci_dev_get_by_minor(adapter->nr); +- if (!peci_dev) +- return 0; ++ ++ spin_lock(&peci_dev_list_lock); ++ list_for_each_entry(peci_dev, &peci_dev_list, list) { ++ if (peci_dev->adapter == adapter) { ++ peci_dev->detached = true; ++ list_del(&peci_dev->list); ++ goto found; ++ } ++ } ++ spin_unlock(&peci_dev_list_lock); ++ return 0; ++ ++found: ++ spin_unlock(&peci_dev_list_lock); + + cdev_del(&peci_dev->cdev); + devt = peci_dev->dev->devt; +- peci_dev_put(peci_dev); + device_destroy(peci_dev_class, devt); ++ peci_dev_put(peci_dev); + + dev_info(dev, "cdev of adapter [%s] unregistered\n", adapter->name); + +-- +2.53.0.1213.gd9a14994de-goog +
diff --git a/recipes-kernel/linux/files/0005-peci-core-Fix-sysfs-new_device-race-condition.patch b/recipes-kernel/linux/files/0005-peci-core-Fix-sysfs-new_device-race-condition.patch new file mode 100644 index 0000000..d871e0f --- /dev/null +++ b/recipes-kernel/linux/files/0005-peci-core-Fix-sysfs-new_device-race-condition.patch
@@ -0,0 +1,45 @@ +From 4bd67c67f83241f7526c31c973ba92c2fc82031a Mon Sep 17 00:00:00 2001 +From: OpenEmbedded <oe.patch@oe> +Date: Fri, 10 Apr 2026 01:33:24 +0000 +Subject: [PATCH] peci: core: Fix sysfs new_device race condition + +Concurrent writes to the 'new_device' sysfs attribute could result in +multiple processes executing peci_new_device() for the same address at +the same time. The peci_check_client_busy() check inside peci_new_device() +would pass for both before either completed device_register(), resulting in +a duplicate sysfs device creation warning and failure (-EEXIST). + +This patch moves the peci_new_device() call inside the userspace_clients_lock +mutex section, synchronizing it with other new_device and delete_device +sysfs writes. +--- + drivers/peci/peci-core.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +diff --git a/drivers/peci/peci-core.c b/drivers/peci/peci-core.c +index 9ca259d..5127a05 100644 +--- a/drivers/peci/peci-core.c ++++ b/drivers/peci/peci-core.c +@@ -1819,12 +1819,16 @@ static ssize_t peci_sysfs_new_device(struct device *dev, + + info.addr = addr; + info.domain_id = domain_id; +- client = peci_new_device(adapter, &info); +- if (!client) +- return -EINVAL; + + /* Keep track of the added device */ + mutex_lock(&adapter->userspace_clients_lock); ++ ++ client = peci_new_device(adapter, &info); ++ if (!client) { ++ mutex_unlock(&adapter->userspace_clients_lock); ++ return -EINVAL; ++ } ++ + list_add_tail(&client->detected, &adapter->userspace_clients); + mutex_unlock(&adapter->userspace_clients_lock); + dev_dbg(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device", +-- +2.53.0.1213.gd9a14994de-goog +
diff --git a/recipes-kernel/linux/linux-gbmc_5.15.bb b/recipes-kernel/linux/linux-gbmc_5.15.bb index f49e9b6..3f296e4 100644 --- a/recipes-kernel/linux/linux-gbmc_5.15.bb +++ b/recipes-kernel/linux/linux-gbmc_5.15.bb
@@ -14,6 +14,11 @@ SRC_URI:prepend = "git://gbmc.googlesource.com/linux;protocol=https;branch=${KBRANCH} " SRC_URI:append = " \ + file://0001-peci-core-Check-xfer-and-refcount-in-peci_command.patch \ + file://0002-peci-core-Relocate-IDR-removal-to-release-callback.patch \ + file://0003-peci-npcm-Fix-use-after-free-and-ordering-in-teardow.patch \ + file://0004-peci-dev-Fix-use-after-free-on-adapter-detach.patch \ + file://0005-peci-core-Fix-sysfs-new_device-race-condition.patch \ file://0001-hwmon-Add-driver-for-MPS-MPQ8785-Synchronous-Step-Do.patch \ file://0001-hwmon-pmbus-Add-ltc4286-driver.patch \ file://0001-Add-support-for-Winbond-W25Q512NW-IQ-IN.patch \