Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: raw-gadget: properly handle interrupted requests

Currently, if a USB request that was queued by Raw Gadget is interrupted
(via a signal), wait_for_completion_interruptible returns -ERESTARTSYS.
Raw Gadget then attempts to propagate this value to userspace as a return
value from its ioctls. However, when -ERESTARTSYS is returned by a syscall
handler, the kernel internally restarts the syscall.

This doesn't allow userspace applications to interrupt requests queued by
Raw Gadget (which is required when the emulated device is asked to switch
altsettings). It also violates the implied interface of Raw Gadget that a
single ioctl must only queue a single USB request.

Instead, make Raw Gadget do what GadgetFS does: check whether the request
was interrupted (dequeued with status == -ECONNRESET) and report -EINTR to
userspace.

Fixes: f2c2e717642c ("usb: gadget: add raw-gadget interface")
Cc: stable <stable@kernel.org>
Signed-off-by: Andrey Konovalov <andreyknvl@gmail.com>
Link: https://lore.kernel.org/r/0db45b1d7cc466e3d4d1ab353f61d63c977fbbc5.1698350424.git.andreyknvl@gmail.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Andrey Konovalov and committed by
Greg Kroah-Hartman
e8033bde 29988747

+16 -10
+16 -10
drivers/usb/gadget/legacy/raw_gadget.c
··· 674 674 if (WARN_ON(in && dev->ep0_out_pending)) { 675 675 ret = -ENODEV; 676 676 dev->state = STATE_DEV_FAILED; 677 - goto out_done; 677 + goto out_unlock; 678 678 } 679 679 if (WARN_ON(!in && dev->ep0_in_pending)) { 680 680 ret = -ENODEV; 681 681 dev->state = STATE_DEV_FAILED; 682 - goto out_done; 682 + goto out_unlock; 683 683 } 684 684 685 685 dev->req->buf = data; ··· 694 694 "fail, usb_ep_queue returned %d\n", ret); 695 695 spin_lock_irqsave(&dev->lock, flags); 696 696 dev->state = STATE_DEV_FAILED; 697 - goto out_done; 697 + goto out_queue_failed; 698 698 } 699 699 700 700 ret = wait_for_completion_interruptible(&dev->ep0_done); ··· 703 703 usb_ep_dequeue(dev->gadget->ep0, dev->req); 704 704 wait_for_completion(&dev->ep0_done); 705 705 spin_lock_irqsave(&dev->lock, flags); 706 - goto out_done; 706 + if (dev->ep0_status == -ECONNRESET) 707 + dev->ep0_status = -EINTR; 708 + goto out_interrupted; 707 709 } 708 710 709 711 spin_lock_irqsave(&dev->lock, flags); 710 - ret = dev->ep0_status; 711 712 712 - out_done: 713 + out_interrupted: 714 + ret = dev->ep0_status; 715 + out_queue_failed: 713 716 dev->ep0_urb_queued = false; 714 717 out_unlock: 715 718 spin_unlock_irqrestore(&dev->lock, flags); ··· 1081 1078 "fail, usb_ep_queue returned %d\n", ret); 1082 1079 spin_lock_irqsave(&dev->lock, flags); 1083 1080 dev->state = STATE_DEV_FAILED; 1084 - goto out_done; 1081 + goto out_queue_failed; 1085 1082 } 1086 1083 1087 1084 ret = wait_for_completion_interruptible(&done); ··· 1090 1087 usb_ep_dequeue(ep->ep, ep->req); 1091 1088 wait_for_completion(&done); 1092 1089 spin_lock_irqsave(&dev->lock, flags); 1093 - goto out_done; 1090 + if (ep->status == -ECONNRESET) 1091 + ep->status = -EINTR; 1092 + goto out_interrupted; 1094 1093 } 1095 1094 1096 1095 spin_lock_irqsave(&dev->lock, flags); 1097 - ret = ep->status; 1098 1096 1099 - out_done: 1097 + out_interrupted: 1098 + ret = ep->status; 1099 + out_queue_failed: 1100 1100 ep->urb_queued = false; 1101 1101 out_unlock: 1102 1102 spin_unlock_irqrestore(&dev->lock, flags);