blob: 0b64fbf4af583ea0631737ebf7618a7250342138 [file]
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