From e149f5e3328c11b1d18ef94c76843f7f2f4cc9f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20Andr=C3=A9=20Vadla=20Ravn=C3=A5s?= Date: Tue, 26 Nov 2024 23:04:02 +0100 Subject: [PATCH] fruity: Avoid USB access in case of kernel NCM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit So we won't run into permission issues, and avoid unneccessary work. Co-authored-by: Håvard Sørbø --- src/fruity/device-monitor.vala | 122 ++++++++++++++++----------------- src/fruity/ncm.vala | 7 +- src/fruity/usb.vala | 69 ++++++++----------- 3 files changed, 91 insertions(+), 107 deletions(-) diff --git a/src/fruity/device-monitor.vala b/src/fruity/device-monitor.vala index 2448615de..1356f44a8 100644 --- a/src/fruity/device-monitor.vala +++ b/src/fruity/device-monitor.vala @@ -993,60 +993,58 @@ namespace Frida.Fruity { } private async void handle_usb_device_arrival (LibUSB.Device raw_device) { - string? udid = null; - char serial[LibUSB.DEVICE_STRING_BYTES_MAX + 1]; - var res = raw_device.get_device_string (SERIAL_NUMBER, serial); - if (res >= LibUSB.Error.SUCCESS) { - serial[res] = '\0'; - udid = (string) serial; - if (udid.length == 24) - udid = udid[:8] + "-" + udid[8:]; - - var transport = usb_transports.first_match (t => t.udid == udid); - if (transport != null) { - if (!transport.try_complete_modeswitch (raw_device)) - transport = null; - } + UsbDevice usb_device; + try { + usb_device = new UsbDevice (raw_device, this); + } catch (Error e) { + return; + } - if (transport == null) { - transport = new PortableCoreDeviceUsbTransport (this, raw_device, udid, pairing_store); - usb_transports.add (transport); + unowned string udid = usb_device.udid; + var transport = usb_transports.first_match (t => t.udid == udid); + if (transport != null) { + if (!transport.try_complete_modeswitch (raw_device)) + transport = null; + } - if (state != STARTING) { - uint delays[] = { 0, 50, 250 }; - for (uint attempts = 0; attempts != delays.length; attempts++) { - uint delay = delays[attempts]; - if (delay != 0) { - var timeout_source = new TimeoutSource (delay); - timeout_source.set_callback (handle_usb_device_arrival.callback); - timeout_source.attach (main_context); + if (transport == null) { + transport = new PortableCoreDeviceUsbTransport (this, usb_device, pairing_store); + usb_transports.add (transport); + + if (state != STARTING) { + uint delays[] = { 0, 50, 250 }; + for (uint attempts = 0; attempts != delays.length; attempts++) { + uint delay = delays[attempts]; + if (delay != 0) { + var timeout_source = new TimeoutSource (delay); + timeout_source.set_callback (handle_usb_device_arrival.callback); + timeout_source.attach (main_context); - var cancel_source = new CancellableSource (io_cancellable); - cancel_source.set_callback (handle_usb_device_arrival.callback); - cancel_source.attach (main_context); + var cancel_source = new CancellableSource (io_cancellable); + cancel_source.set_callback (handle_usb_device_arrival.callback); + cancel_source.attach (main_context); - yield; + yield; - cancel_source.destroy (); - timeout_source.destroy (); + cancel_source.destroy (); + timeout_source.destroy (); - if (io_cancellable.is_cancelled ()) - break; - } + if (io_cancellable.is_cancelled ()) + break; + } - try { - yield transport.open (io_cancellable); + try { + yield transport.open (io_cancellable); + break; + } catch (GLib.Error e) { + // We might still be waiting for a udev rule to run... + if (!(e is Error.PERMISSION_DENIED)) break; - } catch (GLib.Error e) { - // We might still be waiting for a udev rule to run... - if (!(e is Error.PERMISSION_DENIED)) - break; - } } } - - transport_attached (transport); } + + transport_attached (transport); } if (AtomicUint.dec_and_test (ref pending_usb_device_arrivals) && state == STARTING) @@ -1054,7 +1052,7 @@ namespace Frida.Fruity { } private async void handle_usb_device_departure (LibUSB.Device raw_device) { - var transport = usb_transports.first_match (t => t.raw_device == raw_device && !t.modeswitch_in_progress); + var transport = usb_transports.first_match (t => t.usb_device.raw_device == raw_device && !t.modeswitch_in_progress); if (transport == null) return; @@ -1185,9 +1183,9 @@ namespace Frida.Fruity { } private sealed class PortableCoreDeviceUsbTransport : Object, Transport { - public LibUSB.Device raw_device { + public UsbDevice usb_device { get { - return _raw_device; + return _usb_device; } } @@ -1199,7 +1197,7 @@ namespace Frida.Fruity { public string udid { get { - return _udid; + return _usb_device.udid; } } @@ -1232,9 +1230,8 @@ namespace Frida.Fruity { } } - private weak PortableCoreDeviceBackend parent; - private LibUSB.Device _raw_device; - private string _udid; + private unowned PortableCoreDeviceBackend parent; + private UsbDevice _usb_device; private string? _name; private Promise? device_request; @@ -1242,16 +1239,14 @@ namespace Frida.Fruity { private Promise? tunnel_request; private NcmPeer? ncm_peer; - public PortableCoreDeviceUsbTransport (PortableCoreDeviceBackend parent, LibUSB.Device raw_device, string udid, - PairingStore store) { + public PortableCoreDeviceUsbTransport (PortableCoreDeviceBackend parent, UsbDevice device, PairingStore store) { Object (pairing_store: store); this.parent = parent; - _raw_device = raw_device; - _udid = udid; + _usb_device = device; char product[LibUSB.DEVICE_STRING_BYTES_MAX + 1]; - var res = raw_device.get_device_string (PRODUCT, product); + var res = device.raw_device.get_device_string (PRODUCT, product); if (res >= LibUSB.Error.SUCCESS) { product[res] = '\0'; _name = (string) product; @@ -1271,11 +1266,11 @@ namespace Frida.Fruity { device_request = new Promise (); try { - var device = yield UsbDevice.open (_raw_device, parent, cancellable); + if (NcmPeer.detect_ncm_ifaddrs_on_system (_usb_device).is_empty && parent.modeswitch_allowed) { + _usb_device.ensure_open (); - if (parent.modeswitch_allowed) { modeswitch_request = new Promise (); - if (yield device.maybe_modeswitch (cancellable)) { + if (yield _usb_device.maybe_modeswitch (cancellable)) { var source = new TimeoutSource.seconds (2); source.set_callback (() => { if (modeswitch_request != null) { @@ -1286,21 +1281,22 @@ namespace Frida.Fruity { }); source.attach (MainContext.get_thread_default ()); + LibUSB.Device raw_device = null; try { - _raw_device = yield modeswitch_request.future.wait_async (cancellable); + raw_device = yield modeswitch_request.future.wait_async (cancellable); } finally { source.destroy (); } - device = yield UsbDevice.open (_raw_device, parent, cancellable); + _usb_device = new UsbDevice (raw_device, parent); } else { modeswitch_request = null; } } - device_request.resolve (device); + device_request.resolve (_usb_device); - return device; + return _usb_device; } catch (GLib.Error e) { device_request.reject (e); device_request = null; @@ -1423,7 +1419,7 @@ namespace Frida.Fruity { return yield establish_using_our_driver (usb_device, cancellable); } - private static Gee.List detect_ncm_ifaddrs_on_system (UsbDevice usb_device) throws Error { + public static Gee.List detect_ncm_ifaddrs_on_system (UsbDevice usb_device) throws Error { var device_ifaddrs = new Gee.ArrayList (); #if LINUX diff --git a/src/fruity/ncm.vala b/src/fruity/ncm.vala index d3c0d266a..2d13b8df8 100644 --- a/src/fruity/ncm.vala +++ b/src/fruity/ncm.vala @@ -71,6 +71,8 @@ namespace Frida.Fruity { } private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError { + device.ensure_open (); + unowned LibUSB.Device raw_device = device.raw_device; unowned LibUSB.DeviceHandle handle = device.handle; @@ -126,9 +128,10 @@ namespace Frida.Fruity { if (!found_cdc_header || !found_data_interface) throw new Error.NOT_SUPPORTED ("%s", make_user_error_message ("No USB CDC-NCM interface found")); + var language_id = yield device.query_default_language_id (cancellable); + uint8 mac_address[6]; - string mac_address_str = yield device.read_string_descriptor (mac_address_index, device.default_language_id, - cancellable); + string mac_address_str = yield device.read_string_descriptor (mac_address_index, language_id, cancellable); if (mac_address_str.length != 12) throw new Error.PROTOCOL ("Invalid MAC address"); for (uint i = 0; i != 6; i++) { diff --git a/src/fruity/usb.vala b/src/fruity/usb.vala index 9d540c8a0..2617f80b0 100644 --- a/src/fruity/usb.vala +++ b/src/fruity/usb.vala @@ -1,6 +1,11 @@ [CCode (gir_namespace = "FridaFruity", gir_version = "1.0")] namespace Frida.Fruity { - internal sealed class UsbDevice : Object, AsyncInitable { + internal sealed class UsbDevice : Object { + public string udid { + get; + construct; + } + public LibUSB.Device? raw_device { get { return _raw_device; @@ -18,22 +23,8 @@ namespace Frida.Fruity { } } - public string udid { - get { - return _udid; - } - } - - public uint16 default_language_id { - get { - return _default_language_id; - } - } - private LibUSB.Device? _raw_device; private LibUSB.DeviceHandle? _handle; - private string _udid; - private uint16 _default_language_id; private uint num_pending_operations; private Promise? pending_operations_completed; @@ -45,38 +36,24 @@ namespace Frida.Fruity { private const string MODE_INITIAL_UNTETHERED = "3:3:3:0"; // => 5:3:3:0 private const string MODE_INITIAL_TETHERED = "4:4:3:4"; // => 5:4:3:4 - public static async UsbDevice open (LibUSB.Device raw_device, UsbDeviceBackend backend, Cancellable? cancellable = null) - throws Error, IOError { - var device = new UsbDevice (raw_device, backend); + public UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) throws Error { + char serial[LibUSB.DEVICE_STRING_BYTES_MAX + 1]; + var res = raw_device.get_device_string (SERIAL_NUMBER, serial); + Usb.check (res, "Failed to get serial number"); + serial[res] = '\0'; - try { - yield device.init_async (Priority.DEFAULT, cancellable); - } catch (GLib.Error e) { - throw_api_error (e); - } + Object ( + udid: udid_from_serial_number ((string) serial), + backend: backend + ); - return device; - } - - private UsbDevice (LibUSB.Device raw_device, UsbDeviceBackend backend) { - Object (backend: backend); _raw_device = raw_device; } - private async bool init_async (int io_priority, Cancellable? cancellable) throws Error, IOError { + public void ensure_open (Cancellable? cancellable = null) throws Error { + if (_handle != null) + return; Usb.check (_raw_device.open (out _handle), "Failed to open USB device"); - - Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable); - if (language_ids_response.get_size () < sizeof (uint16)) - throw new Error.PROTOCOL ("Invalid language IDs response"); - Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN); - _default_language_id = language_ids.read_uint16 (0); - - var dev_desc = LibUSB.DeviceDescriptor (_raw_device); - string serial_number = yield read_string_descriptor (dev_desc.iSerialNumber, _default_language_id, cancellable); - _udid = udid_from_serial_number (serial_number); - - return true; } public async void close (Cancellable? cancellable) throws IOError { @@ -134,10 +111,18 @@ namespace Frida.Fruity { private static string udid_from_serial_number (string serial) { if (serial.length == 24) - return serial.substring (0, 8) + "-" + serial.substring (8); + return serial[:8] + "-" + serial[8:]; return serial; } + public async uint16 query_default_language_id (Cancellable? cancellable) throws Error, IOError { + Bytes language_ids_response = yield read_string_descriptor_bytes (0, 0, cancellable); + if (language_ids_response.get_size () < sizeof (uint16)) + throw new Error.PROTOCOL ("Invalid language IDs response"); + Buffer language_ids = new Buffer (language_ids_response, LITTLE_ENDIAN); + return language_ids.read_uint16 (0); + } + public async string read_string_descriptor (uint8 index, uint16 language_id, Cancellable? cancellable) throws Error, IOError { var response = yield read_string_descriptor_bytes (index, language_id, cancellable);