Skip to content

Commit

Permalink
hid_osx: read reports through a pipe
Browse files Browse the repository at this point in the history
the callback passed to IOHIDDeviceRegisterInputReportCallback() may
be called multiple times while we are in CFRunLoopRunInMode(). we
need to store the reports somewhere; a pipe is convenient (allowing
fido_hid_read() to keep UNIX-like read() semantics), spacious (at
least a page in size), and has adequate I/O properties (atomic
writes up to PIPE_BUF bytes, where PIPE_BUF > CTAP_MAX_REPORT_LEN).
  • Loading branch information
martelletto committed Aug 21, 2020
1 parent 5ccde03 commit 835309b
Showing 1 changed file with 79 additions and 15 deletions.
94 changes: 79 additions & 15 deletions src/hid_osx.c
Expand Up @@ -22,6 +22,7 @@
struct hid_osx {
IOHIDDeviceRef ref;
CFStringRef loop_id;
int report_pipe[2];
size_t report_in_len;
size_t report_out_len;
unsigned char report[CTAP_MAX_REPORT_LEN];
Expand Down Expand Up @@ -299,17 +300,25 @@ fido_hid_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
}

static void
read_callback(void *context, IOReturn result, void *dev, IOHIDReportType type,
uint32_t report_id, uint8_t *report, CFIndex report_len)
report_callback(void *context, IOReturn result, void *dev, IOHIDReportType type,
uint32_t id, uint8_t *ptr, CFIndex len)
{
(void)context;
struct hid_osx *ctx = context;
ssize_t r;

(void)dev;
(void)report;
(void)report_len;

if (result != kIOReturnSuccess || type != kIOHIDReportTypeInput ||
report_id != 0)
id != 0 || len < 0 || (size_t)len != ctx->report_in_len) {
fido_log_debug("%s: io error", __func__);
return;
}

if ((r = write(ctx->report_pipe[1], ptr, (size_t)len)) < 0 ||
(size_t)r != (size_t)len) {
fido_log_debug("%s: write", __func__);
return;
}
}

static void
Expand All @@ -322,6 +331,37 @@ removal_callback(void *context, IOReturn result, void *sender)
CFRunLoopStop(CFRunLoopGetMain());
}

static int
set_nonblock(int fd)
{
int flags;

if ((flags = fcntl(fd, F_GETFL)) == -1) {
fido_log_debug("%s: fcntl F_GETFL", __func__);
return (-1);
}

if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
fido_log_debug("%s: fcntl, F_SETFL", __func__);
return (-1);
}

return (0);
}

static int
disable_sigpipe(int fd)
{
int disabled = 1;

if (fcntl(fd, F_SETNOSIGPIPE, &disabled) == -1) {
fido_log_debug("%s: fcntl F_SETNOSIGPIPE", __func__);
return (-1);
}

return (0);
}

void *
fido_hid_open(const char *path)
{
Expand All @@ -336,6 +376,25 @@ fido_hid_open(const char *path)
goto fail;
}

ctx->report_pipe[0] = -1;
ctx->report_pipe[1] = -1;

if (pipe(ctx->report_pipe) == -1) {
fido_log_debug("%s: pipe", __func__);
goto fail;
}

if (set_nonblock(ctx->report_pipe[0]) < 0 ||
set_nonblock(ctx->report_pipe[1]) < 0) {
fido_log_debug("%s: set_nonblock", __func__);
goto fail;
}

if (disable_sigpipe(ctx->report_pipe[1]) < 0) {
fido_log_debug("%s: disable_sigpipe", __func__);
goto fail;
}

if ((entry = IORegistryEntryFromPath(kIOMasterPortDefault,
path)) == MACH_PORT_NULL) {
fido_log_debug("%s: IORegistryEntryFromPath", __func__);
Expand Down Expand Up @@ -379,7 +438,7 @@ fido_hid_open(const char *path)
}

IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
(long)ctx->report_in_len, &read_callback, NULL);
(long)ctx->report_in_len, &report_callback, ctx);
IOHIDDeviceRegisterRemovalCallback(ctx->ref, &removal_callback, ctx);
IOHIDDeviceScheduleWithRunLoop(ctx->ref, CFRunLoopGetMain(),
ctx->loop_id);
Expand All @@ -394,6 +453,10 @@ fido_hid_open(const char *path)
CFRelease(ctx->ref);
if (ctx->loop_id != NULL)
CFRelease(ctx->loop_id);
if (ctx->report_pipe[0] != -1)
close(ctx->report_pipe[0]);
if (ctx->report_pipe[1] != -1)
close(ctx->report_pipe[1]);
free(ctx);
ctx = NULL;
}
Expand All @@ -407,7 +470,7 @@ fido_hid_close(void *handle)
struct hid_osx *ctx = handle;

IOHIDDeviceRegisterInputReportCallback(ctx->ref, ctx->report,
(long)ctx->report_in_len, NULL, NULL);
(long)ctx->report_in_len, NULL, ctx);
IOHIDDeviceRegisterRemovalCallback(ctx->ref, NULL, NULL);
IOHIDDeviceUnscheduleFromRunLoop(ctx->ref, CFRunLoopGetMain(),
ctx->loop_id);
Expand All @@ -420,14 +483,17 @@ fido_hid_close(void *handle)
CFRelease(ctx->loop_id);

explicit_bzero(ctx->report, sizeof(ctx->report));
close(ctx->report_pipe[0]);
close(ctx->report_pipe[1]);

free(ctx);
}

int
fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
{
struct hid_osx *ctx = handle;
CFRunLoopRunResult r;
ssize_t r;

explicit_bzero(buf, len);
explicit_bzero(ctx->report, sizeof(ctx->report));
Expand All @@ -444,15 +510,13 @@ fido_hid_read(void *handle, unsigned char *buf, size_t len, int ms)
fido_log_debug("%s: CFRunLoopGetCurrent != CFRunLoopGetMain",
__func__);

if ((r = CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0,
true)) != kCFRunLoopRunHandledSource) {
fido_log_debug("%s: CFRunLoopRunInMode=%d", __func__, (int)r);
CFRunLoopRunInMode(ctx->loop_id, (double)ms/1000.0, true);

if ((r = read(ctx->report_pipe[0], buf, len)) < 0 || (size_t)r != len) {
fido_log_debug("%s: read", __func__);
return (-1);
}

memcpy(buf, ctx->report, len);
explicit_bzero(ctx->report, sizeof(ctx->report));

return ((int)len);
}

Expand Down

0 comments on commit 835309b

Please sign in to comment.