linux-usb.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/1] usb: gadget: f_hid: Conduct proper refcounting on shared f_hidg pointer
@ 2022-11-17 12:08 Lee Jones
  2022-11-17 12:50 ` Greg KH
  2022-11-17 12:50 ` Greg KH
  0 siblings, 2 replies; 24+ messages in thread
From: Lee Jones @ 2022-11-17 12:08 UTC (permalink / raw)
  To: lee, balbi, gregkh; +Cc: linux-kernel, linux-usb

A reference to struct f_hidg is shared with this driver's associated
character device handling component without provision for life-time
handling.  In some circumstances, this can lead to unwanted
behaviour depending on the order in which things are torn down.

Utilise, the reference counting functionality already provided by the
implanted character device structure to ensure the struct f_hidg's
memory is only freed once the character device handling has finished
with it.

Signed-off-by: Lee Jones <lee@kernel.org>
---
 drivers/usb/gadget/function/f_hid.c | 47 +++++++++++++++++++++++------
 1 file changed, 37 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index ca0a7d9eaa34e..5da8f44d47d9d 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -77,6 +77,8 @@ struct f_hidg {
 
 	struct usb_ep			*in_ep;
 	struct usb_ep			*out_ep;
+
+	bool				gc;
 };
 
 static inline struct f_hidg *func_to_hidg(struct usb_function *f)
@@ -84,6 +86,11 @@ static inline struct f_hidg *func_to_hidg(struct usb_function *f)
 	return container_of(f, struct f_hidg, func);
 }
 
+static inline bool f_hidg_is_open(struct f_hidg *hidg)
+{
+	return !!kref_read(&hidg->cdev.kobj.kref);
+}
+
 /*-------------------------------------------------------------------------*/
 /*                           Static descriptors                            */
 
@@ -273,6 +280,18 @@ static struct usb_gadget_strings *ct_func_strings[] = {
 	NULL,
 };
 
+static void hidg_free_resources(struct f_hidg *hidg)
+{
+       struct f_hid_opts *opts = container_of(hidg->func.fi, struct f_hid_opts, func_inst);
+
+       mutex_lock(&opts->lock);
+       kfree(hidg->report_desc);
+       kfree(hidg->set_report_buf);
+       kfree(hidg);
+       --opts->refcnt;
+       mutex_unlock(&opts->lock);
+}
+
 /*-------------------------------------------------------------------------*/
 /*                              Char Device                                */
 
@@ -539,7 +558,16 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
 
 static int f_hidg_release(struct inode *inode, struct file *fd)
 {
+	struct f_hidg *hidg  = fd->private_data;
+
+	if (hidg->gc) {
+		/* Gadget has already been disconnected and we are the last f_hidg user */
+		cdev_del(&hidg->cdev);
+		hidg_free_resources(hidg);
+	}
+
 	fd->private_data = NULL;
+
 	return 0;
 }
 
@@ -1239,17 +1267,16 @@ static struct usb_function_instance *hidg_alloc_inst(void)
 
 static void hidg_free(struct usb_function *f)
 {
-	struct f_hidg *hidg;
-	struct f_hid_opts *opts;
+	struct f_hidg *hidg = func_to_hidg(f);
 
-	hidg = func_to_hidg(f);
-	opts = container_of(f->fi, struct f_hid_opts, func_inst);
-	kfree(hidg->report_desc);
-	kfree(hidg->set_report_buf);
-	kfree(hidg);
-	mutex_lock(&opts->lock);
-	--opts->refcnt;
-	mutex_unlock(&opts->lock);
+	if (f_hidg_is_open(hidg))
+		/*
+		 * Gadget disconnected whilst an open dev node exists.
+		 * Delay freeing resources until it closes.
+		 */
+		hidg->gc = true;
+	else
+		hidg_free_resources(hidg);
 }
 
 static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
-- 
2.38.1.431.g37b22c650d-goog


^ permalink raw reply related	[flat|nested] 24+ messages in thread
* [PATCH 1/1] usb: gadget: f_hid: Conduct proper refcounting on shared f_hidg pointer
@ 2022-10-17 11:27 Lee Jones
  2022-10-22 12:52 ` Greg Kroah-Hartman
  0 siblings, 1 reply; 24+ messages in thread
From: Lee Jones @ 2022-10-17 11:27 UTC (permalink / raw)
  To: lee; +Cc: linux-kernel, Felipe Balbi, Greg Kroah-Hartman, linux-usb

A pointer to the main HID gadget struct (*f_hidg) is shared with the
character device handing (read() and write(), etc support) on open().
However external references are presently not tracked.  This could
easily lead to some unsavoury behaviour if gadget support is disabled
/ disconnected.  Keeping track of the refcount ensures that resources
are only freed *after* they are no longer in use.

Cc: Felipe Balbi <balbi@kernel.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: linux-usb@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
 drivers/usb/gadget/function/f_hid.c | 37 +++++++++++++++++++++--------
 1 file changed, 27 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index ca0a7d9eaa34e..79d4ee8a5647f 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/hid.h>
 #include <linux/idr.h>
+#include <linux/kref.h>
 #include <linux/cdev.h>
 #include <linux/mutex.h>
 #include <linux/poll.h>
@@ -77,6 +78,8 @@ struct f_hidg {
 
 	struct usb_ep			*in_ep;
 	struct usb_ep			*out_ep;
+
+	struct kref			refcount;
 };
 
 static inline struct f_hidg *func_to_hidg(struct usb_function *f)
@@ -273,6 +276,19 @@ static struct usb_gadget_strings *ct_func_strings[] = {
 	NULL,
 };
 
+static void hidg_free_resources(struct kref *ref)
+{
+	struct f_hidg *hidg = container_of(ref, struct f_hidg, refcount);
+	struct f_hid_opts *opts = container_of(hidg->func.fi, struct f_hid_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	kfree(hidg->report_desc);
+	kfree(hidg->set_report_buf);
+	kfree(hidg);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
 /*-------------------------------------------------------------------------*/
 /*                              Char Device                                */
 
@@ -539,7 +555,13 @@ static __poll_t f_hidg_poll(struct file *file, poll_table *wait)
 
 static int f_hidg_release(struct inode *inode, struct file *fd)
 {
+	struct f_hidg *hidg =
+		container_of(inode->i_cdev, struct f_hidg, cdev);
+
 	fd->private_data = NULL;
+
+	kref_put(&hidg->refcount, hidg_free_resources);
+
 	return 0;
 }
 
@@ -548,6 +570,8 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
 	struct f_hidg *hidg =
 		container_of(inode->i_cdev, struct f_hidg, cdev);
 
+	kref_get(&hidg->refcount);
+
 	fd->private_data = hidg;
 
 	return 0;
@@ -1239,17 +1263,9 @@ static struct usb_function_instance *hidg_alloc_inst(void)
 
 static void hidg_free(struct usb_function *f)
 {
-	struct f_hidg *hidg;
-	struct f_hid_opts *opts;
+	struct f_hidg *hidg = func_to_hidg(f);
 
-	hidg = func_to_hidg(f);
-	opts = container_of(f->fi, struct f_hid_opts, func_inst);
-	kfree(hidg->report_desc);
-	kfree(hidg->set_report_buf);
-	kfree(hidg);
-	mutex_lock(&opts->lock);
-	--opts->refcnt;
-	mutex_unlock(&opts->lock);
+	kref_put(&hidg->refcount, hidg_free_resources);
 }
 
 static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
@@ -1271,6 +1287,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
 	hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
 	if (!hidg)
 		return ERR_PTR(-ENOMEM);
+	kref_init(&hidg->refcount);
 
 	opts = container_of(fi, struct f_hid_opts, func_inst);
 
-- 
2.38.0.413.g74048e4d9e-goog


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

end of thread, other threads:[~2022-11-22 11:55 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2022-11-17 12:08 [PATCH 1/1] usb: gadget: f_hid: Conduct proper refcounting on shared f_hidg pointer Lee Jones
2022-11-17 12:50 ` Greg KH
2022-11-17 13:26   ` Lee Jones
2022-11-17 17:38     ` Greg KH
2022-11-17 12:50 ` Greg KH
2022-11-17 13:46   ` Lee Jones
2022-11-17 16:47     ` Alan Stern
2022-11-18  8:54       ` Lee Jones
2022-11-18 15:59         ` Alan Stern
2022-11-18 16:37           ` John Keeping
2022-11-18 21:07             ` Alan Stern
2022-11-20 17:22               ` John Keeping
2022-11-20 20:46                 ` Alan Stern
2022-11-21 12:30                   ` Lee Jones
2022-11-21 12:38                   ` John Keeping
2022-11-21 16:18                     ` Alan Stern
2022-11-21 18:54                       ` John Keeping
2022-11-21 19:17                         ` Alan Stern
2022-11-22 11:52                           ` John Keeping
2022-11-22  8:31                         ` Lee Jones
2022-11-22 11:55                           ` John Keeping
  -- strict thread matches above, loose matches on Subject: below --
2022-10-17 11:27 Lee Jones
2022-10-22 12:52 ` Greg Kroah-Hartman
2022-10-24  7:17   ` Lee Jones

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).