mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v1] misc: amd-sbi: Fix use-after-free on driver unbind
@ 2026-10-26 11:39 Akshay Gupta
  2026-10-26 12:14 ` Greg KH
  0 siblings, 1 reply; 4+ messages in thread
From: Akshay Gupta @ 2026-10-26 11:39 UTC (permalink / raw)
  To: linux-kernel
  Cc: arnd, gregkh, naveenkrishna.chatradhi, Anand.Umarji,
	Prathima L K, Akshay Gupta

From: Prathima L K <Prathima.Lk@amd.com>

sbrmi_data was allocated with devm_kzalloc() and freed at unbind while
an open /dev/sbrmi-* fd could still reference it, leading to a
use-after-free on ioctl after removal.

Refcount sbrmi_data with a kref: take a reference in open(), drop it in
release(), and free the structure only when the last fd closes. Mark the
device 'detached' under lock when the misc device is deregistered so all
ioctl/xfer paths return -ENODEV. Move misc deregistration to a devm
action and drop the now-unneeded i2c/i3c ".remove" callbacks.

Fixes: 35ac2034db72bbbc73609aab5f05ff6e0d38fdd0 ("misc: amd-sbi: Add support for AMD_SBI IOCTL")

Reviewed-by: Akshay Gupta <Akshay.Gupta@amd.com>
Signed-off-by: Akshay Gupta <Akshay.Gupta@amd.com>
Signed-off-by: Prathima L K <Prathima.Lk@amd.com>
---
 drivers/misc/amd-sbi/rmi-core.c | 54 +++++++++++++++++++++++++++++++++
 drivers/misc/amd-sbi/rmi-core.h |  6 ++++
 drivers/misc/amd-sbi/rmi-i2c.c  | 54 +++++++++++++++++++--------------
 3 files changed, 91 insertions(+), 23 deletions(-)

diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c
index 95c9109101b5..e77f114c16c6 100644
--- a/drivers/misc/amd-sbi/rmi-core.c
+++ b/drivers/misc/amd-sbi/rmi-core.c
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/regmap.h>
+#include <linux/slab.h>
 #include "rmi-core.h"
 
 /* Mask for Status Register bit[1] */
@@ -193,6 +194,10 @@ static int rmi_cpuid_read(struct sbrmi_data *data,
 	u16 thread;
 
 	mutex_lock(&data->lock);
+	if (data->detached) {
+		ret = -ENODEV;
+		goto exit_unlock;
+	}
 	/* cache the rev value to identify if protocol is supported or not */
 	if (!data->rev) {
 		ret = sbrmi_get_rev(data);
@@ -307,6 +312,10 @@ static int rmi_mca_msr_read(struct sbrmi_data *data,
 	u16 thread;
 
 	mutex_lock(&data->lock);
+	if (data->detached) {
+		ret = -ENODEV;
+		goto exit_unlock;
+	}
 	/* cache the rev value to identify if protocol is supported or not */
 	if (!data->rev) {
 		ret = sbrmi_get_rev(data);
@@ -384,6 +393,10 @@ int rmi_mailbox_xfer(struct sbrmi_data *data,
 	u8 byte;
 
 	mutex_lock(&data->lock);
+	if (data->detached) {
+		ret = -ENODEV;
+		goto exit_unlock;
+	}
 
 	msg->fw_ret_code = 0;
 
@@ -475,6 +488,10 @@ static int apml_rmi_reg_xfer(struct sbrmi_data *data,
 		return -EFAULT;
 
 	mutex_lock(&data->lock);
+	if (data->detached) {
+		mutex_unlock(&data->lock);
+		return -ENODEV;
+	}
 	if (msg.rflag) {
 		ret = regmap_read(data->regmap, msg.reg_addr, &data_read);
 		if (!ret)
@@ -548,6 +565,41 @@ static int apml_mcamsr_xfer(struct sbrmi_data *data, struct apml_mcamsr_msg __us
 	return ret;
 }
 
+void sbrmi_data_release(struct kref *kref)
+{
+	struct sbrmi_data *data = container_of(kref, struct sbrmi_data, kref);
+
+	mutex_destroy(&data->lock);
+	kfree(data);
+}
+
+static int sbrmi_open(struct inode *inode, struct file *fp)
+{
+	struct sbrmi_data *data;
+
+	data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
+
+	mutex_lock(&data->lock);
+	if (data->detached) {
+		mutex_unlock(&data->lock);
+		return -ENODEV;
+	}
+	mutex_unlock(&data->lock);
+
+	kref_get(&data->kref);
+
+	return 0;
+}
+
+static int sbrmi_release(struct inode *inode, struct file *fp)
+{
+	struct sbrmi_data *data;
+
+	data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
+	kref_put(&data->kref, sbrmi_data_release);
+	return 0;
+}
+
 static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 {
 	void __user *argp = (void __user *)arg;
@@ -570,6 +622,8 @@ static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
 
 static const struct file_operations sbrmi_fops = {
 	.owner		= THIS_MODULE,
+	.open		= sbrmi_open,
+	.release	= sbrmi_release,
 	.unlocked_ioctl	= sbrmi_ioctl,
 	.compat_ioctl	= compat_ptr_ioctl,
 };
diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h
index 975ae858e9fd..ae084beb6c03 100644
--- a/drivers/misc/amd-sbi/rmi-core.h
+++ b/drivers/misc/amd-sbi/rmi-core.h
@@ -9,6 +9,7 @@
 #include <linux/miscdevice.h>
 #include <linux/mutex.h>
 #include <linux/i2c.h>
+#include <linux/kref.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <uapi/misc/amd-apml.h>
@@ -56,9 +57,13 @@ struct sbrmi_data {
 	struct regmap *regmap;
 	/* Mutex locking */
 	struct mutex lock;
+	/* Reference count; keeps @sbrmi_data alive while misc fds are open */
+	struct kref kref;
 	u32 pwr_limit_max;
 	u8 dev_static_addr;
 	u8 rev;
+	/* Set on driver unbind; open/ioctl return -ENODEV afterward */
+	bool detached;
 };
 
 int rmi_mailbox_xfer(struct sbrmi_data *data, struct apml_mbox_msg *msg);
@@ -71,4 +76,5 @@ static inline int create_hwmon_sensor_device(struct device *dev, struct sbrmi_da
 }
 #endif
 int create_misc_rmi_device(struct sbrmi_data *data, struct device *dev);
+void sbrmi_data_release(struct kref *kref);
 #endif /*_SBRMI_CORE_H_*/
diff --git a/drivers/misc/amd-sbi/rmi-i2c.c b/drivers/misc/amd-sbi/rmi-i2c.c
index 463a9a757464..456f17806107 100644
--- a/drivers/misc/amd-sbi/rmi-i2c.c
+++ b/drivers/misc/amd-sbi/rmi-i2c.c
@@ -16,6 +16,7 @@
 #include <linux/mutex.h>
 #include <linux/of.h>
 #include <linux/regmap.h>
+#include <linux/slab.h>
 #include "rmi-core.h"
 
 #define REV_TWO_BYTE_ADDR	0x21
@@ -54,17 +55,40 @@ static int sbrmi_get_max_pwr_limit(struct sbrmi_data *data)
 	return ret;
 }
 
+static void sbrmi_misc_unregister(void *arg)
+{
+	struct sbrmi_data *data = arg;
+
+	misc_deregister(&data->sbrmi_misc_dev);
+
+	mutex_lock(&data->lock);
+	data->detached = true;
+	mutex_unlock(&data->lock);
+}
+
+static void sbrmi_driver_unref(void *arg)
+{
+	struct sbrmi_data *data = arg;
+
+	kref_put(&data->kref, sbrmi_data_release);
+}
+
 static int sbrmi_common_probe(struct device *dev, struct regmap *regmap, uint8_t address)
 {
 	struct sbrmi_data *data;
 	int ret;
 
-	data = devm_kzalloc(dev, sizeof(struct sbrmi_data), GFP_KERNEL);
+	data = kzalloc_obj(*data);
 	if (!data)
 		return -ENOMEM;
 
 	data->regmap = regmap;
 	mutex_init(&data->lock);
+	kref_init(&data->kref);
+
+	ret = devm_add_action_or_reset(dev, sbrmi_driver_unref, data);
+	if (ret)
+		return ret;
 
 	/* Enable alert for SB-RMI sequence */
 	ret = sbrmi_enable_alert(data);
@@ -83,7 +107,12 @@ static int sbrmi_common_probe(struct device *dev, struct regmap *regmap, uint8_t
 	ret = create_hwmon_sensor_device(dev, data);
 	if (ret < 0)
 		return ret;
-	return create_misc_rmi_device(data, dev);
+
+	ret = create_misc_rmi_device(data, dev);
+	if (ret)
+		return ret;
+
+	return devm_add_action_or_reset(dev, sbrmi_misc_unregister, data);
 }
 
 static struct regmap_config sbrmi_regmap_config = {
@@ -128,18 +157,6 @@ static int sbrmi_i2c_probe(struct i2c_client *client)
 	return sbrmi_common_probe(dev, regmap, client->addr);
 }
 
-static void sbrmi_i2c_remove(struct i2c_client *client)
-{
-	struct sbrmi_data *data = dev_get_drvdata(&client->dev);
-
-	misc_deregister(&data->sbrmi_misc_dev);
-	/* Assign fops and parent of misc dev to NULL */
-	data->sbrmi_misc_dev.fops = NULL;
-	data->sbrmi_misc_dev.parent = NULL;
-	dev_info(&client->dev, "Removed sbrmi-i2c driver\n");
-	return;
-}
-
 static const struct i2c_device_id sbrmi_id[] = {
 	{ .name = "sbrmi-i2c" },
 	{ }
@@ -160,7 +177,6 @@ static struct i2c_driver sbrmi_driver = {
 		.of_match_table = of_match_ptr(sbrmi_of_match),
 	},
 	.probe = sbrmi_i2c_probe,
-	.remove = sbrmi_i2c_remove,
 	.id_table = sbrmi_id,
 };
 
@@ -212,13 +228,6 @@ static int sbrmi_i3c_probe(struct i3c_device *i3cdev)
 	return sbrmi_common_probe(dev, regmap, i3cdev->desc->info.dyn_addr);
 }
 
-static void sbrmi_i3c_remove(struct i3c_device *i3cdev)
-{
-	struct sbrmi_data *data = dev_get_drvdata(&i3cdev->dev);
-
-	misc_deregister(&data->sbrmi_misc_dev);
-}
-
 static const struct i3c_device_id sbrmi_i3c_id[] = {
 	/* PID for AMD SBRMI device */
 	I3C_DEVICE_EXTRA_INFO(0x112, 0x0, 0x2, NULL),
@@ -235,7 +244,6 @@ static struct i3c_driver sbrmi_i3c_driver = {
 		.name = "sbrmi-i3c",
 	},
 	.probe = sbrmi_i3c_probe,
-	.remove = sbrmi_i3c_remove,
 	.id_table = sbrmi_i3c_id,
 };
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v1] misc: amd-sbi: Fix use-after-free on driver unbind
  2026-10-26 11:39 [PATCH v1] misc: amd-sbi: Fix use-after-free on driver unbind Akshay Gupta
@ 2026-10-26 12:14 ` Greg KH
  2026-10-28 12:19   ` Gupta, Akshay
  0 siblings, 1 reply; 4+ messages in thread
From: Greg KH @ 2026-10-26 12:14 UTC (permalink / raw)
  To: Akshay Gupta
  Cc: linux-kernel, arnd, naveenkrishna.chatradhi, Anand.Umarji, Prathima L K

On Mon, Oct 26, 2026 at 05:09:10PM +0530, Akshay Gupta wrote:
> From: Prathima L K <Prathima.Lk@amd.com>
> 
> sbrmi_data was allocated with devm_kzalloc() and freed at unbind while
> an open /dev/sbrmi-* fd could still reference it, leading to a
> use-after-free on ioctl after removal.
> 
> Refcount sbrmi_data with a kref: take a reference in open(), drop it in
> release(), and free the structure only when the last fd closes. Mark the
> device 'detached' under lock when the misc device is deregistered so all
> ioctl/xfer paths return -ENODEV. Move misc deregistration to a devm
> action and drop the now-unneeded i2c/i3c ".remove" callbacks.
> 
> Fixes: 35ac2034db72bbbc73609aab5f05ff6e0d38fdd0 ("misc: amd-sbi: Add support for AMD_SBI IOCTL")

The documentation shows how to properly format this line, please fix it
up, and don't put a blank line between it and these:

> Reviewed-by: Akshay Gupta <Akshay.Gupta@amd.com>
> Signed-off-by: Akshay Gupta <Akshay.Gupta@amd.com>

signed-off-by implies review.

> Signed-off-by: Prathima L K <Prathima.Lk@amd.com>

Wait, this is out of order, yours should be below this, right?



> ---
>  drivers/misc/amd-sbi/rmi-core.c | 54 +++++++++++++++++++++++++++++++++
>  drivers/misc/amd-sbi/rmi-core.h |  6 ++++
>  drivers/misc/amd-sbi/rmi-i2c.c  | 54 +++++++++++++++++++--------------
>  3 files changed, 91 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c
> index 95c9109101b5..e77f114c16c6 100644
> --- a/drivers/misc/amd-sbi/rmi-core.c
> +++ b/drivers/misc/amd-sbi/rmi-core.c
> @@ -13,6 +13,7 @@
>  #include <linux/module.h>
>  #include <linux/mutex.h>
>  #include <linux/regmap.h>
> +#include <linux/slab.h>
>  #include "rmi-core.h"
>  
>  /* Mask for Status Register bit[1] */
> @@ -193,6 +194,10 @@ static int rmi_cpuid_read(struct sbrmi_data *data,
>  	u16 thread;
>  
>  	mutex_lock(&data->lock);
> +	if (data->detached) {
> +		ret = -ENODEV;
> +		goto exit_unlock;

Why isn't a guard being used here instead?  Then you can do a simple
"return -ENODEV", right?

> +static int sbrmi_open(struct inode *inode, struct file *fp)
> +{
> +	struct sbrmi_data *data;
> +
> +	data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
> +
> +	mutex_lock(&data->lock);

Again, please use a guard.

> +	if (data->detached) {
> +		mutex_unlock(&data->lock);
> +		return -ENODEV;
> +	}
> +	mutex_unlock(&data->lock);
> +
> +	kref_get(&data->kref);

Are you attempting to use a kref without a lock?  That way lies madness
and is sure to be wrong.

Are you attempting to keep the data "alive" somehow?  Are you sure this
is correct?

> +
> +	return 0;
> +}
> +
> +static int sbrmi_release(struct inode *inode, struct file *fp)
> +{
> +	struct sbrmi_data *data;
> +
> +	data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
> +	kref_put(&data->kref, sbrmi_data_release);

If open is the only thing that can get the reference to the kref, and
this is the thing that drops it, why is the kref needed at all?



> +	return 0;
> +}
> +
>  static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
>  {
>  	void __user *argp = (void __user *)arg;
> @@ -570,6 +622,8 @@ static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
>  
>  static const struct file_operations sbrmi_fops = {
>  	.owner		= THIS_MODULE,
> +	.open		= sbrmi_open,
> +	.release	= sbrmi_release,
>  	.unlocked_ioctl	= sbrmi_ioctl,
>  	.compat_ioctl	= compat_ptr_ioctl,
>  };
> diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h
> index 975ae858e9fd..ae084beb6c03 100644
> --- a/drivers/misc/amd-sbi/rmi-core.h
> +++ b/drivers/misc/amd-sbi/rmi-core.h
> @@ -9,6 +9,7 @@
>  #include <linux/miscdevice.h>
>  #include <linux/mutex.h>
>  #include <linux/i2c.h>
> +#include <linux/kref.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
>  #include <uapi/misc/amd-apml.h>
> @@ -56,9 +57,13 @@ struct sbrmi_data {
>  	struct regmap *regmap;
>  	/* Mutex locking */
>  	struct mutex lock;
> +	/* Reference count; keeps @sbrmi_data alive while misc fds are open */
> +	struct kref kref;
>  	u32 pwr_limit_max;
>  	u8 dev_static_addr;
>  	u8 rev;
> +	/* Set on driver unbind; open/ioctl return -ENODEV afterward */
> +	bool detached;

How can open/ioctl even run if the driver is unloaded?  You aren't using
the cdev api properly I think...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v1] misc: amd-sbi: Fix use-after-free on driver unbind
  2026-10-26 12:14 ` Greg KH
@ 2026-10-28 12:19   ` Gupta, Akshay
  2026-10-28 12:26     ` Greg KH
  0 siblings, 1 reply; 4+ messages in thread
From: Gupta, Akshay @ 2026-10-28 12:19 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-kernel, arnd, naveenkrishna.chatradhi, Anand.Umarji, Prathima L K


On 10/26/2026 5:44 PM, Greg KH wrote:
> Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
>
>
> On Mon, Oct 26, 2026 at 05:09:10PM +0530, Akshay Gupta wrote:
>> From: Prathima L K <Prathima.Lk@amd.com>
>>
>> sbrmi_data was allocated with devm_kzalloc() and freed at unbind while
>> an open /dev/sbrmi-* fd could still reference it, leading to a
>> use-after-free on ioctl after removal.
>>
>> Refcount sbrmi_data with a kref: take a reference in open(), drop it in
>> release(), and free the structure only when the last fd closes. Mark the
>> device 'detached' under lock when the misc device is deregistered so all
>> ioctl/xfer paths return -ENODEV. Move misc deregistration to a devm
>> action and drop the now-unneeded i2c/i3c ".remove" callbacks.
>>
>> Fixes: 35ac2034db72bbbc73609aab5f05ff6e0d38fdd0 ("misc: amd-sbi: Add support for AMD_SBI IOCTL")
> The documentation shows how to properly format this line, please fix it
> up, and don't put a blank line between it and these:

Hi Greg,

Thank you for the feedback.

Will update the commit message in proper format.

>
>> Reviewed-by: Akshay Gupta <Akshay.Gupta@amd.com>
>> Signed-off-by: Akshay Gupta <Akshay.Gupta@amd.com>
> signed-off-by implies review.
>
>> Signed-off-by: Prathima L K <Prathima.Lk@amd.com>
> Wait, this is out of order, yours should be below this, right?
>
I will drop my Reviewed-by, and fix the order
>
>> ---
>>   drivers/misc/amd-sbi/rmi-core.c | 54 +++++++++++++++++++++++++++++++++
>>   drivers/misc/amd-sbi/rmi-core.h |  6 ++++
>>   drivers/misc/amd-sbi/rmi-i2c.c  | 54 +++++++++++++++++++--------------
>>   3 files changed, 91 insertions(+), 23 deletions(-)
>>
>> diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c
>> index 95c9109101b5..e77f114c16c6 100644
>> --- a/drivers/misc/amd-sbi/rmi-core.c
>> +++ b/drivers/misc/amd-sbi/rmi-core.c
>> @@ -13,6 +13,7 @@
>>   #include <linux/module.h>
>>   #include <linux/mutex.h>
>>   #include <linux/regmap.h>
>> +#include <linux/slab.h>
>>   #include "rmi-core.h"
>>
>>   /* Mask for Status Register bit[1] */
>> @@ -193,6 +194,10 @@ static int rmi_cpuid_read(struct sbrmi_data *data,
>>        u16 thread;
>>
>>        mutex_lock(&data->lock);
>> +     if (data->detached) {
>> +             ret = -ENODEV;
>> +             goto exit_unlock;
> Why isn't a guard being used here instead?  Then you can do a simple
> "return -ENODEV", right?

The existing code in rmi-core.c take data->lock with mutex_lock() and 
release through mutex_unlock()
The new code added as part of this fix patch, follows this so patch 
stays a lifetime fix.
I can submit separate patch converting the mutex_lock/mutex_unlock() to 
guard(mutex) after the fix patch is merged.
Let me know your opinion if you suggest to submit the guard_mutex() 
patch before the fix patch.

>> +static int sbrmi_open(struct inode *inode, struct file *fp)
>> +{
>> +     struct sbrmi_data *data;
>> +
>> +     data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
>> +
>> +     mutex_lock(&data->lock);
> Again, please use a guard.
same as above.
>> +     if (data->detached) {
>> +             mutex_unlock(&data->lock);
>> +             return -ENODEV;
>> +     }
>> +     mutex_unlock(&data->lock);
>> +
>> +     kref_get(&data->kref);
> Are you attempting to use a kref without a lock?  That way lies madness
> and is sure to be wrong.
>
> Are you attempting to keep the data "alive" somehow?  Are you sure this
> is correct?
Yes, the kref is there to keep sbrmi_data alive. kref_get() in open() 
does not race with the final put. misc_open() calls ->open() while 
holding misc_mtx, and misc_deregister() takes that same mutex. The probe 
reference is dropped by a devm action that runs only after
misc_deregister() has returned, so the object cannot reach zero while 
open() runs.
kref_get() and kref_put() are atomic. I will add a comment at the 
kref_get() saying this.
>> +
>> +     return 0;
>> +}
>> +
>> +static int sbrmi_release(struct inode *inode, struct file *fp)
>> +{
>> +     struct sbrmi_data *data;
>> +
>> +     data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
>> +     kref_put(&data->kref, sbrmi_data_release);
> If open is the only thing that can get the reference to the kref, and
> this is the thing that drops it, why is the kref needed at all?
open () is not the only holder, kref_init() is the reference owned by 
the driver binding and a devm action drops it on unbind. each open() 
takes another reference and drops it in release().
sbrmi_data is freed only when both are gone, which is what closes the 
window where devm frees the structure while an ioctl is still using it.
>
>
>> +     return 0;
>> +}
>> +
>>   static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
>>   {
>>        void __user *argp = (void __user *)arg;
>> @@ -570,6 +622,8 @@ static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
>>
>>   static const struct file_operations sbrmi_fops = {
>>        .owner          = THIS_MODULE,
>> +     .open           = sbrmi_open,
>> +     .release        = sbrmi_release,
>>        .unlocked_ioctl = sbrmi_ioctl,
>>        .compat_ioctl   = compat_ptr_ioctl,
>>   };
>> diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h
>> index 975ae858e9fd..ae084beb6c03 100644
>> --- a/drivers/misc/amd-sbi/rmi-core.h
>> +++ b/drivers/misc/amd-sbi/rmi-core.h
>> @@ -9,6 +9,7 @@
>>   #include <linux/miscdevice.h>
>>   #include <linux/mutex.h>
>>   #include <linux/i2c.h>
>> +#include <linux/kref.h>
>>   #include <linux/platform_device.h>
>>   #include <linux/regmap.h>
>>   #include <uapi/misc/amd-apml.h>
>> @@ -56,9 +57,13 @@ struct sbrmi_data {
>>        struct regmap *regmap;
>>        /* Mutex locking */
>>        struct mutex lock;
>> +     /* Reference count; keeps @sbrmi_data alive while misc fds are open */
>> +     struct kref kref;
>>        u32 pwr_limit_max;
>>        u8 dev_static_addr;
>>        u8 rev;
>> +     /* Set on driver unbind; open/ioctl return -ENODEV afterward */
>> +     bool detached;
> How can open/ioctl even run if the driver is unloaded?  You aren't using
> the cdev api properly I think...

This fix addresses driver unbind, including unbind through sysfs; it is 
not limited to module unload. misc_deregister() removes the miscdevice 
from the misc-device list and destroys its device node. Although it 
prevents new opens, it does not retain a reference to the driver-private 
sbrmi_data for files that are already open. Such files retain sbrmi_fops 
and private_data, so a later ioctl can still enter the driver.

Without the kref, unbind cleanup could free sbrmi_data while an open 
file still refers to its embedded miscdevice, causing a use-after-free 
on a subsequent ioctl. With the kref, unbind marks the device detached, 
deregisters the miscdevice, and drops the probe reference. Existing open 
files retain their references, and the object is freed only after the 
final close. Subsequent ioctl operations on those lingering descriptors 
observe detached and return -ENODEV.

>
> thanks,
>
> greg k-h

^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v1] misc: amd-sbi: Fix use-after-free on driver unbind
  2026-10-28 12:19   ` Gupta, Akshay
@ 2026-10-28 12:26     ` Greg KH
  0 siblings, 0 replies; 4+ messages in thread
From: Greg KH @ 2026-10-28 12:26 UTC (permalink / raw)
  To: Gupta, Akshay
  Cc: linux-kernel, arnd, naveenkrishna.chatradhi, Anand.Umarji, Prathima L K

On Wed, Oct 28, 2026 at 05:49:32PM +0530, Gupta, Akshay wrote:
> 
> On 10/26/2026 5:44 PM, Greg KH wrote:
> > Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
> > 
> > 
> > On Mon, Oct 26, 2026 at 05:09:10PM +0530, Akshay Gupta wrote:
> > > From: Prathima L K <Prathima.Lk@amd.com>
> > > 
> > > sbrmi_data was allocated with devm_kzalloc() and freed at unbind while
> > > an open /dev/sbrmi-* fd could still reference it, leading to a
> > > use-after-free on ioctl after removal.
> > > 
> > > Refcount sbrmi_data with a kref: take a reference in open(), drop it in
> > > release(), and free the structure only when the last fd closes. Mark the
> > > device 'detached' under lock when the misc device is deregistered so all
> > > ioctl/xfer paths return -ENODEV. Move misc deregistration to a devm
> > > action and drop the now-unneeded i2c/i3c ".remove" callbacks.
> > > 
> > > Fixes: 35ac2034db72bbbc73609aab5f05ff6e0d38fdd0 ("misc: amd-sbi: Add support for AMD_SBI IOCTL")
> > The documentation shows how to properly format this line, please fix it
> > up, and don't put a blank line between it and these:
> 
> Hi Greg,
> 
> Thank you for the feedback.
> 
> Will update the commit message in proper format.
> 
> > 
> > > Reviewed-by: Akshay Gupta <Akshay.Gupta@amd.com>
> > > Signed-off-by: Akshay Gupta <Akshay.Gupta@amd.com>
> > signed-off-by implies review.
> > 
> > > Signed-off-by: Prathima L K <Prathima.Lk@amd.com>
> > Wait, this is out of order, yours should be below this, right?
> > 
> I will drop my Reviewed-by, and fix the order
> > 
> > > ---
> > >   drivers/misc/amd-sbi/rmi-core.c | 54 +++++++++++++++++++++++++++++++++
> > >   drivers/misc/amd-sbi/rmi-core.h |  6 ++++
> > >   drivers/misc/amd-sbi/rmi-i2c.c  | 54 +++++++++++++++++++--------------
> > >   3 files changed, 91 insertions(+), 23 deletions(-)
> > > 
> > > diff --git a/drivers/misc/amd-sbi/rmi-core.c b/drivers/misc/amd-sbi/rmi-core.c
> > > index 95c9109101b5..e77f114c16c6 100644
> > > --- a/drivers/misc/amd-sbi/rmi-core.c
> > > +++ b/drivers/misc/amd-sbi/rmi-core.c
> > > @@ -13,6 +13,7 @@
> > >   #include <linux/module.h>
> > >   #include <linux/mutex.h>
> > >   #include <linux/regmap.h>
> > > +#include <linux/slab.h>
> > >   #include "rmi-core.h"
> > > 
> > >   /* Mask for Status Register bit[1] */
> > > @@ -193,6 +194,10 @@ static int rmi_cpuid_read(struct sbrmi_data *data,
> > >        u16 thread;
> > > 
> > >        mutex_lock(&data->lock);
> > > +     if (data->detached) {
> > > +             ret = -ENODEV;
> > > +             goto exit_unlock;
> > Why isn't a guard being used here instead?  Then you can do a simple
> > "return -ENODEV", right?
> 
> The existing code in rmi-core.c take data->lock with mutex_lock() and
> release through mutex_unlock()
> The new code added as part of this fix patch, follows this so patch stays a
> lifetime fix.
> I can submit separate patch converting the mutex_lock/mutex_unlock() to
> guard(mutex) after the fix patch is merged.
> Let me know your opinion if you suggest to submit the guard_mutex() patch
> before the fix patch.
> 
> > > +static int sbrmi_open(struct inode *inode, struct file *fp)
> > > +{
> > > +     struct sbrmi_data *data;
> > > +
> > > +     data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
> > > +
> > > +     mutex_lock(&data->lock);
> > Again, please use a guard.
> same as above.
> > > +     if (data->detached) {
> > > +             mutex_unlock(&data->lock);
> > > +             return -ENODEV;
> > > +     }
> > > +     mutex_unlock(&data->lock);
> > > +
> > > +     kref_get(&data->kref);
> > Are you attempting to use a kref without a lock?  That way lies madness
> > and is sure to be wrong.
> > 
> > Are you attempting to keep the data "alive" somehow?  Are you sure this
> > is correct?
> Yes, the kref is there to keep sbrmi_data alive. kref_get() in open() does
> not race with the final put. misc_open() calls ->open() while holding
> misc_mtx, and misc_deregister() takes that same mutex. The probe reference
> is dropped by a devm action that runs only after
> misc_deregister() has returned, so the object cannot reach zero while open()
> runs.
> kref_get() and kref_put() are atomic. I will add a comment at the kref_get()
> saying this.
> > > +
> > > +     return 0;
> > > +}
> > > +
> > > +static int sbrmi_release(struct inode *inode, struct file *fp)
> > > +{
> > > +     struct sbrmi_data *data;
> > > +
> > > +     data = container_of(fp->private_data, struct sbrmi_data, sbrmi_misc_dev);
> > > +     kref_put(&data->kref, sbrmi_data_release);
> > If open is the only thing that can get the reference to the kref, and
> > this is the thing that drops it, why is the kref needed at all?
> open () is not the only holder, kref_init() is the reference owned by the
> driver binding and a devm action drops it on unbind. each open() takes
> another reference and drops it in release().
> sbrmi_data is freed only when both are gone, which is what closes the window
> where devm frees the structure while an ioctl is still using it.

That's not good, this should be attached to the file descriptor.  Don't
go allocating things at bind as you will mess with the lifetime rules
here very quickly, and it gets very messy.  Rely on the rules that the
vfs layer gives us and it will be much much simpler.

> > 
> > 
> > > +     return 0;
> > > +}
> > > +
> > >   static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> > >   {
> > >        void __user *argp = (void __user *)arg;
> > > @@ -570,6 +622,8 @@ static long sbrmi_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
> > > 
> > >   static const struct file_operations sbrmi_fops = {
> > >        .owner          = THIS_MODULE,
> > > +     .open           = sbrmi_open,
> > > +     .release        = sbrmi_release,
> > >        .unlocked_ioctl = sbrmi_ioctl,
> > >        .compat_ioctl   = compat_ptr_ioctl,
> > >   };
> > > diff --git a/drivers/misc/amd-sbi/rmi-core.h b/drivers/misc/amd-sbi/rmi-core.h
> > > index 975ae858e9fd..ae084beb6c03 100644
> > > --- a/drivers/misc/amd-sbi/rmi-core.h
> > > +++ b/drivers/misc/amd-sbi/rmi-core.h
> > > @@ -9,6 +9,7 @@
> > >   #include <linux/miscdevice.h>
> > >   #include <linux/mutex.h>
> > >   #include <linux/i2c.h>
> > > +#include <linux/kref.h>
> > >   #include <linux/platform_device.h>
> > >   #include <linux/regmap.h>
> > >   #include <uapi/misc/amd-apml.h>
> > > @@ -56,9 +57,13 @@ struct sbrmi_data {
> > >        struct regmap *regmap;
> > >        /* Mutex locking */
> > >        struct mutex lock;
> > > +     /* Reference count; keeps @sbrmi_data alive while misc fds are open */
> > > +     struct kref kref;
> > >        u32 pwr_limit_max;
> > >        u8 dev_static_addr;
> > >        u8 rev;
> > > +     /* Set on driver unbind; open/ioctl return -ENODEV afterward */
> > > +     bool detached;
> > How can open/ioctl even run if the driver is unloaded?  You aren't using
> > the cdev api properly I think...
> 
> This fix addresses driver unbind, including unbind through sysfs; it is not
> limited to module unload. misc_deregister() removes the miscdevice from the
> misc-device list and destroys its device node. Although it prevents new
> opens, it does not retain a reference to the driver-private sbrmi_data for
> files that are already open. Such files retain sbrmi_fops and private_data,
> so a later ioctl can still enter the driver.

unbind through sysfs is for developers and if things break, you get to
keep the pieces when it happens (see the discussions on lkml about the
recent taint flag added when this happens.)

So it's rare, but still, let's make this simpler by just moving the
resource management to open/release instead of probe/remove.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-28 12:26 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-26 11:39 [PATCH v1] misc: amd-sbi: Fix use-after-free on driver unbind Akshay Gupta
2026-10-26 12:14 ` Greg KH
2026-10-28 12:19   ` Gupta, Akshay
2026-10-28 12:26     ` Greg KH

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®