# CVE-2022-32917: AppleSPU out of bounds write
*Ned Williamson*

## The Basics

**Disclosure or Patch Date:** 2022 September 12

**Product:** Apple iOS, macOS

**Advisory:**

*iOS:* https://support.apple.com/en-us/HT213445
*macOS:* https://support.apple.com/en-us/HT213443

**Affected Versions:** iOS 15.7, macOS 11.7 and earlier

**First Patched Version:** iOS 15.7, macOS 11.7

**Issue/Bug Report:** N/A

**Patch CL:** N/A

**Bug-Introducing CL:** N/A

**Reporter(s):** Anonymous

## The Code

**Proof-of-concept:** This can be triggered by creating an `IOUserClient` to send messages to the `_asyncMessage` object. This can be done by its exposed `RTBuddy` interface.

**Exploit sample:** N/A

**Did you have access to the exploit sample when doing the analysis?** No

## The Vulnerability

**Bug class:** Buffer overflow

**Vulnerability details:**

AppleSPU.kext is a kernel extension that manages various drivers on macOS and iOS. It registers a message handler `AppleSPU::asyncMessage` with an `RTBuddy` endpoint. These endpoints can be accessed by instances of `IOUserClient`. One such user-exposed message with opcode `0x89` (called `ALLOCATE_BUFFER` in the pseudocode below) for the `AppleSPU` endpoint allocates a new buffer in the `AppleSPU` object. Active buffers are tracked with an array of buffer entries that has a static size of 16.

There is no bounds check on the number of allocated buffers, so if more than 16 are allocated, an out of bounds write occurs within the `AppleSPU` object. The pseudocode can be found below:

```c
struct BufferEntry {
    _WORD word1;
    _QWORD *buffer;
    _QWORD unk2;
    _QWORD unk3;
    _QWORD size;
}

struct AppleSPU {
  // ...
  BufferEntry buffer_entries[16];
  unsigned int num_entries;
  // ...
}

void __fastcall AppleSPU::_asyncMessage(AppleSPU *this, void *a2, MessageDataType *message_data)
{
  while ( 1 )
  {
    // ...
    case ALLOCATE_BUFFER:
      size = this->AppleSPU.processor_memory_alignment * message_data->num_elements;
      buffer = ((__int64 (*)(void))this->AppleSPU.processor->vtable->procesor.allocate_buffer)(size);
      if ( !buffer )
        panic();
      v33 = (*(__int64 (**)(void))(*(_QWORD *)buffer + 120LL))();
      v34 = (*(__int64 (__fastcall **)(__int64))(*(_QWORD *)buffer + 128LL))(v32);
      entry = this->AppleSPU.buffer_entries[this->AppleSPU.num_entries];
      entry->word1 = message_data->word1;
      entry->buffer = buffer;
      entry->unk2 = v33;
      entry->unk3 = v34;
      entry->size = size;
      this->AppleSPU.num_entries++;
```

**Patch analysis:** A bounds check was added on `AppleSPU::num_entries`.

```diff
    case ALLOCATE_BUFFER:
+      if (this->AppleSPU.num_entries >= 16u)
+        panic("out of buffer entries");
      size = this->AppleSPU.processor_memory_alignment * message_data->num_elements;
      buffer = ((__int64 (*)(void))this->AppleSPU.processor->vtable->procesor.allocate_buffer)(size);
      if ( !buffer )
        panic();
```

**Thoughts on how this vuln might have been found _(fuzzing, code auditing, variant analysis, etc.)_:** This bug is fairly simple, so it could have been found with either fuzzing or code auditing. For fuzzing, some knowledge of the structure of message types would be helpful to find the `ALLOCATE_BUFFER` message type, either by using a seed corpus of valid AppleSPU messages, coverage feedback, or using a grammar derived from manual analysis.

**(Historical/present/future) context of bug:** N/A

## The Exploit

(The terms *exploit primitive*, *exploit strategy*, *exploit technique*, and *exploit flow* are [defined here](https://googleprojectzero.blogspot.com/2020/06/a-survey-of-recent-ios-kernel-exploits.html).)

**Exploit strategy (or strategies):**  N/A, exploit not available

**Exploit flow:**

**Known cases of the same exploit flow:**

**Part of an exploit chain?**

## The Next Steps

### Variant analysis

**Areas/approach for variant analysis (and why):**
Audit for usage of statically-sized arrays and ensure bounds checks are used. Fuzz all endpoints exposed to IOUserClient, including drivers.

**Found variants:** N/A

### Structural improvements

What are structural improvements such as ways to kill the bug class, prevent the introduction of this vulnerability, mitigate the exploit flow, make this type of vulnerability harder to exploit, etc.?

**Ideas to kill the bug class:** Use containers with checked accesses or prove that accesses are safe (e.g. with a language extension, compiler support, etc.).

**Ideas to mitigate the exploit flow:** This is an overflow within a struct, so memory tagging that uses per-allocation granularity is insufficient to detect this issue unless fine-grained tags for each type are used. This is a challenge to accomplish while respecting the C structure layout specification. Therefore broader solutions such as banning C-style arrays within structures or inserting bounds checks with the compiler using the static size is a good approach here.

**Other potential improvements:**

### 0-day detection methods

What are potential detection methods for similar 0-days? Meaning are there any ideas of how this exploit or similar exploits could be detected **as a 0-day**?

## Other References
