[PATCH] usb: gadget: f_loopback: fix descriptor leak on unbind

Chaithanya Lagisetty <[email protected]>
Newsgroups org.kernel.vger.linux-kernel,org.kernel.vger.linux-usb
Message-ID <[email protected]>
loopback_bind() allocates descriptor copies through
usb_assign_descriptors(), but f_loopback does not release them during
the unbind path. On every bind/unbind cycle of the gadget (for example
by repeatedly writing the UDC attribute through configfs) a new set of
descriptors is allocated while the previous ones are leaked. syzbot
reported this via kmemleak:

  BUG: memory leak
  unreferenced object 0xffff888016b8f180 (size 64):
    comm "repro", pid 5613
    backtrace:
      __kmalloc_noprof+0x3bf/0x550
      usb_copy_descriptors+0x6c/0x160
      usb_assign_descriptors+0x48/0x180
      loopback_bind+0xff/0x120
      usb_add_function+0xca/0x270
      configfs_composite_bind+0x667/0x9b0
      gadget_bind_driver+0xed/0x390

Move descriptor cleanup to a new loopback_unbind() callback that frees
them with usb_free_all_descriptors(), matching the lifecycle used by
other gadget functions such as f_acm. With descriptors released during
unbind, the usb_free_all_descriptors() call in lb_free_func() becomes
redundant and can be removed.

Fixes: 10287baec761 ("usb: gadget: always update HS/SS descriptors and create a copy of them")
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=28cf08dec5895bd562e6
Signed-off-by: Chaithanya Lagisetty <[email protected]>
---
 drivers/usb/gadget/function/f_loopback.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index d2d07fb49e70..40aaf2eb00f2 100644
--- a/drivers/usb/gadget/function/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
@@ -216,6 +216,11 @@ static int loopback_bind(struct usb_configuration *c, struct usb_function *f)
 	return 0;
 }
 
+static void loopback_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	usb_free_all_descriptors(f);
+}
+
 static void lb_free_func(struct usb_function *f)
 {
 	struct f_lb_opts *opts;
@@ -226,7 +231,6 @@ static void lb_free_func(struct usb_function *f)
 	opts->refcnt--;
 	mutex_unlock(&opts->lock);
 
-	usb_free_all_descriptors(f);
 	kfree(func_to_loop(f));
 }
 
@@ -442,6 +446,7 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
 
 	loop->function.name = "loopback";
 	loop->function.bind = loopback_bind;
+	loop->function.unbind = loopback_unbind;
 	loop->function.set_alt = loopback_set_alt;
 	loop->function.disable = loopback_disable;
 	loop->function.strings = loopback_strings;
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.