Skip to content

Commit

Permalink
osx: handle run loop firing report_callback() multiple times
Browse files Browse the repository at this point in the history
One invocation of CFRunLoopRunInMode() may fire the report callback
multiple times. When that happens, the next call to fido_hid_read() may
block for the full timeout if the authenticator has already sent its
response.

The pipe is already configured to be non-blocking. This means we can
attempt to read from the pipe on entering fido_hid_read() and only
execute the run loop if there is no data already available.
  • Loading branch information
LDVG committed Dec 21, 2023
1 parent 9634391 commit 2cafa9e
Showing 1 changed file with 18 additions and 10 deletions.
28 changes: 18 additions & 10 deletions src/hid_osx.c
Original file line number Diff line number Diff line change
Expand Up @@ -537,20 +537,28 @@ fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
return (-1);
}

IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetCurrent(),
ctx->loop_id);
/* check for pending frame */
if ((r = read(ctx->report_pipe[0], buf, len)) == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK) {
fido_log_error(errno, "%s: read", __func__);
return (-1);
}

if (ms == -1)
ms = 5000; /* wait 5 seconds by default */
IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetCurrent(),
ctx->loop_id);

CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true);
if (ms == -1)
ms = 5000; /* wait 5 seconds by default */

IOHIDDeviceUnscheduleFromRunLoop(ctx->ref, CFRunLoopGetCurrent(),
ctx->loop_id);
CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true);

if ((r = read(ctx->report_pipe[0], buf, len)) == -1) {
fido_log_error(errno, "%s: read", __func__);
return (-1);
IOHIDDeviceUnscheduleFromRunLoop(ctx->ref,
CFRunLoopGetCurrent(), ctx->loop_id);

if ((r = read(ctx->report_pipe[0], buf, len)) == -1) {
fido_log_error(errno, "%s: read", __func__);
return (-1);
}
}

if (r < 0 || (size_t)r != len) {
Expand Down

0 comments on commit 2cafa9e

Please sign in to comment.