greatscottgadgets / cynthion

USB test instrument
https://greatscottgadgets.com/cynthion/
BSD 3-Clause "New" or "Revised" License
86 stars 20 forks source link

Failing to proxy device with large configuration descriptor #162

Open xairy opened 3 months ago

xairy commented 3 months ago

With this fix applied, proxying a Logitech Webcam C110, which has a large configuration descriptor, fails with:

...
INFO    | logging        | [16:21:55] USBControlRequest(direction=1, type=0, recipient=0, number=6, value=512, index=0, length=1132, data=bytearray(b''), device=USBProxyDevice(name='generic device', device_class=0, device_subclass=0, protocol_revision_number=0, max_packet_size_ep0=64, vendor_id=24843, product_id=18003, manufacturer_string='Facedancer', product_string='Generic USB Device', serial_number_string='S/N 3420E', supported_languages=(<LanguageIDs.ENGLISH_US: 1033>,), device_revision=0, usb_spec_version=2, device_speed=None, descriptors={<DescriptorTypes.DEVICE: 1>: <function USBBaseDevice.__post_init__.<locals>.<lambda> at 0x7f07bd3e7ee0>, <DescriptorTypes.CONFIGURATION: 2>: <bound method USBBaseDevice.get_configuration_descriptor of ...>, <DescriptorTypes.STRING: 3>: <bound method USBBaseDevice.get_string_descriptor of ...>}, configurations={}, backend=<facedancer.backends.moondancer.MoondancerApp object at 0x7f07bc6347f0>))
INFO    | logging        | [16:21:55] <: b'\t\x02l\x04\x04\x01\x00\x80\xfa\x08\x0b\x00\x02\x0e\x03\x00\x04\t\x04\x00\x00\x01\x0e\x01\x00\x04\r$\x01\x00\x01O\x00\x80\xc3\xc9\x01\x01\x01\x1c$\x06\x06\xb0\xd0\xbbh\xa4a\x83K\x90\xb7\xa6!_<Op\x18\x01\x02\x03\xff\xff\xff\x00\x12$\x02\x01\x01\x02\x00\x00\x00\x00\x00\x00\x00\x00\x03\x0e\n\x00\x0b$\x05\x02\x01\x00\x00\x02\x7f\x15\x00\t$\x03\x03\x01\x01\x00\x02\x00\x07\x05\x81\x03\x10\x00\x07\x05%\x03\x10\x00\t\x04\x01\x00\x00\x0e\x02\x00\x04\x0f$\x01\x02\xe9\x02\x82\x00\x03\x01\x01\x00\x01\x00\x00\x1b$\x04\x01\tYUY2\x00\x00\x10\x00\x80\x00\x00\xaa\x008\x9bq\x10\x01\x00\x00\x00\x00"$\x05\x01\x01\x80\x02\xe0\x01\x00\x00e\x04\x00\x00\xca\x08\x00`\t\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\x02\x01`\x01 \x01\x00@s\x01\x00\x80\xe6\x02\x00\x18\x03\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\x03\x01@\x01\xf0\x00\x00@\x19\x01\x00\x802\x02\x00X\x02\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\x04\x01\xb0\x00\x90\x00\x00\xd0\\\x00\x00\xa0\xb9\x00\x00\xc6\x00\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\x05\x01\xa0\x00x\x00\x00PF\x00\x00\xa0\x8c\x00\x00\x96\x00\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\x06\x01 \x02 \x01\x00\xc0=\x02\x00\x80{\x04\x00\xc8\x04\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\x07\x01\xb0\x01\xf0\x00\x00\xb0{\x01\x00`\xf7\x02\x00*\x03\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\x08\x01@\x01\xb0\x00\x00@\xce\x00\x00\x80\x9c\x01\x00\xb8\x01\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x05\t\x01\x80\x02h\x01\x00\xc0K\x03\x00\x80\x97\x06\x00\x08\x07\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00\x06$\r\x01\x01\x04\x0b$\x06\x02\x0b\x01\x01\x00\x00\x00\x00"$\x07\x01\x01\x80\x02\xe0\x01\x00\x80\x97\x06\x00\x00/\r\x00\x10\x0e\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x02\x01`\x01 \x01\x00\xe0,\x02\x00\xc0Y\x04\x00\xa4\x04\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x03\x01@\x01\xf0\x00\x00\xe0\xa5\x01\x00\xc0K\x03\x00\x84\x03\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x04\x01\xb0\x00\x90\x00\x008\x8b\x00\x00p\x16\x01\x00)\x01\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x05\x01\xa0\x00x\x00\x00xi\x00\x00\xf0\xd2\x00\x00\xe1\x00\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x06\x01 \x02 \x01\x00\xa0\\\x03\x00@\xb9\x06\x00,\x07\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x07\x01\xb0\x01\xf0\x00\x00\x889\x02\x00\x10s\x04\x00\xbf\x04\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x08\x01@\x01\xb0\x00\x00`5\x01\x00\xc0j\x02\x00\x94\x02\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\t\x01\x80\x02h\x01\x00\xa0\xf1\x04\x00@\xe3\t\x00\x8c\n\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\n\x01 \x03\xe0\x01\x00`=\x08\x00\xc0z\x10\x00\x94\x11\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00"$\x07\x0b\x01\x00\x04\x00\x03\x00\x00\xe0\x10\x00\x00\xc0!\x00\x00$\x00\x15\x16\x05\x00\x02\x15\x16\x05\x00*,\n\x00\x06$\r\x01\x01\x04\t\x04\x01\x01\x01\x0e\x02\x00\x04\x07\x05\x82\x05\x00\x14\x01\t\x04\x01\x02\x01\x0e\x02\x00\x04\x07\x05\x82\x05\x00\x0c\x01\t\x04\x01\x03\x01\x0e\x02\x00\x04\x07\x05\x82\x05\x00\x04\x01\t\x04\x01\x04\x01\x0e\x02\x00\x04\x07\x05\x82\x05\x00\x02\x01\x08\x0b\x02\x02\x01\x00\x00\x05\t\x04\x02\x00\x00\x01\x01\x00\x05\t$\x01\x00\x01+\x00\x01\x03\x0c$\x02\x01\x01\x02\x00\x01\x00\x00\x00\x00\r$\x06\x05\x01\x06\x03\x00\x00\x00\x00\x00\x00\t$\x03\x03\x01\x01\x00\x05\x00\t\x04\x03\x00\x00\x01\x02\x00\x05\t\x04\x03\x01\x01\x01\x02\x00\x05\x07$\x01\x03\x01\x01\x00 $\x02\x01\x01\x02\x10\x08\x80\xbb\x00D\xac\x00\xc0]\x00"V\x00\x80>\x00\xe0.\x00\x11+\x00@\x1f\x00\t\x05\x83\rd\x00\x04\x00\x00\x07%\x01\x01\x01\x00\x00\t\x04\x03\x02\x01\x01\x02\x00\x05\x07$\x01\x03\x01\x01\x00 $\x02\x01\x01\x01\x08\x08\x80\xbb\x00D\xac\x00\xc0]\x00"V\x00\x80>\x00\xe0.\x00\x11+\x00@\x1f\x00\t\x05\x83\r2\x00\x04\x00\x00\x07%\x01\x01\x01\x00\x00'
INFO    | moondancer     | Disconnecting from target host.
Traceback (most recent call last):
  File "./examples/usbproxy.py", line 30, in <module>
    main(proxy)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/devices/__init__.py", line 41, in default_main
    device.emulate(*coroutines)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/device.py", line 232, in emulate
    self.run_with(*coroutines)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/device.py", line 217, in run_with
    asyncio.run(inner())
  File "/usr/lib/python3.8/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/lib/python3.8/asyncio/base_events.py", line 616, in run_until_complete
    return future.result()
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/device.py", line 215, in inner
    await asyncio.gather(self.run(), *coroutines)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/device.py", line 204, in run
    self.backend.service_irqs()
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/backends/moondancer.py", line 414, in service_irqs
    self.handle_receive_control(event.endpoint_number)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/backends/moondancer.py", line 480, in handle_receive_control
    self.connected_device.handle_request(request)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/proxy.py", line 131, in handle_request
    self._proxy_in_control_request(request)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/proxy.py", line 246, in _proxy_in_control_request
    self.send(0, data)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/device.py", line 272, in send
    self.backend.send_on_endpoint(0, data, blocking=blocking)
  File "/home/user/.local/lib/python3.8/site-packages/facedancer/backends/moondancer.py", line 329, in send_on_endpoint
    self.api.write_endpoint(endpoint_number, blocking, bytes(data))
  File "/home/user/.local/lib/python3.8/site-packages/pygreat/comms.py", line 1126, in method
    return self.execute_command(verb_number, in_format, out_format, name=name, class_name=class_name,
  File "/home/user/.local/lib/python3.8/site-packages/pygreat/comms.py", line 1281, in execute_command
    return self.comms_backend.execute_command(self.CLASS_NUMBER, verb, in_format,
  File "/home/user/.local/lib/python3.8/site-packages/pygreat/comms.py", line 788, in execute_command
    raw_result = self.execute_raw_command(class_number, verb, payload, timeout,
  File "/home/user/.local/lib/python3.8/site-packages/pygreat/comms_backends/usb1.py", line 381, in execute_raw_command
    raise ValueError("Command payload is too long!")
ValueError: Command payload is too long!

This appears to be caused by LIBGREAT_MAX_COMMAND_SIZE in pygreat being only 1024.

I tried increasing LIBGREAT_MAX_COMMAND_SIZE to 4096, but this soft-bricked my Cynthion. I had to reload the facedancer firmware into it to make it work again.

antoinevg commented 2 months ago

Generally there are no transfer size limits in Facedancer as it packetizes all transfers except for CONTROL IN transfers as this would require some fairly complex state synchronization between the Facedancer host code and the Facedancer device firmware.

On Cynthion, the firmware sets LIBGREAT_MAX_COMMAND_SIZE to 1024 bytes as we only have 64 kB of RAM available to us on the Facedancer SoC. (!)

GreatFET does a little better and can handle up to 4096 bytes but it also suffers from the same inherent problem.

That said, it occurs to me that this limit was imposed before we moved the Facedancer firmware to the qspi flash which has freed up about 50% of the available RAM.

So you could try to also bump LIBGREAT_MAX_COMMAND_SIZE a little higher in firmware/libgreat/src/gcp.rs.

We're also working on making the Cynthion HyperRAM available to the SoC which will allow us to greatly increase the transfer size.

Though ideally this is a limitation that needs to be removed from Facedancer itself.

The tracking issue for this is here: https://github.com/greatscottgadgets/facedancer/issues/114