Patch "usb: gadget: f_hid: fix: Move IN request allocation to set_alt()" has been added to the 4.9-stable tree
Subsystems (2)
This is a note to let you know that I've just added the patch titled
usb: gadget: f_hid: fix: Move IN request allocation to set_alt()
to the 4.9-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
usb-gadget-f_hid-fix-move-in-request-allocation-to-set_alt.patch
and it can be found in the queue-4.9 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
749494b6bdbbaf0899aa1c62a1ad74cd747bce47Mon Sep 17 00:00:00 2001
From: Krzysztof Opasiak <[email protected]>
Date: Tue, 24 Jan 2017 03:27:24 +0100
Subject: usb: gadget: f_hid: fix: Move IN request allocation to set_alt()
From: Krzysztof Opasiak <[email protected]>
commit 749494b6bdbbaf0899aa1c62a1ad74cd747bce47 upstream.
Since commit: ba1582f22231 ("usb: gadget: f_hid: use alloc_ep_req()") we cannot allocate any requests in bind() as we check if we should align request buffer based on endpoint descriptor which is assigned in set_alt().
Allocating request in bind() function causes a NULL pointer dereference.
This commit moves allocation of IN request from bind() to set_alt() to prevent this issue.
--- drivers/usb/gadget/function/f_hid.c | 89 +++++++++++++++++++++++++++--------- 1 file changed, 67 insertions(+), 22 deletions(-)
drivers/usb/gadget/function/f_hid.c +67 -22
@@ -284,6 +284,7 @@ static ssize_t f_hidg_write(struct file size_t count, loff_t *offp) { struct f_hidg *hidg = file->private_data; +struct usb_request *req;unsigned long flags;ssize_t status = -ENOMEM ;
@@ -293,7 +294,7 @@ static ssize_t f_hidg_write(struct filespin_lock_irqsave (&hidg->write_spinlock, flags);#define WRITE_COND (!hidg->write_pending) - +try_again:/* write queue */ while (!WRITE_COND ) {spin_unlock_irqrestore (&hidg->write_spinlock, flags);
@@ -308,6 +309,7 @@ static ssize_t f_hidg_write(struct file } hidg->write_pending =1 ; + req = hidg->req; count =min_t (unsigned, count, hidg->report_length);spin_unlock_irqrestore (&hidg->write_spinlock, flags);
@@ -320,24 +322,38 @@ static ssize_t f_hidg_write(struct file goto release_write_pending; } - hidg->req->status =0 ; - hidg->req->zero =0 ; - hidg->req->length = count; - hidg->req->complete = f_hidg_req_complete; - hidg->req->context = hidg; +spin_lock_irqsave (&hidg->write_spinlock, flags); + +/* we our function has been disabled by host */ +if (!hidg->req) { +free_ep_req (hidg->in_ep, hidg->req); +/* +* TODO +* Should we fail with error here? +*/ + goto try_again; + } + + req->status =0 ; + req->zero =0 ; + req->length = count; + req->complete = f_hidg_req_complete; + req->context = hidg; status =usb_ep_queue (hidg->in_ep, hidg->req,GFP_ATOMIC );if (status <0 ) {ERROR (hidg->func.config->cdev,"usb_ep_queue error on int endpoint %zd\n" , status); - goto release_write_pending; + goto release_write_pending_unlocked; }else { status = count; } +spin_unlock_irqrestore (&hidg->write_spinlock, flags);return status; release_write_pending:spin_lock_irqsave (&hidg->write_spinlock, flags); +release_write_pending_unlocked: hidg->write_pending =0 ;spin_unlock_irqrestore (&hidg->write_spinlock, flags);
@@ -541,12 +557,23 @@ static void hidg_disable(struct usb_funckfree (list); }spin_unlock_irqrestore (&hidg->read_spinlock, flags); + +spin_lock_irqsave (&hidg->write_spinlock, flags); +if (!hidg->write_pending) { +free_ep_req (hidg->in_ep, hidg->req); + hidg->write_pending =1 ; + } + + hidg->req =NULL ; +spin_unlock_irqrestore (&hidg->write_spinlock, flags); }static int hidg_set_alt (struct usb_function *f,unsigned intf,unsigned alt) {struct usb_composite_dev *cdev = f->config->cdev;struct f_hidg *hidg =func_to_hidg (f); +struct usb_request *req_in =NULL ; +unsigned long flags;int i, status =0 ;VDBG (cdev,"hidg_set_alt intf:%d alt:%d\n" , intf, alt);
@@ -567,6 +594,12 @@ static int hidg_set_alt(struct usb_funct goto fail; } hidg->in_ep->driver_data = hidg; + + req_in =hidg_alloc_ep_req (hidg->in_ep, hidg->report_length); +if (!req_in) { + status = -ENOMEM ; + goto disable_ep_in; + } }
@@ -578,12 +611,12 @@ static int hidg_set_alt(struct usb_funct hidg->out_ep);if (status) {ERROR (cdev,"config_ep_by_speed FAILED!\n" ); - goto fail; + goto free_req_in; } status =usb_ep_enable (hidg->out_ep);if (status <0 ) {ERROR (cdev,"Enable OUT endpoint FAILED!\n" ); - goto fail; + goto free_req_in; } hidg->out_ep->driver_data = hidg;
@@ -599,17 +632,37 @@ static int hidg_set_alt(struct usb_funct req->context = hidg; status =usb_ep_queue (hidg->out_ep, req,GFP_ATOMIC ); -if (status) +if (status) {ERROR (cdev,"%s queue req --> %d\n" , hidg->out_ep->name, status); +free_ep_req (hidg->out_ep, req); + } }else { -usb_ep_disable (hidg->out_ep); status = -ENOMEM ; - goto fail; + goto disable_out_ep; } } } +if (hidg->in_ep !=NULL ) { +spin_lock_irqsave (&hidg->write_spinlock, flags); + hidg->req = req_in; + hidg->write_pending =0 ; +spin_unlock_irqrestore (&hidg->write_spinlock, flags); + +wake_up (&hidg->write_queue); + } +return 0 ; +disable_out_ep: +usb_ep_disable (hidg->out_ep); +free_req_in: +if (req_in) +free_ep_req (hidg->in_ep, req_in); + +disable_ep_in: +if (hidg->in_ep) +usb_ep_disable (hidg->in_ep); + fail:return status; }
@@ -658,12 +711,6 @@ static int hidg_bind(struct usb_configur goto fail; hidg->out_ep = ep; -/* preallocate request and buffer */ - status = -ENOMEM ; - hidg->req =alloc_ep_req (hidg->in_ep, hidg->report_length); -if (!hidg->req) - goto fail; -/* set descriptor dynamic values */ hidg_interface_desc.bInterfaceSubClass = hidg->bInterfaceSubClass; hidg_interface_desc.bInterfaceProtocol = hidg->bInterfaceProtocol;
@@ -690,6 +737,8 @@ static int hidg_bind(struct usb_configur goto fail;spin_lock_init (&hidg->write_spinlock); + hidg->write_pending =1 ; + hidg->req =NULL ;spin_lock_init (&hidg->read_spinlock);init_waitqueue_head (&hidg->write_queue);init_waitqueue_head (&hidg->read_queue);
@@ -954,10 +1003,6 @@ static void hidg_unbind(struct usb_confidevice_destroy (hidg_class,MKDEV (major, hidg->minor));cdev_del (&hidg->cdev); -/* disable/free request and end point */ -usb_ep_disable (hidg->in_ep); -free_ep_req (hidg->in_ep, hidg->req); -usb_free_all_descriptors (f); }
Patches currently in stable-queue which might be from [email protected] are
queue-4.9/usb-gadget-f_hid-fix-move-in-request-allocation-to-set_alt.patch