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.
This commit is contained in:
s0up4200 2026-08-08 09:44:02 +02:00
parent 8a941c5553
commit 822b762b3c
No known key found for this signature in database
3 changed files with 3 additions and 3 deletions

View File

@ -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)

View File

@ -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

View File

@ -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)