| From d710562e01c48d59be3f60d58b7a85958b39aeda Mon Sep 17 00:00:00 2001 |
| From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> |
| Date: Thu, 9 Jan 2020 13:17:22 +0000 |
| Subject: usb: gadget: f_ecm: Use atomic_t to track in-flight request |
| |
| From: Bryan O'Donoghue <bryan.odonoghue@linaro.org> |
| |
| commit d710562e01c48d59be3f60d58b7a85958b39aeda upstream. |
| |
| Currently ecm->notify_req is used to flag when a request is in-flight. |
| ecm->notify_req is set to NULL and when a request completes it is |
| subsequently reset. |
| |
| This is fundamentally buggy in that the unbind logic of the ECM driver will |
| unconditionally free ecm->notify_req leading to a NULL pointer dereference. |
| |
| Fixes: da741b8c56d6 ("usb ethernet gadget: split CDC Ethernet function") |
| Cc: stable <stable@vger.kernel.org> |
| Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org> |
| Signed-off-by: Felipe Balbi <balbi@kernel.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/usb/gadget/function/f_ecm.c | 16 ++++++++++++---- |
| 1 file changed, 12 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/usb/gadget/function/f_ecm.c |
| +++ b/drivers/usb/gadget/function/f_ecm.c |
| @@ -52,6 +52,7 @@ struct f_ecm { |
| struct usb_ep *notify; |
| struct usb_request *notify_req; |
| u8 notify_state; |
| + atomic_t notify_count; |
| bool is_open; |
| |
| /* FIXME is_open needs some irq-ish locking |
| @@ -380,7 +381,7 @@ static void ecm_do_notify(struct f_ecm * |
| int status; |
| |
| /* notification already in flight? */ |
| - if (!req) |
| + if (atomic_read(&ecm->notify_count)) |
| return; |
| |
| event = req->buf; |
| @@ -420,10 +421,10 @@ static void ecm_do_notify(struct f_ecm * |
| event->bmRequestType = 0xA1; |
| event->wIndex = cpu_to_le16(ecm->ctrl_id); |
| |
| - ecm->notify_req = NULL; |
| + atomic_inc(&ecm->notify_count); |
| status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC); |
| if (status < 0) { |
| - ecm->notify_req = req; |
| + atomic_dec(&ecm->notify_count); |
| DBG(cdev, "notify --> %d\n", status); |
| } |
| } |
| @@ -448,17 +449,19 @@ static void ecm_notify_complete(struct u |
| switch (req->status) { |
| case 0: |
| /* no fault */ |
| + atomic_dec(&ecm->notify_count); |
| break; |
| case -ECONNRESET: |
| case -ESHUTDOWN: |
| + atomic_set(&ecm->notify_count, 0); |
| ecm->notify_state = ECM_NOTIFY_NONE; |
| break; |
| default: |
| DBG(cdev, "event %02x --> %d\n", |
| event->bNotificationType, req->status); |
| + atomic_dec(&ecm->notify_count); |
| break; |
| } |
| - ecm->notify_req = req; |
| ecm_do_notify(ecm); |
| } |
| |
| @@ -907,6 +910,11 @@ static void ecm_unbind(struct usb_config |
| |
| usb_free_all_descriptors(f); |
| |
| + if (atomic_read(&ecm->notify_count)) { |
| + usb_ep_dequeue(ecm->notify, ecm->notify_req); |
| + atomic_set(&ecm->notify_count, 0); |
| + } |
| + |
| kfree(ecm->notify_req->buf); |
| usb_ep_free_request(ecm->notify, ecm->notify_req); |
| } |