Cannot communicate with DriverKit extension from macOs App due to IOServiceOpen error

We inspired by announce that DriverKit will come to iPadOs this fall and decided to experiment with DriverKit on macOS.

We started from scratch but followed all the recommendations from Sample.

With our installer code we can:

  1. Install driver extension to macOS system. (systemextensionsctl list shows [activated enabled]) for our driver
  2. See driver record using ioreg. So it's displayed: MyDriver <class IOUserService, id 0x10000193b, registered, matched, active, busy 0 (0 ms), retain 7>
  3. See *.MyDriver process in Activity Monitor

In client code we can find a match:

    private let dextIdentifier = "MyDriver"
    private var lastResult: kern_return_t = kIOReturnSuccess
    private var connection: io_connect_t = IO_OBJECT_NULL

        var iterator: io_iterator_t = IO_OBJECT_NULL
        var driver: io_service_t = IO_OBJECT_NULL

        lastResult = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceNameMatching(dextIdentifier), &iterator)

        print("Search for services ...")

        while case let service = IOIteratorNext(iterator),
              service != IO_OBJECT_NULL {
            driver = service
        }
        IOObjectRelease(iterator)

        guard driver != IO_OBJECT_NULL else { return }

So driver is not IO_OBJECT_NULL. But we stuck on IOServiceOpen. So code:

        lastResult = IOServiceOpen(driver, mach_task_self_, UInt32(kIOHIDServerConnectType), &connection);
        validateLastResult(forOperation: "opening service")

prints opening service: (iokit/common) general error since IOServiceOpen returns 0x2bc

What we tried:

  • Disable SIP in recovery mode and set systemextensionsctl developer on

Result: opening service: (iokit/common) general error

  • Add com.apple.developer.driverkit.userclient-access to our client App.

Result: App crashes on start and in Console we see: taskgated-helper - Unsatisfied entitlements: com.apple.developer.driverkit.userclient-access. We requested this entitlement.

  • We found post were com.apple.developer.driverkit.allow-any-userclient-access suggested as solution.

Result: Driver extension is not started and we see in Console:

taskgated-helper MyDriver: Unsatisfied entitlements: com.apple.developer.driverkit.allow-any-userclient-access
amfid /Library/SystemExtensions/B7624EEF-3688-4735-A58B-26FEF4DE353C/MyDriver.dext/MyDriver signature not valid: -67671
  • We recreated appIds, profiles on developer account that has com.apple.developer.driverkit.allow-any-userclient-access from Apple.

Result: Driver extension is started but we still see opening service: (iokit/common) general error

Result: opening service: (iokit/common) general error

Our environment:
macOS Monterey 12.4
XCode Version 13.4 (13F17a)

Driver Code is pretty straightforward:

///// iig
class MyDriver: public IOUserClient
{
public:
    virtual bool init(void) override;
    virtual kern_return_t Start(IOService * provider) override;
    virtual kern_return_t Stop(IOService* provider) override;
    virtual void free(void) override;
};

///// cpp

#define Log(fmt, ...) os_log(OS_LOG_DEFAULT, "MyDriver - " fmt "\n", ##__VA_ARGS__)

struct MyDriver_IVars {
    OSAction* callbackAction = nullptr;
};

bool MyDriver::init() {
    bool result = false;

    Log("init()");

    result = super::init();
    if (result != true)
    {
        Log("init() - super::init failed.");
        goto Exit;
    }

    ivars = IONewZero(MyDriver_IVars, 1);
    if (ivars == nullptr)
    {
        Log("init() - Failed to allocate memory for ivars.");
        goto Exit;
    }

    Log("init() - Finished.");
    return true;

Exit:
    return false;
}

kern_return_t
IMPL(MyDriver, Start)
{
    kern_return_t ret;
    ret = Start(provider, SUPERDISPATCH);
    if(ret != kIOReturnSuccess) {
        Log("Start() - super::Start failed with error: 0x%08x.", ret);
        goto Exit;
    }

    ret = RegisterService();
    if (ret != kIOReturnSuccess)
    {
        Log("Start() - Failed to register service with error: 0x%08x.", ret);
        goto Exit;
    }

    Log("Start() - Finished.");
    ret = kIOReturnSuccess;

Exit:
    return ret;
}

kern_return_t
IMPL(MyDriver, Stop)
{
    kern_return_t ret = kIOReturnSuccess;

    Log("Stop()");

    ret = Stop(provider, SUPERDISPATCH);
    if (ret != kIOReturnSuccess)
    {
        Log("Stop() - super::Stop failed with error: 0x%08x.", ret);
    }

    return ret;
}

void MyDriver::free(void)
{
    Log("free()");

    IOSafeDeleteNULL(ivars, MyDriver_IVars, 1);

    super::free();
}

Result: App crashes on start and in Console we see: taskgated-helper - Unsatisfied entitlements: com.apple.developer.driverkit.userclient-access. We requested this entitlement.

Without being granted the specific entitlement as a dictionary of strings containing the bundle ID of your dext, this entitlement will do more harm than good. If you are planning on using com.apple.developer.driverkit.allow-any-userclient-access, please remove the com.apple.developer.driverkit.userclient-access entitlement from your app unless you have received the entitlement with the correct value.

taskgated-helper MyDriver: Unsatisfied entitlements: com.apple.developer.driverkit.allow-any-userclient-access amfid /Library/SystemExtensions/B7624EEF-3688-4735-A58B-26FEF4DE353C/MyDriver.dext/MyDriver signature not valid: -67671

Does the provisioning profile for your dext contain this entitlement?

Result: Driver extension is started but we still see opening service: (iokit/common) general error

Likely due to the presence of both entitlements, or potentially because:

<code block>

Your driver contains no implementation of ::NewUserClient. This means that it can't accept the UserClient connection. Please make sure to read the documentation around the sample app, implement ::NewUserClient, and ensure that your dext's Info.plist has been appropriately set-up to accept UserClient connections.

@Drewbadour thank you for quick reply and help!

Does the provisioning profile for your dext contain this entitlement?

Yes. I see in provision profile:

<key>com.apple.developer.driverkit.allow-any-userclient-access</key>
<true/>

Your driver contains no implementation of ::NewUserClient. This means that it can't accept the UserClient connection. Please make sure to read the documentation around the sample app, implement ::NewUserClient, and ensure that your dext's Info.plist has been appropriately set-up to accept UserClient connections.

I'm definitely sure that we tried to add virtual kern_return_t NewUserClient(uint32_t type, IOUserClient** userClient) override; into our Driver. So we added this method and implementation into our Driver and now we see different error - (iokit/common) not permitted. This gave me an idea that Driver is not re-installed/updated sometimes. So we added code to make deactivation request and it looks like now we always have new driver version.

Next after reboot and "uninstall first" approach we tested CommunicatingBetweenADriverKitExtensionAndAClientApp again. We can connect to NullDriver and call methods and callback. Unfortunately, we could not connect to NullDriver using our macOS based client and we had same (iokit/common) not permitted. But we can if we set sandbox to false. Next we found temporary exception for iokit:

    <key>com.apple.security.temporary-exception.iokit-user-client-class</key>
    <array>
        <string>IOUserUserClient</string>
    </array>

and now we can connect to NullDriver and our Driver by sand-boxed macOS App. It looks like our issue is solved!

Outstanding question how to make sure that new version of driver is installed and run? Should we increase build version for each driver re-install and check what's active by systemextensionsctl list?

Cannot communicate with DriverKit extension from macOs App due to IOServiceOpen error
 
 
Q