usb: gadget: add raw-gadget interface
Subsystems (3)
v4 cover usb: gadget: add raw-gadget interface
This patchset (currently a single patch) adds a new userspace interface for the USB Gadget subsystem called USB Raw Gadget (I don't mind changing the name to something else if there are better ideas). This is what currently being used to enable coverage-buided USB fuzzing with syzkaller:
https://github.com/google/syzkaller/blob/master/docs/linux/external_fuzzing_usb.md
Initially I was using GadgetFS (together with the Dummy HCD/UDC module) to perform emulation of USB devices for fuzzing, but later switched to a custom written interface. The incentive to implement a different interface was to provide a somewhat raw and direct access to the USB Gadget layer for the userspace, where every USB request is passed to the userspace to get a response. See documentation for the list of differences between Raw Gadget and GadgetFS.
This patchset has been pushed to the public Linux kernel Gerrit instance:
https://linux-review.googlesource.com/c/linux/kernel/git/torvalds/linux/+/2144
Changes v3 -> v4:
- Print debug message when maxpacket check fails.
- Use module_misc_device() instead of module_init/exit().
- Reuse DRIVER_NAME macro in raw_device struct definition.
- Don't print WARNING in raw_release().
- Add comment that explains locking into raw_event_queue_fetch().
- Print a WARNING when event queue size is exceeded.
- Rename raw.c to raw_gadget.c.
- Mention module name in Kconfig.
- Reworked logging to use dev_err/dbg() instead of pr_err/debug().
Changes v2 -> v3:
- Updated device path in documentation.
- Changed usb_raw_init struct layout to make it the same for 32 bit compat mode.
- Added compat_ioctl to raw_fops.
- Changed raw_ioctl_init() to return EINVAL for invalid USB speeds, except for USB_SPEED_UNKNOWN, which defaults to USB_SPEED_HIGH.
- Reject endpoints with maxpacket = 0 in raw_ioctl_ep_enable().
Changes v1 -> v2:
- Moved raw.c to legacy/.
- Changed uapi header to use __u* types.
- Switched from debugfs entry to a misc device.
- Changed raw_dev from refcount to kref.
- Moved UDC_NAME_LENGTH_MAX to uapi headers.
- Used usb_endpoint_type() and usb_endpoint_dir_in/out() functions instead of open coding them.
- Added "WITH Linux-syscall-note" to SPDX id in the uapi header.
- Removed pr_err() if case dev_new() fails.
- Reduced the number of debugging messages.
Andrey Konovalov (1):
usb: gadget: add raw-gadget interface
Documentation/usb/index.rst | 1 + Documentation/usb/raw-gadget.rst | 59 ++ drivers/usb/gadget/legacy/Kconfig | 11 + drivers/usb/gadget/legacy/Makefile | 1 + drivers/usb/gadget/legacy/raw_gadget.c | 1071 ++++++++++++++++++++++++ include/uapi/linux/usb/raw_gadget.h | 167 ++++ 6 files changed, 1310 insertions(+) create mode 100644 Documentation/usb/raw-gadget.rst create mode 100644 drivers/usb/gadget/legacy/raw_gadget.c create mode 100644 include/uapi/linux/usb/raw_gadget.h
Signature · 1 line
2.24.1.735.g03f4e72817-goog
PATCH v4 1/1 usb: gadget: add raw-gadget interface
USB Raw Gadget is a kernel module that provides a userspace interface for the USB Gadget subsystem. Essentially it allows to emulate USB devices from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is currently a strictly debugging feature and shouldn't be used in production.
Raw Gadget is similar to GadgetFS, but provides a more low-level and direct access to the USB Gadget layer for the userspace. The key differences are:
1. Every USB request is passed to the userspace to get a response, while GadgetFS responds to some USB requests internally based on the provided descriptors. However note, that the UDC driver might respond to some requests on its own and never forward them to the Gadget layer.
2. GadgetFS performs some sanity checks on the provided USB descriptors, while Raw Gadget allows you to provide arbitrary data as responses to USB requests.
3. Raw Gadget provides a way to select a UDC device/driver to bind to, while GadgetFS currently binds to the first available UDC.
4. Raw Gadget uses predictable endpoint names (handles) across different UDCs (as long as UDCs have enough endpoints of each required transfer type).
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
--- Documentation/usb/index.rst | 1 + Documentation/usb/raw-gadget.rst | 59 ++ drivers/usb/gadget/legacy/Kconfig | 11 + drivers/usb/gadget/legacy/Makefile | 1 + drivers/usb/gadget/legacy/raw_gadget.c | 1071 ++++++++++++++++++++++++ include/uapi/linux/usb/raw_gadget.h | 167 ++++ 6 files changed, 1310 insertions(+) create mode 100644 Documentation/usb/raw-gadget.rst create mode 100644 drivers/usb/gadget/legacy/raw_gadget.c create mode 100644 include/uapi/linux/usb/raw_gadget.h
Documentation/usb/index.rst +1 -0
@@ -22,6 +22,7 @@ USB support misc_usbsevseg mtouchusb ohci + raw-gadget rio usbip_protocol usbmon
Documentation/usb/raw-gadget.rst new file +59 -0
59 lines not on this page · raw message
drivers/usb/gadget/legacy/Kconfig +11 -0
@@ -489,3 +489,14 @@ config USB_G_WEBCAM Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_webcam". + +config USB_RAW_GADGET + tristate "USB Raw Gadget" + help + USB Raw Gadget is a kernel module that provides a userspace interface + for the USB Gadget subsystem. Essentially it allows to emulate USB + devices from userspace. See Documentation/usb/raw-gadget.rst for + details. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "raw_gadget".
drivers/usb/gadget/legacy/Makefile +1 -0
@@ -43,3 +43,4 @@ obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o obj-$(CONFIG_USB_G_NCM) += g_ncm.o obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o +obj-$(CONFIG_USB_RAW_GADGET) += raw_gadget.o
drivers/usb/gadget/legacy/raw_gadget.c new file +1071 -0
1,071 lines not on this page · raw message
include/uapi/linux/usb/raw_gadget.h new file +167 -0
167 lines not on this page · raw message
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Greg Kroah-Hartman
maintainerQuoted 22 lines · Andrey Konovalov
USB Raw Gadget is a kernel module that provides a userspace interface for the USB Gadget subsystem. Essentially it allows to emulate USB devices from userspace. Enabled with
CONFIG_USB_RAW_GADGET. Raw Gadget is currently a strictly debugging feature and shouldn't be used in production.Raw Gadget is similar to GadgetFS, but provides a more low-level and direct access to the USB Gadget layer for the userspace. The key differences are:
1. Every USB request is passed to the userspace to get a response, while GadgetFS responds to some USB requests internally based on the provided descriptors. However note, that the UDC driver might respond to some requests on its own and never forward them to the Gadget layer.
2. GadgetFS performs some sanity checks on the provided USB descriptors, while Raw Gadget allows you to provide arbitrary data as responses to USB requests.
3. Raw Gadget provides a way to select a UDC device/driver to bind to, while GadgetFS currently binds to the first available UDC.
Quoted 7 lines
4. Raw Gadget uses predictable endpoint names (handles) across different UDCs (as long as UDCs have enough endpoints of each required transfer type).
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
Signed-off-by Andrey Konovalov <[email protected]>
This looks good to me, with the one minor problem below that you can fix with a follow-on patch. This should probably go through Felipe's tree though, so I'll wait for him to review the gadget side and then queue it up in his tree:
include/uapi/linux/usb/raw_gadget.h line 48
Quoted 12 lines
include/uapi/linux/usb/raw_gadget.h new file +12
-0+/* The type of event fetched with the USB_RAW_IOCTL_EVENT_FETCH ioctl. */ +enum usb_raw_event_type { +USB_RAW_EVENT_INVALID , + +/* This event is queued when the driver has bound to a UDC. */ +USB_RAW_EVENT_CONNECT , + +/* This event is queued when a new control request arrived to ep0. */ +USB_RAW_EVENT_CONTROL , + +/* The list might grow in the future. */ +};
You have to manually specify the enum values in the .h file for all entries in order to assure that both userspace and the kernel will be in sync with the same values. I think that's documented in the "how to write an ioctl interface" document that is somewhere in Documentation/...
thanks,
greg k-h
Quoted 28 lines · Greg Kroah-Hartman
Quoted 26 lines · Andrey Konovalov
USB Raw Gadget is a kernel module that provides a userspace interface for the USB Gadget subsystem. Essentially it allows to emulate USB devices from userspace. Enabled with
CONFIG_USB_RAW_GADGET. Raw Gadget is currently a strictly debugging feature and shouldn't be used in production.Raw Gadget is similar to GadgetFS, but provides a more low-level and direct access to the USB Gadget layer for the userspace. The key differences are:
1. Every USB request is passed to the userspace to get a response, while GadgetFS responds to some USB requests internally based on the provided descriptors. However note, that the UDC driver might respond to some requests on its own and never forward them to the Gadget layer.
2. GadgetFS performs some sanity checks on the provided USB descriptors, while Raw Gadget allows you to provide arbitrary data as responses to USB requests.
3. Raw Gadget provides a way to select a UDC device/driver to bind to, while GadgetFS currently binds to the first available UDC.
4. Raw Gadget uses predictable endpoint names (handles) across different UDCs (as long as UDCs have enough endpoints of each required transfer type).
Quoted 6 lines
Quoted 3 lines
5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
Signed-off-by Andrey Konovalov <[email protected]>This looks good to me, with the one minor problem below that you can fix with a follow-on patch.
Great, thanks! I'd prefer to send out v5 to keep this a single patch if that's OK.
I've also found an issue, but I'm not sure if that is the bug in Raw Gadget, or in the gadget layer (in the former case I'll add this fix
to v5 as well). What I believe I'm seeing is __fput()->usb_gadget_unregister_driver()->usb_gadget_remove_driver()->gadget_unbind() racing with dummy_timer()->gadget_setup(). In my case it results in gadget_unbind() doing set_gadget_data(gadget, NULL), and then gadget_setup() dereferencing get_gadget_data(gadget).
Alan, does it look possible for those two functions to race? Should this be prevented by the gadget layer, or should I use some kind of locking in my gadget driver to prevent this?
Quoted 3 lines
This should probably go through Felipe's tree
though, so I'll wait for him to review the gadget side and then queue it up in his tree:
OK, looking forward to Felipe's review.
Quoted 10 lines
Quoted 10 lines
include/uapi/linux/usb/raw_gadget.h new file +10
-0+/* The type of event fetched with the USB_RAW_IOCTL_EVENT_FETCH ioctl. */ +enum usb_raw_event_type { +USB_RAW_EVENT_INVALID , + +/* This event is queued when the driver has bound to a UDC. */ +USB_RAW_EVENT_CONNECT , + +/* This event is queued when a new control request arrived to ep0. */ +USB_RAW_EVENT_CONTROL , +
Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ /* The list might grow in the future. */ +};You have to manually specify the enum values in the .h file for all entries in order to assure that both userspace and the kernel will be in sync with the same values. I think that's documented in the "how to write an ioctl interface" document that is somewhere in
Documentation/...
Will fix in v5, thanks!
Quoted 11 lines · Andrey Konovalov
I've also found an issue, but I'm not sure if that is the bug in Raw Gadget, or in the gadget layer (in the former case I'll add this fix
to v5 as well). What I believe I'm seeing is __fput()->usb_gadget_unregister_driver()->usb_gadget_remove_driver()->gadget_unbind() racing with dummy_timer()->gadget_setup(). In my case it results ingadget_unbind()doingset_gadget_data(gadget, NULL), and thengadget_setup()dereferencingget_gadget_data(gadget).Alan, does it look possible for those two functions to race? Should this be prevented by the gadget layer, or should I use some kind of locking in my gadget driver to prevent this?
In your situation this race shouldn't happen, because before udc->driver->unbind() is invoked we call usb_gadget_disconnect(). If that routine succeeds -- which it always does under dummy-hcd -- then there can't be any more setup callbacks, because find_endpoint() will always return NULL (the is_active() test fails; see the various set_link_state* routines). So I don't see how you could have ended up with the race you describe.
However, a real UDC might not be able to perform a disconnect under software control. In that case usb_gadget_disconnect() would not change the pullup state, and there would be a real possibility of a setup callback racing with an unbind callback. This seems like a genuine problem and I can't think of a solution offhand.
What we would need is a way to tell the UDC driver to stop invoking gadget callbacks, _before_ the UDC driver's stop callback gets called. Maybe this should be merged into the pullup callback somehow.
Alan Stern
Quoted 15 lines · Alan Stern
Quoted 11 lines · Andrey Konovalov
I've also found an issue, but I'm not sure if that is the bug in Raw Gadget, or in the gadget layer (in the former case I'll add this fix
to v5 as well). What I believe I'm seeing is __fput()->usb_gadget_unregister_driver()->usb_gadget_remove_driver()->gadget_unbind() racing with dummy_timer()->gadget_setup(). In my case it results ingadget_unbind()doingset_gadget_data(gadget, NULL), and thengadget_setup()dereferencingget_gadget_data(gadget).Alan, does it look possible for those two functions to race? Should this be prevented by the gadget layer, or should I use some kind of locking in my gadget driver to prevent this?
Quoted 7 lines
In your situation this race shouldn't happen, because before
udc->driver->unbind()is invoked we callusb_gadget_disconnect(). If that routine succeeds -- which it always does under dummy-hcd -- then there can't be any more setup callbacks, becausefind_endpoint()will always return NULL (theis_active()test fails; see the various set_link_state* routines). So I don't see how you could have ended up with the race you describe.
I've managed to reproduce the race by adding an mdelay() into the beginning of the setup() callback. AFAIU what happens is setup() gets called (and waits on the mdelay()), then unbind() comes in and does set_gadget_data(NULL), and then setup() proceeds, gets NULL through get_gadget_data() and crashes on null-ptr-deref. I've got the same crash a few times after many days of fuzzing, so I assume it can happen without the mdelay() as well.
Quoted 12 lines
However, a real UDC might not be able to perform a disconnect under software control. In that case
usb_gadget_disconnect()would not change the pullup state, and there would be a real possibility of a setup callback racing with an unbind callback. This seems like a genuine problem and I can't think of a solution offhand.What we would need is a way to tell the UDC driver to stop invoking gadget callbacks, _before_ the UDC driver's stop callback gets called. Maybe this should be merged into the pullup callback somehow.
Alan Stern
Quoted 25 lines · Andrey Konovalov
Quoted 15 lines · Alan Stern
Quoted 11 lines · Andrey Konovalov
I've also found an issue, but I'm not sure if that is the bug in Raw Gadget, or in the gadget layer (in the former case I'll add this fix
to v5 as well). What I believe I'm seeing is __fput()->usb_gadget_unregister_driver()->usb_gadget_remove_driver()->gadget_unbind() racing with dummy_timer()->gadget_setup(). In my case it results ingadget_unbind()doingset_gadget_data(gadget, NULL), and thengadget_setup()dereferencingget_gadget_data(gadget).Alan, does it look possible for those two functions to race? Should this be prevented by the gadget layer, or should I use some kind of locking in my gadget driver to prevent this?
Quoted 7 lines
In your situation this race shouldn't happen, because before
udc->driver->unbind()is invoked we callusb_gadget_disconnect(). If that routine succeeds -- which it always does under dummy-hcd -- then there can't be any more setup callbacks, becausefind_endpoint()will always return NULL (theis_active()test fails; see the various set_link_state* routines). So I don't see how you could have ended up with the race you describe.
Quoted 17 lines
I've managed to reproduce the race by adding an
mdelay()into the beginning of thesetup()callback. AFAIU what happens issetup()gets called (and waits on themdelay()), thenunbind()comes in and doesset_gadget_data(NULL), and thensetup()proceeds, gets NULL throughget_gadget_data()and crashes on null-ptr-deref. I've got the same crash a few times after many days of fuzzing, so I assume it can happen without themdelay()as well.Quoted 9 lines
However, a real UDC might not be able to perform a disconnect under software control. In that case
usb_gadget_disconnect()would not change the pullup state, and there would be a real possibility of a setup callback racing with an unbind callback. This seems like a genuine problem and I can't think of a solution offhand.What we would need is a way to tell the UDC driver to stop invoking gadget callbacks, _before_ the UDC driver's stop callback gets called. Maybe this should be merged into the pullup callback somehow.
Perhaps for the dummy driver we need to wait for setup() to finish if it's being executed and then stop the dummy timer in dummy_pullup()?
Quoted 40 lines · Andrey Konovalov
Quoted 39 lines · Andrey Konovalov
Quoted 15 lines · Alan Stern
Quoted 11 lines · Andrey Konovalov
I've also found an issue, but I'm not sure if that is the bug in Raw Gadget, or in the gadget layer (in the former case I'll add this fix
to v5 as well). What I believe I'm seeing is __fput()->usb_gadget_unregister_driver()->usb_gadget_remove_driver()->gadget_unbind() racing with dummy_timer()->gadget_setup(). In my case it results ingadget_unbind()doingset_gadget_data(gadget, NULL), and thengadget_setup()dereferencingget_gadget_data(gadget).Alan, does it look possible for those two functions to race? Should this be prevented by the gadget layer, or should I use some kind of locking in my gadget driver to prevent this?
Quoted 7 lines
In your situation this race shouldn't happen, because before
udc->driver->unbind()is invoked we callusb_gadget_disconnect(). If that routine succeeds -- which it always does under dummy-hcd -- then there can't be any more setup callbacks, becausefind_endpoint()will always return NULL (theis_active()test fails; see the various set_link_state* routines). So I don't see how you could have ended up with the race you describe.I've managed to reproduce the race by adding an
mdelay()into the beginning of thesetup()callback. AFAIU what happens issetup()gets called (and waits on themdelay()), thenunbind()comes in and doesset_gadget_data(NULL), and thensetup()proceeds, gets NULL throughget_gadget_data()and crashes on null-ptr-deref. I've got the same crash a few times after many days of fuzzing, so I assume it can happen without themdelay()as well.Quoted 6 lines
However, a real UDC might not be able to perform a disconnect under software control. In that case
usb_gadget_disconnect()would not change the pullup state, and there would be a real possibility of a setup callback racing with an unbind callback. This seems like a genuine problem and I can't think of a solution offhand.
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
What we would need is a way to tell the UDC driver to stop invoking gadget callbacks, _before_ the UDC driver's stop callback gets called. Maybe this should be merged into the pullup callback somehow.
Perhaps for the dummy driver we need to wait for
setup()to finish if it's being executed and then stop the dummy timer indummy_pullup()?
Yes, we need to wait for a setup callback to finish. But dummy_timer should not be stopped; otherwise URBs that have already been submitted would never be given back.
The big problem is that usb_gadget_disconnect() can be called in interrupt context. In general, a UDC driver will need to call synchronize_irq() to make sure there aren't any callbacks still running, and that can be done only in process context. dummy-hcd is slightly different since it doesn't manage actual hardware; it calls usleep_range() instead of synchronize_irq() -- but that also requires process context.
We could change the gadget API to require that usb_gadget_disconnect() and related routines always be called in process context. I don't know if that's such a good idea though. A gadget driver might want to disconnect from within its setup handler or a completion routine, for example.
A better approach would be to add a new synchronize_callbacks() pointer in the usb_gadget_ops structure. But to work properly it would have to be mandatory for every UDC driver, and that's not so easy to accomplish.
Suggestions?
Alan Stern
Quoted 16 lines · Andrey Konovalov
This patchset (currently a single patch) adds a new userspace interface for the USB Gadget subsystem called USB Raw Gadget (I don't mind changing the name to something else if there are better ideas). This is what currently being used to enable coverage-buided USB fuzzing with syzkaller:
https://github.com/google/syzkaller/blob/master/docs/linux/external_fuzzing_usb.md
Initially I was using GadgetFS (together with the Dummy HCD/UDC module) to perform emulation of USB devices for fuzzing, but later switched to a custom written interface. The incentive to implement a different interface was to provide a somewhat raw and direct access to the USB Gadget layer for the userspace, where every USB request is passed to the userspace to get a response. See documentation for the list of differences between Raw Gadget and GadgetFS.
Quoted 3 lines
This patchset has been pushed to the public Linux kernel Gerrit instance:
https://linux-review.googlesource.com/c/linux/kernel/git/torvalds/linux/+/2144
Hi Greg,
Just an after holidays reminder that I've sent v4 of this patchset and looking forward to a review.
Thanks!
Quoted 50 lines
Changes v3 -> v4:
- Print debug message when maxpacket check fails.
- Usemodule_misc_device()instead of module_init/exit().
- ReuseDRIVER_NAMEmacro inraw_devicestruct definition.
- Don't print WARNING inraw_release().
- Add comment that explains locking intoraw_event_queue_fetch().
- Print a WARNING when event queue size is exceeded.
- Renameraw.ctoraw_gadget.c.
- Mention module name inKconfig.
- Reworked logging to use dev_err/dbg() instead of pr_err/debug().Changes v2 -> v3:
- Updated device path in documentation.
- Changedusb_raw_initstruct layout to make it the same for 32 bit compat mode.
- Addedcompat_ioctltoraw_fops.
- Changedraw_ioctl_init()to return EINVAL for invalid USB speeds, except forUSB_SPEED_UNKNOWN, which defaults toUSB_SPEED_HIGH.
- Reject endpoints with maxpacket = 0 inraw_ioctl_ep_enable().Changes v1 -> v2:
- Movedraw.cto legacy/.
- Changed uapi header to use __u* types.
- Switched from debugfs entry to a misc device.
- Changedraw_devfrom refcount to kref.
- MovedUDC_NAME_LENGTH_MAXto uapi headers.
- Usedusb_endpoint_type()and usb_endpoint_dir_in/out() functions instead of open coding them.
- Added "WITH Linux-syscall-note" to SPDX id in the uapi header.
- Removedpr_err()if casedev_new()fails.
- Reduced the number of debugging messages.Andrey Konovalov (1):
usb: gadget: add raw-gadget interfaceDocumentation/usb/index.rst | 1 + Documentation/usb/raw-gadget.rst | 59 ++ drivers/usb/gadget/legacy/Kconfig | 11 + drivers/usb/gadget/legacy/Makefile | 1 + drivers/usb/gadget/legacy/raw_gadget.c | 1071 ++++++++++++++++++++++++ include/uapi/linux/usb/raw_gadget.h | 167 ++++ 6 files changed, 1310 insertions(+) create mode 100644 Documentation/usb/raw-gadget.rst create mode 100644 drivers/usb/gadget/legacy/raw_gadget.c create mode 100644 include/uapi/linux/usb/raw_gadget.h--
2.24.1.735.g03f4e72817-goog