I am developing a dext that is running into issues pertaining to IOConnectMapMemory (at least I think so).
There are 3 parts of code that are involved, the dext (which allocates the memory in the first place), a user client library which is involved in connecting to the dext and releasing when the hardware is removed, and finally some processing code (at the user level) which executes on this shared block of memory from the dext.
The shared memory is allocated using an IOBufferMemoryDescriptor:
IOBufferMemoryDescriptor::Create(kIOMemoryDirectionNone, sizeof(sharedMemoryBlock), IOVMPageSize, &(ivars->mSharedMemoryBlockMemDesc));
The User Client Library acquires a mapped pointer to this memory by calling IOConnectMapMemory:
IOConnectMapMemory(mConnect, kMemoryType_SharedMemoryBlock,
mach_task_self(),
(mach_vm_address_t*)&mUserClientSharedBlockPtr,
(mach_vm_size_t*)&mSizeOfUserClientSharedBlock, kIOMapAnywhere);
…which triggers the dext’s IOUserClient subclass' “CopyClientMemoryForType_Impl”. That code adds a retain and returns a pointer to the IOBufferMemoryDescriptor:
case kMemoryType_SharedMemoryBlock:
// error checks first (make sure it’s allocated and initialized, etc)
ivars->mSharedMemoryBlockMemDesc->retain();
*memory = ivars->mSharedMemoryBlockMemDesc;
break;
IOConnectMapMemory returns the “mapped pointer” (in mUserClientSharedBlockPtr) to the User Client Library, which in turn provides it to the processing code.
The processing code checks the pointer validity, and if valid, runs its processing. This worked fine with a kext implementation. This fails with dext implementation, because during the processing call (after validity check but during usage) the mapped pointer can become NULL, which seems to be against the design pattern, and causes the application to crash due to an access violation (dereferencing NULL).
I assume I am doing something incorrect here, but I’m not seeing what it is. The memory was retained, so it should not be deleted until the User Client Library has released it, but the only release available would be IOConnectUnmapMemory, and that fails with “invalid argument” (0xE00002C2) after the device is hot-unplugged.
I am not finding IOConnectMapMemory examples on developer.apple.com. I have verified via the forums that IOConnectMapMemory is still a recommended practice with DriverKit development: https://developer.apple.com/forums/thread/803947?answerId=862249022#862249022 .
What’s the trick here for stability? The device is a peripheral which can be unplugged or turned off at any time, which would result in the dext and user client library code tearing down the structures and memory, but it should be able to do so safely without causing access violations.
(Note, this has been simplified for the purpose of focusing the question, in reality there are 4 separate memory blocks which are shared in this fashion: two ring buffers, main engine status, and client status. They each use their own memory_type definition, but a general solution is needed and can be applied to all 4, and there can be multiple clients at any point in time).