问题描述
我试图了解有关DriverKit和内存管理的更多信息,我读了以下问题:
How to allocate memory in a DriverKit system extension and map it to another process?
我想了解如何使用IOMemoryDescriptor::CreateMapping
。
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
在我的Kext用户客户端类中,我正在做(我正在简化):
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;
}
这是我不知道如何在DExt中复制的内容,也是我认为我真的不了解CreateMapping
的基础的地方。
还是我以前无法做到的事情?
在我的驱动程序中,这是我不知道如何使用CreateMapping
和IOMemoryMap
的地方,因此可以将该缓冲区映射到内存位置并使用不同的值进行更新。
我可以创建一个IOBufferMemoryDescriptor
,但是如何将其绑定到应用程序的缓冲区?我也不了解CreateMapping
的各种选项。
请注意,在另一个测试应用中,我已经成功使用了IOConnectMapMemory64()
/ copyClientMemoryForType()
,但是我想专门学习CreateMapping()。
(我希望我对此问题进行了很多编辑……对于StackOverflow来说还很新)
解决方法
还是我以前无法做到的事情?
简而言之,不是。
您正在尝试映射任意用户进程内存,客户端应用程序未使用IOKit将其明确标记为可用于驱动程序。这与Apple关于安全性,安保性和沙箱的想法不符,因此DriverKit中没有这种东西。
显然,kexts功能强大,因此在以前,这是可能的,实际上,我本人在运送驱动程序时就使用了该技术,然后在将所述kexts移植到DriverKit时遇到了麻烦。
据我所知,直接访问客户端进程内存的唯一方法是:
- 通过传递> = 4097字节的缓冲区作为
IOConnectCall…Method()
的结构输入或输出参数,以便它们作为IOMemoryDescriptor
到达驱动程序。 请注意,这些可以长期保留在驱动程序中,但是至少对于输入结构,由于使用了写时复制映射,因此用户空间方面的更新不会反映在驱动程序方面。em>因此,应仅将它们用于按预期方向发送数据。 - 通过用户流程,使用
IOMemoryDescriptor
/IOConnectMapMemory64()
将现有CopyClientMemoryForType()
映射到其空间中。
这确实意味着您不能使用所使用的间接数据结构。您将必须使用“打包”结构,或在持久共享缓冲区中建立索引。
“打包”结构是指包含标头结构(例如clientData
)的缓冲区,其后在连续存储器中紧跟着其他数据(例如buffer
),并通过偏移量引用到此连续存储器中。整个连续的内存块都可以作为输入结构传递。
我已向Apple提出反馈,要求建立一种更强大的机制来在用户客户端和敏捷之间交换数据;我不知道是否会实施,但如果这样的设施有用,我建议您也这样做。 (通过示例说明您想要使用的内容)我们报告的越多,发生的可能性就越大。 ({IOMemoryDescriptor::CreateSubMemoryDescriptor()
是在我提出请求后添加的;我不会声称自己是第一个这样做的人,或者苹果并没有计划在我的建议之前添加它,但是它们 积极改进DriverKit API。)
对问题之前的原始答案进行了更具体的编辑:
(保留,因为它大体上解释了如何处理外部方法的缓冲区参数,这可能对将来的读者有所帮助。)
您的问题有点含糊,但是让我看看我是否可以算出您在kext中所做的,与您在dext中所做的:
- 您正在应用程序中呼叫
IOConnectCallStructMethod(connection,selector,buffer,256,NULL,NULL);
。这意味着buffer
作为“结构输入”参数传递给您的外部方法。 - 由于缓冲区的长度为256个字节,小于或等于
sizeof(io_struct_inband_t)
,因此缓冲区的内容被发送到内核带内-换句话说,它是在IOConnectCallStructMethod()
调用时复制。 - 这意味着在您的kext的外部方法分派函数中,结构输入是通过传入
structureInput
结构中的structureInputSize
/IOExternalMethodArguments
字段传递的。structureInput
是内核上下文中的指针,可以直接取消引用。 该指针仅在执行方法分派期间有效,并且在方法同步返回后将无法使用。 - 如果需要将缓冲区用于设备I / O,则可能需要将其包装在
IOMemoryDescriptor
中。实际上,一种方法是通过IOMemoryDescriptor::CreateMapping()
。 - 如果缓冲区为4097字节或更大,它将通过
structureInputDescriptor
IOMemoryDescriptor
传递,可以直接传递给设备I / O,也可以通过内存映射来在核心。该内存描述符直接引用用户进程的内存。
DriverKit扩展在功能上有更多限制,但是外部方法参数的到达方式几乎完全相同。
- 小结构通过
IOUserClientMethodArguments
'structureInput
字段到达,该字段指向OSData
object。您可以通过getBytesNoCopy()
/getLength()
方法访问内容。 - 如果您需要
IOMemoryDescriptor
中的这些数据来进行I / O,我知道的唯一方法是使用IOBufferMemoryDescriptor
或IOUSBHostDevice::CreateIOBuffer()
创建IOBufferMemoryDescriptor::Create
并然后将数据从OSData
对象复制到缓冲区中。 - 大型缓冲区已经通过
IOMemoryDescriptor
再次引用。您可以将此传递给I / O函数,或使用CreateMapping()
映射到驱动程序的地址空间。
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,&buffer_memory_descriptor) != kIOReturnSuccess)
{
if (buffer_memory_descriptor)
{
OSSafeReleaseNULL(buffer_memory_descriptor);
}
return kIOReturnError;
}
if (buffer_memory_descriptor->Map(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 */