2

I am trying to learn more about DriverKit and memory management, and I read this question:

How to allocate memory in a DriverKit system extension and map it to another process?

And I would like to understand how to use IOMemoryDescriptor::CreateMapping.

I wrote a little app to test this where I do (very simplified code):

uint8_t * buffer = new uint8_t[256];
for (int i = 0 ; i < 256 ; i++)
   buffer[i] = 0xC6;

clientData in, out;
in.nbytes = 256;
in.pbuffer = buffer;
size_t sout = sizeof(out);

IOConnectCallStructMethod(connection, selector,&in,sizeof(in),&out,&sout);

// out.pbuffer now has new values in it

In my Kext user client class, I was doing (I am simplifying):

IOReturn UserClient::MyExtFunction(clientData * in, clientData * out, IOByteCount inSize, IOByteCount * outSize)
{
   MyFunction(in->nBytes, in->pbuffer);//this will change the content of pbuffer

   *out = *in; 
}


IOReturn UserClient::MyFunction(SInt32 nBytesToRead,void* pUserBuffer,SInt32* nBytesRead)
{
    PrepareBuffer(nBytesToRead,&pBuffer);
    ...
    (call function that will fill pBuffer)
}


IOReturn UserClient::PrepareBuffer(UInt32 nBytes,void** pBuffer);
{
   IOMemoryDescriptor * desc = IOMemoryDescriptor::withAddressRange((mach_vm_address_t)*pBuffer,nBytes,direction, owner task);

   desc->prepare();
   IOMemoryMap * map = desc->map();
   *pBuffer = (void*)map->getVirtualAddress();

   return kIOReturnSuccess;
}

This is what I don't know how to reproduce in a DExt and where I think I really don't understand the basic of CreateMapping.

Or is what I used to do not possible?

In my driver, this is where I don't know how to use CreateMapping and IOMemoryMap so this buffer can be mapped to a memory location and updated with different values.

I can create an IOBufferMemoryDescriptor but how do I tie it to the buffer from my application? I also don't understand the various options for CreateMapping.

Please note that in another test app I have successfully used IOConnectMapMemory64()/CopyClientMemoryForType() but I would like to learn specifically about CreateMapping().

(I hope it is alright I edited this question a lot... still new to StackOverflow)

4

2 回答 2

3

还是我以前做不到的?

简而言之,没有。

您正在尝试映射任意用户进程内存,客户端应用程序未使用 IOKit 明确将其标记为可供驱动程序使用。这不符合 Apple 关于安全、安保和沙盒的想法,因此此类东西在 DriverKit 中不可用。

显然,kexts 是无所不能的,所以这在以前是可能的,事实上,我自己在运送驱动程序时使用了这种技术,然后在将所说的 kexts 移植到 DriverKit 时遇到了麻烦。

据我所知,直接访问客户端进程内存的唯一方法是:

  • 通过将缓冲区 >= 4097 字节作为结构输入或输出参数传递给IOConnectCall…Method()s,以便它们IOMemoryDescriptor在驱动程序中作为 s 到达。请注意,这些可以长期保留在驱动程序中,但至少对于输入结构,用户空间端的更新不会反映在驱动程序端,因为使用了写时复制映射。因此,它们应该纯粹用于在预期方向上发送数据。
  • 由用户进程使用/将现有映射IOMemoryDescriptor到其空间。IOConnectMapMemory64()CopyClientMemoryForType()

这确实意味着您不能使用像您正在使用的那样的间接数据结构。您必须使用“打包”结构或索引到持久共享缓冲区。

通过“打包”结构,我的意思是包含诸如 your 之类的标头结构的缓冲区clientData,其后在连续内存中是进一步的数据,例如 your buffer,通过偏移量将其引用到此连续内存中。整个连续的内存块可以作为输入结构传递。

我已向 Apple 提交反馈,要求提供更强大的机制来在用户客户端和 dext 之间交换数据;我不知道它是否会实施,但如果这样的设施有用,我建议你也这样做。(用例子解释你想用它做什么)我们报告的越多,它发生的可能性就越大。(IOMemoryDescriptor::CreateSubMemoryDescriptor()在我提出请求后添加;我不会声称我是第一个这样做的人,或者 Apple 不打算在我提出建议之前添加它,但他们正在积极改进 DriverKit API。)

问题之前的原始答案被编辑得更具体:

(保留,因为它概括地解释了如何处理外部方法的缓冲区参数,这可能对未来的读者有帮助。)

你的问题有点含糊,但让我看看我是否能弄清楚你在 kext 中做了什么,以及你在 dext 中做了什么:

  • 您正在调用IOConnectCallStructMethod(connection, selector, buffer, 256, NULL, NULL);您的应用程序。这意味着buffer作为“结构输入”参数传递给您的外部方法。
  • 因为您的缓冲区是 256 字节长,小于或等于,所以缓冲区的内容被sizeof(io_struct_inband_t)内发送到内核- 换句话说,它在调用时被复制。IOConnectCallStructMethod()
  • 这意味着在您的 kext 的外部方法调度函数中,结构输入是通过传入结构中的structureInput/structureInputSize字段传递的IOExternalMethodArgumentsstructureInput是内核上下文中的指针,可以直接取消引用。该指针仅在您的方法分派执行期间有效,并且在方法同步返回后不能使用。
  • 如果您需要将缓冲区用于设备 I/O,则可能需要将其包装在IOMemoryDescriptor. 一种方法确实是通过IOMemoryDescriptor::CreateMapping().
  • 如果缓冲区是 4097 字节或更大,它将通过 传递structureInputDescriptor IOMemoryDescriptor,它可以直接传递到设备 I/O,或者内存映射以在内核中取消引用。此内存描述符直接引用用户进程的内存。

DriverKit 扩展的功能受到了相当多的限制,但外部方法参数的到达方式几乎完全相同。

  • 小型结构通过IOUserClientMethodArguments'structureInput字段到达,该字段指向一个OSDataobject。您可以通过getBytesNoCopy()/getLength()方法访问内容。
  • IOMemoryDescriptor如果您在前向 I/O中需要这些数据,我所知道的唯一方法是创建一个IOBufferMemoryDescriptorusingIOUSBHostDevice::CreateIOBuffer()IOBufferMemoryDescriptor::Create然后将数据从OSData对象复制到缓冲区中。
  • 大型缓冲区再次通过IOMemoryDescriptor. 您可以将其传递给 I/O 函数,或使用将其映射到驱动程序的地址空间CreateMapping()
于 2020-10-09T09:50:40.923 回答
-1
namespace
{
    /*
    **********************************************************************************
    **          create a memory descriptor and map its address
    **********************************************************************************
    */
    IOReturn arcmsr_userclient_create_memory_descriptor_and_map_address(const void* address, size_t length, IOMemoryDescriptor** memory_descriptor) 
    {
        IOBufferMemoryDescriptor *buffer_memory_descriptor = nullptr;
        uint64_t buffer_address;
        uint64_t len;

    #if ARCMSR_DEBUG_IO_USER_CLIENT
        arcmsr_debug_print("ArcMSRUserClient: *******************************************************\n");
        arcmsr_debug_print("ArcMSRUserClient: ** IOUserClient IOMemoryDescriptor create_with_bytes   \n");
        arcmsr_debug_print("ArcMSRUserClient: *******************************************************\n");
    #endif
        if (!address || !memory_descriptor) 
        {
            return kIOReturnBadArgument;
        }
        if (IOBufferMemoryDescriptor::Create(kIOMemoryDirectionInOut, length, 0, &buffer_memory_descriptor) != kIOReturnSuccess) 
        {
            if (buffer_memory_descriptor) 
            {
                OSSafeReleaseNULL(buffer_memory_descriptor);
            }
            return kIOReturnError;
        }
        if (buffer_memory_descriptor->Map(0, 0, 0, 0, &buffer_address, &len) != kIOReturnSuccess) 
        {
            if (buffer_memory_descriptor) 
            {
                OSSafeReleaseNULL(buffer_memory_descriptor);
            }
            return kIOReturnError;
        }
        if (length != len) 
        {
            if (buffer_memory_descriptor) 
            {
                OSSafeReleaseNULL(buffer_memory_descriptor);
            }
            return kIOReturnNoMemory;
        }
        memcpy(reinterpret_cast<void*>(buffer_address), address, length);
        *memory_descriptor = buffer_memory_descriptor;
        return kIOReturnSuccess;
    }
}  /* namespace */
于 2021-04-09T07:07:37.623 回答