c++ - IOUserClientMethodArguments 完成值始终为 NULL

标签 c++ iokit driverkit

我正在尝试使用 IOConnectCallAsyncStructMethod 在 iPadOS 版 DriverKit 中设置客户端和驱动程序之间的回调。

这就是我调用IOConnectCallAsyncStructMethod

的方式
    ret = IOConnectCallAsyncStructMethod(connection, MessageType_RegisterAsyncCallback, masterPort, asyncRef, kIOAsyncCalloutCount, nullptr, 0, &outputAssignCallback, &outputSize);

其中asyncRef是:

    asyncRef[kIOAsyncCalloutFuncIndex] = (io_user_reference_t)AsyncCallback;
    asyncRef[kIOAsyncCalloutRefconIndex] = (io_user_reference_t)nullptr;

AsyncCallback是:

static void AsyncCallback(void* refcon, IOReturn result, void** args, uint32_t numArgs)
{
    const char* funcName = nullptr;
    uint64_t* arrArgs = (uint64_t*)args;
    ReadDataStruct* output = (ReadDataStruct*)(arrArgs + 1);

    switch (arrArgs[0])
    {
        case 1:
        {
            funcName = "'Register Async Callback'";
        } break;

        case 2:
        {
            funcName = "'Async Request'";
        } break;

        default:
        {
            funcName = "UNKNOWN";
        } break;
    }

    printf("Got callback of %s from dext with returned data ", funcName);
    
    printf("with return code: 0x%08x.\n", result);

    // Stop the run loop so our program can return to normal processing.
    CFRunLoopStop(globalRunLoop);
}

但是 IOConnectCallAsyncStructMethod 始终返回 kIOReturnBadArgument 并且我可以看到,当该方法:

kern_return_t MyDriverClient::ExternalMethod(uint64_t selector, IOUserClientMethodArguments* arguments, const IOUserClientMethodDispatch* dispatch, OSObject* target, void* reference) {
    
    kern_return_t ret = kIOReturnSuccess;

    if (selector < NumberOfExternalMethods)
    {
        dispatch = &externalMethodChecks[selector];
        if (!target)
        {
            target = this;
        }
    }

    return super::ExternalMethod(selector, arguments, dispatch, target, reference);

被调用,在IOUserClientMethodArguments*参数中,完成是completion =(OSAction •) NULL

这是我用来检查值的IOUserClientMethodDispatch:

    [ExternalMethodType_RegisterAsyncCallback] =
    {
        .function = (IOUserClientMethodFunction) &Mk1dDriverClient::StaticRegisterAsyncCallback,
        .checkCompletionExists = true,
        .checkScalarInputCount = 0,
        .checkStructureInputSize = 0,
        .checkScalarOutputCount = 0,
        .checkStructureOutputSize = sizeof(ReadDataStruct),
    },

知道我做错了什么吗?或者还有其他想法吗?

最佳答案

kIOReturnBadArgument 的可能原因:

方法调用中的端口参数看起来很可疑:

IOConnectCallAsyncStructMethod(connection, MessageType_RegisterAsyncCallback, masterPort, …
------------------------------------------------------------------------------^^^^^^^^^^

如果您将 IOKit 主/主端口 ( kIOMasterPortDefault ) 传递到此处,那么这是错误的。该参数的目的是提供一个通知 Mach 端口,该端口将接收异步完成消息。您需要创建一个端口并将其调度到适当的调度队列或运行循环上。我通常使用这样的东西:

    // Save this somewhere for the entire time you might receive notification callbacks:
    IONotificationPortRef notify_port = IONotificationPortCreate(kIOMasterPortDefault);
    // Set the GCD dispatch queue on which we want callbacks called (can be main queue):
    IONotificationPortSetDispatchQueue(notify_port, callback_dispatch_queue);
    // This is what you pass to each async method call:
    mach_port_t callback_port = IONotificationPortGetMachPort(notify_port);

完成通知端口后,请确保使用 IONotificationPortDestroy() 销毁它。 .

看起来您可能正在使用运行循环。在这种情况下,不要调用 IONotificationPortSetDispatchQueue ,您可以使用IONotificationPortGetRunLoopSource函数来获取通知端口的运行循环源,然后您可以将其安排在CFRunloop上您正在使用的对象。

有关异步完成参数的一些注意事项:

您还没有发布您的 DriverKit 端 AsyncCompletion()调用,无论如何这不会导致您立即出现问题,但一旦您修复异步调用本身,它可能会崩溃:

如果您的异步完成仅传递 2 个用户参数,则您在应用程序端使用了错误的回调函数签名。而不是IOAsyncCallback您必须使用IOAsyncCallback2表格。

此外,即使您传递 3 个或更多参数,其中 IOAsyncCallback形式是正确的,我相信这段代码在技术上会由于别名规则而触发未定义的行为:

    uint64_t* arrArgs = (uint64_t*)args;
    ReadDataStruct* output = (ReadDataStruct*)(arrArgs + 1);

    switch (arrArgs[0])

我认为以下内容是正确的:

    ReadDataStruct* output = (ReadDataStruct*)(args + 1);

    switch ((uintptr_t)args[0])

(不要转换数组指针本身,而是转换每个 void* 元素。)

有关异步输出结构参数的注释

我注意到您的异步方法调用中有一个结构输出参数,其缓冲区看起来相当小。如果您计划在初始ExternalMethod返回后使用DriverKit端的数据更新它,您可能会感到惊讶:输出结构参数不是作为 IOMemoryDescriptor 传递的。将在方法返回时立即复制到应用程序端,而不是在触发异步完成时。

那么如何解决这个问题呢?对于非常小的数据,将其传递到异步完成参数本身中。对于任意大小的字节缓冲区,我知道的唯一方法是确保输出结构参数通过 IOMemoryDescriptor 传递,它可以在驱动程序和应用程序进程之间的共享映射中持久地进行内存映射。好的,如何将它作为内存描述符传递?基本上,输出结构必须大于 4096 字节。是的,这本质上意味着如果您必须使缓冲区变得异常大。

关于c++ - IOUserClientMethodArguments 完成值始终为 NULL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74478546/

相关文章:

c++ - 由于 C++ 模式下的缩进,Emacs imenu 和 speedbar+semantic 失败

macos - 从无代码 KEXT 迁移到 DEXT 后的性能问题

c++ - 如何使用 IOConnectCallStructMethod 发送数据作为输入

macos - PCIDriverKit 授权检查失败(将 KEXT 迁移到 DEXT)

c - fpe2 和 sp78 数据类型?

c++ - 我可以在不等待 future 限制的情况下使用 std::async 吗?

c++ - 替换而不升压

c++ - WriteProcessMemory() 返回 487?使用 CheatEngine 找到的值

ios - IOHIDEventSystemClientScheduleWithRunLoop 与 EXC_BAD_ACCESS

macos - 我可以使用守护程序打开 DriverKit 用户客户端吗?