From 822b762b3c96dad7844144c3538701ae06fb196b Mon Sep 17 00:00:00 2001 From: s0up4200 Date: Sat, 8 Aug 2026 09:44:02 +0200 Subject: [PATCH] Fix crashes on invalid UTF-8 in device name fields A device name is display-only, but three code paths decode it strictly and raise UnicodeDecodeError, aborting the whole operation: - handle_device_discovery: crashes 'solaar pair' during Bolt discovery - BoltReceiver.device_codename: crashes 'solaar show' and 'solaar unpair' - get_friendly_name: crashes 'solaar show' after device enumeration Observed in the wild with an MX Master 3S whose user-writable friendly name field contained 0xc3 0x23 (invalid UTF-8). The Bolt receiver copies this name at pairing time, so the bad bytes surface from both the receiver registers and the live HID++ 2.0 feature, making the device impossible to pair, list, or unpair with Solaar. Decode with errors='replace' so a garbage name renders as replacement characters instead of making the device unusable. --- lib/logitech_receiver/hidpp20.py | 2 +- lib/logitech_receiver/notifications.py | 2 +- lib/logitech_receiver/receiver.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/logitech_receiver/hidpp20.py b/lib/logitech_receiver/hidpp20.py index def630a2..a1159a4b 100644 --- a/lib/logitech_receiver/hidpp20.py +++ b/lib/logitech_receiver/hidpp20.py @@ -1891,7 +1891,7 @@ class Hidpp20: logger.error("failed to read whole name of %s (expected %d chars)", device, name_length) return None - return name.decode("utf-8") + return name.decode("utf-8", errors="replace") def get_battery_status(self, device: Device): report = device.feature_request(SupportedFeature.BATTERY_STATUS) diff --git a/lib/logitech_receiver/notifications.py b/lib/logitech_receiver/notifications.py index 933d2477..33d14b94 100644 --- a/lib/logitech_receiver/notifications.py +++ b/lib/logitech_receiver/notifications.py @@ -531,7 +531,7 @@ def handle_device_discovery(receiver: Receiver, notification: HIDPPNotification) receiver.pairing.device_address = notification.data[6:12] receiver.pairing.device_authentication = notification.data[14] elif notification.data[1] == 1: - receiver.pairing.device_name = notification.data[3 : 3 + notification.data[2]].decode("utf-8") + receiver.pairing.device_name = notification.data[3 : 3 + notification.data[2]].decode("utf-8", errors="replace") return True diff --git a/lib/logitech_receiver/receiver.py b/lib/logitech_receiver/receiver.py index 15f9a9cc..ac0e670e 100644 --- a/lib/logitech_receiver/receiver.py +++ b/lib/logitech_receiver/receiver.py @@ -497,7 +497,7 @@ class BoltReceiver(Receiver): codename = self.read_register(Registers.RECEIVER_INFO, InfoSubRegisters.BOLT_DEVICE_NAME + n, 0x01) if codename: codename = codename[3 : 3 + min(14, ord(codename[2:3]))] - return codename.decode("ascii") + return codename.decode("ascii", errors="replace") def device_pairing_information(self, n: int) -> dict: pair_info = self.read_register(Registers.RECEIVER_INFO, InfoSubRegisters.BOLT_PAIRING_INFORMATION + n)