November 14, 2020

Don’t place a port in shared memory


Today I read a very interesting blogpost by Brandon Azad from Google Project 0 ( , which disclosed an incorrect bound-check in the function H11ANEInDirectPathClient::externalMethod, allowing to invoke (more privileged) H11ANEInUserClient’s external methods in the context of (less privileged) H11ANEInDirectPathClient, eventually leading to type confusions or other security issues.

It is very likely a copy-paste bug. Similar to Brandon, I also missed the vulnerability! It’s kind of unacceptable. Following Brandon’s blogpost, we will share another bug in the H11ANEIn driver.


The H11ANEIn driver was first introduced in iOS 12 for A12 devices (e.g., iPhone XS and XS Max).  I think that “ANE” stands for “Apple Neural Engine”.

The H11ANEIn driver offers two types of IOUserClients, H11ANEInDirectPathClient and H11ANEInUserClient. In particular, H11ANEInUserClient is more powerful than H11ANEInDirectPathClient, as H11ANEInUserClient has more external methods than H11ANEInDirectPathClient. However, creating H11ANEInUserClient requires a special entitlement, Only a few executables on iOS have the entitlements, such as aned and mediaserverd.


Fortunately, the container sandbox allows to open the H11ANEInDirectPathClient IOUserClient. So third-party apps can open and communicate with H11ANEInDirectPathClient IOUserClients. Looking at the function H11ANEInDirectPathClient::externalMethod, (Ok, please forget the incorrect bound-check here), we can find that H11ANEInDirectPathClient actually has the following interfaces:

  • _ANE_DeviceOpen
  • _ANE_DeviceClose
  • _ANE_ProgramSendRequest

Now let’s take a look at the interface _ANE_ProgramSendRequest. Function _ANE_ProgramSendRequest accepts a structural input with length 16.

In fact, at the very beginning of function _ANE_ProgramSendRequest, it would treat the structural input as two uint64 values, and pass them into IOMemoryDescriptor::withAddressRange();

Clearly, the first value is a userspace address, and the second value is the length of the userspace buffer. After creating the IOMemoryDescriptor_ANE_ProgramSendRequest continues to call the prepare and map functions, and map the memory into the kernel space. In other words, _ANE_ProgramSendRequest creates a shared memory now.

More importantly, the _ANE_ProgramSendRequest interface should be called through the IOConnectCallAsyncMethod function. IOConnectCallAsyncMethod allows the IOKit extension to asynchronously process requests from the users-pace. By supplying a notification/wakeup port, a user-space program will receive a notification message when the request is done.  It implies that the kernel or the extension should store the wakeup port somewhere so that the kernel or the extension can send the notification later.

The vulnerability and exploit

The vulnerability is that, _ANE_ProgramSendRequest records the notification context, including the port pointer, in the shared memory!

As we can see above, before invoking H11ANEIn::ANE_ProgramSendRequest, the wakeup port is stored at the shared memory at offset 2616 (note that the offsets vary across iOS kernel versions ).

It’s quite straightforward to convert the vulnerability into an info leak.  We can easily get a port pointer in the shared memory after we trigger the execution of H11ANEInDirectPathClient::_ANE_ProgramSendRequest.

Beyond the info leak, you may have already realized that there are a lot of chances to further exploit the vulnerability, especially for the readers who can still remember the vulnerability exploited by the very first Pangu jailbreak tool (for iOS 7.4.1).

Just a quick recap. At that time, IOSharedDataQueue stores a mach message header in shared memory. We achieve the kernel exploit by crafting the mach message header (

Similar here, we can modify the port pointer in the shared memory so that when a notification is to be sent to a fake (arbitrary) port. This blog won’t go to details of the exploit. There are so many different ways to accomplish a kernel exploit when you have a full control to a port pointer.

Things I haven’t mentioned

It wouldn’t be that easy to reproduce the vulnerability, because you will find that H11ANEIn::ANE_ProgramSendRequest will not deliver a notification to the user-space app unless you have a properly-filled request.

First, H11ANEInDirectPathClient::ANE_DeviceOpen needs to be executed before H11ANEIn::ANE_ProgramSendRequest. Second, H11ANEIn::ANE_ProgramSendRequest will eventually go to the function H11ANEIn::ANE_ProgramSendRequest_gated.

Both  H11ANEInDirectPathClient::ANE_DeviceOpen and H11ANEIn::ANE_ProgramSendRequest_gated have a common operation: looking up a program buffer according to a program handle id. This program handle id comes from the user input. Without a correct program handle id, H11ANEIn::ANE_ProgramSendRequest would simply return. No notification would be sent.

So what is the program buffer and the program handle id? Remember that there is another more privileged IOUserClient, H11ANEInUserClient. H11ANEInUserClient has the interfaces to create and destroy ane programs and get the program handle ids:

  • H11ANEInUserClient::_ANE_ProgramCreate
  • H11ANEInUserClient:: _ANE_ProgramCreateInstance
  • H11ANEInUserClient::_ANE_ProgramDestroy

However, container apps cannot create H11ANEInUserClient, what can we do?

I had to spend some time on machine learning.  It’s true. I learned how to use the Core ML framework (, and how to integrate machine learning models into my app. I tried all the models on the page .

Finally, I figured out that our app can talk to aned, and then ask aned to create a H11ANEInUserClient and create an ane program. After aned returns a program handle id to my app, we can continue to develop our exploit. So, if you want to reproduce the vulnerability, learn machine learning first!

Thank you for your time.