IOServiceOpen returns kIOReturnError (0xE00002BC) before NewUserClient — DEXT matches and opens pipes successfully

I'm hitting a kernel-side rejection on IOServiceOpen from a host app against my DEXT's IOUserService, before any code in my DEXT's NewUserClient runs. DEXT activation and USB matching succeed; only the user-client connection fails.

What works

  • DEXT activates and shows as [activated enabled] in systemextensionsctl list.
  • DEXT matches IOUSBHostInterface for the target device and Start() runs to completion.
  • Inside Start(), CopyInterface() returns successfully and CopyPipe() for the expected endpoints all succeed.
  • Host app receives the matching notification for the DEXT's IOUserService and calls IOServiceOpen(service, mach_task_self(), 0, &connect).

What fails

IOServiceOpen returns kIOReturnError (0xE00002BC). My DEXT's NewUserClient override is never reached — verified by the absence of any breadcrumb log and by stepping through under lldb (no entry on the DEXT side).

This reproduces both with:

  • The original com.apple.developer.driverkit.userclient-access entitlement listing the host bundle ID.
  • The dev fallback com.apple.developer.driverkit.allow-any-userclient-access = true on host + DEXT.

(Background: the App ID portal has the bundle-ID list for userclient-access stored as a single newline-joined string instead of separate array entries — see Support Thread 822652 — so I've been using allow-any-userclient-access = true for now. The IOServiceOpen failure persists either way.)

Diagnostics I can't get

I'd like to confirm the kernel-side rejection reason, but DEXT os_log output is suppressed in Console and:

sudo log config --process <dext-pid> --mode "level:debug"
log: Unable to set mode for pid <dext-pid>

I've tried by PID and by subsystem; both refuse. SIP is in its default state. Any pointer to the correct invocation (or a Configuration Profile to enable DriverKit verbose logging) would unblock me.

Environment

  • macOS 26.3.1 (build 25D2128)
  • Xcode 26.3 (build 17C529)
  • Host app: AppKit, sandboxed, Mac App Store distribution
  • DEXT: matches IOUSBHostInterface on idVendor: 0x1452 (DNP) and (pending capability approval) 0x1343 (Citizen)
  • Entitlements on host: com.apple.developer.driverkit, com.apple.developer.driverkit.userclient-access (or allow-any-userclient-access = true for dev)
  • Entitlements on DEXT: com.apple.developer.driverkit, com.apple.developer.driverkit.transport.usb, com.apple.developer.driverkit.allow-any-userclient-access for dev

Questions

  1. Is IOServiceOpen → kIOReturnError before NewUserClient always an entitlement/sandbox check failure, or are there other kernel-side reasons (matching score, IOService class hierarchy mismatch) that produce the same generic code?
  2. What's the correct way to enable DEXT os_log capture so I can see the rejection reason?
  3. Is there a known interaction between a malformed userclient-access array on the App ID (Forums Thread 822652) and the kernel's user-client authorization path that would persist even after switching to allow-any-userclient-access = true?

Sample profiles, codesign output, and the exact matching dictionary available on request.

Thanks.

Answered by DTS Engineer in 887527022

Any pointer to the correct invocation (or a Configuration Profile to enable DriverKit verbose logging) would unblock me.

Two points here:

  • I have a post here about IOLog's "oddness" which is worth reviewing. That post also has the macro that's supposed to log "publicly", so you can confirm your logging is doing what it's supposed to do.

  • There was one developer post about DriverKit logging "weirdness", which we never heard back on. IF you determine that this is some kind of system bug, then I'd appreciate you filing a bug on this.

  • Quinn's "Your Friend the System Log" and "Recording Private Data in the System Log" both cover how you can manage logging redaction in more detail. One detail that alluded to there is that the SystemLogging.System payload lets you disable ALL private data redaction system wide.

Expanding on that last point, while I wouldn't leave redaction disabled, particularly on any machine your actually "using", that does provide a quick and easy way to see exactly what data was being logged. That can make it much easier to figure out how you need to configure a more targeted profile that only unredacts what you actually need.

Moving on to specifics:

(Background: the App ID portal has the bundle-ID list for userclient-access stored as a single newline-joined string instead of separate array entries — see Support Thread 822652

This is now fixed. However...

so I've been using allow-any-userclient-access = true for now. The IOServiceOpen failure persists either way.)

...I actually added this to provide a "handy" solution for development purposes and I'd probably continue using it (during development).

I'm hitting a kernel-side rejection on IOServiceOpen from a host app against my DEXT's IOUserService, before any code in my DEXT's NewUserClient runs

So, the FIRST thing I would do here is get our DriverKit UserClient sample up and running. That's the simplest possible configuration and, in my experience, get this working "once" makes it much easier to debug your real configuration. Similarly, if you CAN'T get it working, then that's a good indication that the problem is something "broader" than specific configuration. For example, one developer I helped several months ago had been banging against the DEXT system for several weeks only to eventually discover that a company installed EndpointSecurity app... was intentionally blocking his connection.

Also, once you've got the project working, it's relatively easy to retarget that samples code to target your DEXT instead of it's normal target. That lets you quickly validate whether the problem is with your DEXT or your host app.

Having said that...

Is IOServiceOpen → kIOReturnError before NewUserClient always an entitlement/sandbox check failure, or are there other kernel-side reasons (matching score, IOService class hierarchy mismatch) that produce the same generic code?

No, quite the opposite. The fact you're getting kIOReturnError actually makes me think this ISN'T a signing/entitlement issue- the system is pretty good about using specific error codes, so signing issues typically return "kIOReturnNotPrivileged". It typically uses "kIOReturnError" as the "catch-all" error code it uses for cases it doesn't specifically recognize.

I would recommend disabling the app sandbox (mostly because it's easy) in the host app, but if you're still failing then I would start with the assumption that the problem is something about the DEXT configuration, NOT it's signing. One small note here:

Entitlements on host: com.apple.developer.driverkit, com.apple.developer.driverkit.userclient-access (or allow-any-userclient-access = true for dev)

...is that "allow-any-userclient-access" has NO effect on a user process. This post here has a more detailed run through of how this can be configured for development/testing purposes.

Next, on this point:

Host app receives the matching notification for the DEXT's IOUserService and calls IOServiceOpen(service, mach_task_self(), 0, &connect).

One of the things I'm always careful to validate is that I'm actually working with the IOKit object I think I'm working with. I have a forum post here that includes my "print" function, which you can use to validate whatever object you got from the IORegistry. I also have a post here with a specific example of how this can go wrong.

I've tried by PID and by subsystem; both refuse. SIP is in its default state. Any pointer to the correct invocation (or a Configuration Profile to enable DriverKit verbose logging) would unblock me.

I'm not sure that the correct log command would be, but the reason that command doesn't work is that your "DEXT" doesn't actually "log" in the normal sense. Your DEXT actually logs data by sending it down to the kernel, which then outputs it through the standard kernel log channel.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Accepted Answer

Any pointer to the correct invocation (or a Configuration Profile to enable DriverKit verbose logging) would unblock me.

Two points here:

  • I have a post here about IOLog's "oddness" which is worth reviewing. That post also has the macro that's supposed to log "publicly", so you can confirm your logging is doing what it's supposed to do.

  • There was one developer post about DriverKit logging "weirdness", which we never heard back on. IF you determine that this is some kind of system bug, then I'd appreciate you filing a bug on this.

  • Quinn's "Your Friend the System Log" and "Recording Private Data in the System Log" both cover how you can manage logging redaction in more detail. One detail that alluded to there is that the SystemLogging.System payload lets you disable ALL private data redaction system wide.

Expanding on that last point, while I wouldn't leave redaction disabled, particularly on any machine your actually "using", that does provide a quick and easy way to see exactly what data was being logged. That can make it much easier to figure out how you need to configure a more targeted profile that only unredacts what you actually need.

Moving on to specifics:

(Background: the App ID portal has the bundle-ID list for userclient-access stored as a single newline-joined string instead of separate array entries — see Support Thread 822652

This is now fixed. However...

so I've been using allow-any-userclient-access = true for now. The IOServiceOpen failure persists either way.)

...I actually added this to provide a "handy" solution for development purposes and I'd probably continue using it (during development).

I'm hitting a kernel-side rejection on IOServiceOpen from a host app against my DEXT's IOUserService, before any code in my DEXT's NewUserClient runs

So, the FIRST thing I would do here is get our DriverKit UserClient sample up and running. That's the simplest possible configuration and, in my experience, get this working "once" makes it much easier to debug your real configuration. Similarly, if you CAN'T get it working, then that's a good indication that the problem is something "broader" than specific configuration. For example, one developer I helped several months ago had been banging against the DEXT system for several weeks only to eventually discover that a company installed EndpointSecurity app... was intentionally blocking his connection.

Also, once you've got the project working, it's relatively easy to retarget that samples code to target your DEXT instead of it's normal target. That lets you quickly validate whether the problem is with your DEXT or your host app.

Having said that...

Is IOServiceOpen → kIOReturnError before NewUserClient always an entitlement/sandbox check failure, or are there other kernel-side reasons (matching score, IOService class hierarchy mismatch) that produce the same generic code?

No, quite the opposite. The fact you're getting kIOReturnError actually makes me think this ISN'T a signing/entitlement issue- the system is pretty good about using specific error codes, so signing issues typically return "kIOReturnNotPrivileged". It typically uses "kIOReturnError" as the "catch-all" error code it uses for cases it doesn't specifically recognize.

I would recommend disabling the app sandbox (mostly because it's easy) in the host app, but if you're still failing then I would start with the assumption that the problem is something about the DEXT configuration, NOT it's signing. One small note here:

Entitlements on host: com.apple.developer.driverkit, com.apple.developer.driverkit.userclient-access (or allow-any-userclient-access = true for dev)

...is that "allow-any-userclient-access" has NO effect on a user process. This post here has a more detailed run through of how this can be configured for development/testing purposes.

Next, on this point:

Host app receives the matching notification for the DEXT's IOUserService and calls IOServiceOpen(service, mach_task_self(), 0, &connect).

One of the things I'm always careful to validate is that I'm actually working with the IOKit object I think I'm working with. I have a forum post here that includes my "print" function, which you can use to validate whatever object you got from the IORegistry. I also have a post here with a specific example of how this can go wrong.

I've tried by PID and by subsystem; both refuse. SIP is in its default state. Any pointer to the correct invocation (or a Configuration Profile to enable DriverKit verbose logging) would unblock me.

I'm not sure that the correct log command would be, but the reason that command doesn't work is that your "DEXT" doesn't actually "log" in the normal sense. Your DEXT actually logs data by sending it down to the kernel, which then outputs it through the standard kernel log channel.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

IOServiceOpen returns kIOReturnError (0xE00002BC) before NewUserClient — DEXT matches and opens pipes successfully
 
 
Q