Skip to content

Commit 72169eb

Browse files
committed
mac: ignore hotplug devices with no backing io_service_t
Such a device has no usable identity for the cache, so it could be reported twice and never evicted; skip it at both cache-insertion points instead. Also document that the "reported exactly once" guarantee is best effort when the live-arrival path runs out of memory, unlike the registration-time paths which can still fail loudly. Assisted-by: claude-code:claude-opus-5
1 parent cc60780 commit 72169eb

1 file changed

Lines changed: 25 additions & 0 deletions

File tree

mac/hid.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,6 +1438,14 @@ static void hid_internal_hotplug_connect_callback(void *context, IOReturn result
14381438
(void) result;
14391439
(void) sender;
14401440

1441+
/* A device without a backing io_service_t carries no usable identity (see
1442+
match_ref_to_info()): once cached it would match neither the arrival
1443+
dedupe nor its own removal, so it would be reported more than once and
1444+
never evicted. Keep it consistently invisible instead. */
1445+
if (!device || IOHIDDeviceGetService(device) == MACH_PORT_NULL) {
1446+
return;
1447+
}
1448+
14411449
if (!startup) {
14421450
/* Lock the mutex to avoid race conditions */
14431451
pthread_mutex_lock(&hid_hotplug_context.mutex);
@@ -1459,6 +1467,17 @@ static void hid_internal_hotplug_connect_callback(void *context, IOReturn result
14591467

14601468
info = create_device_info(device);
14611469
if (!info) {
1470+
/* Out of memory on the live-arrival path: the device ends up neither in
1471+
the cache nor in an event, so "reported exactly once" is best effort
1472+
here. The registration-time paths are hardened against this -
1473+
hid_internal_hotplug_build_device_cache() fails the startup and
1474+
hid_hotplug_register_callback() fails the registration rather than
1475+
commit a partial initial pass - because both still have a caller to
1476+
report the failure to. An IOKit callback has none, and the
1477+
IOHIDManager does not re-report the device, so there is nothing left
1478+
to fail or retry against. (During the startup phase the device is
1479+
still picked up by hid_internal_hotplug_build_device_cache(), which
1480+
does fail loudly if it cannot allocate either.) */
14621481
if (!startup) {
14631482
pthread_mutex_unlock(&hid_hotplug_context.mutex);
14641483
}
@@ -1658,6 +1677,12 @@ static int hid_internal_hotplug_build_device_cache(void)
16581677
continue;
16591678
}
16601679

1680+
/* Same identity requirement as the live-arrival path: an entry with no
1681+
backing io_service_t could never be deduped against, nor evicted */
1682+
if (IOHIDDeviceGetService(device_array[i]) == MACH_PORT_NULL) {
1683+
continue;
1684+
}
1685+
16611686
/* Already in the cache (the drain got to it first) */
16621687
if (hid_internal_hotplug_is_known_device(device_array[i])) {
16631688
continue;

0 commit comments

Comments
 (0)