[Apple's comment](https://github.com/apple-oss-distributions/IOHIDFamily/blob/19666c840a6d896468416ff0007040a10b7b46b8/IOHIDFamily/IOHIDDevice.cpp#L1601) from the source code when this issue was fixed sums this up nicely:
14
14
15
+
```cpp
15
16
// Find the number of cookies in the data. The data from elementData is shared with user space and may change at any time.
17
+
```
16
18
17
19
Let us have a look at the function before the patch (I have tried to label relevant lines):
require_action(elementVal, fail, ret = kIOReturnNoMemory);
81
+
element->setDataBits(elementVal);
82
+
elementVal->release();
84
83
85
-
// Actually post elements
86
-
ret = postElementValues((IOHIDElementCookie *)cookies, (UInt32)cookieCount, 0, completionTimeout, completion);
84
+
cookies[index] = headerPtr->cookie; // [6]
85
+
}
87
86
88
-
fail:
89
-
WORKLOOP_UNLOCK;
90
-
if (cookies != &cookies_[0]) {
91
-
IOFreeData(cookies, cookieSize);
92
-
}
87
+
// Actually post elements
88
+
ret = postElementValues((IOHIDElementCookie *)cookies, (UInt32)cookieCount, 0, completionTimeout, completion);
93
89
94
-
return ret;
90
+
fail:
91
+
WORKLOOP_UNLOCK;
92
+
if (cookies != &cookies_[0]) {
93
+
IOFreeData(cookies, cookieSize);
95
94
}
95
+
96
+
return ret;
97
+
}
98
+
```
96
99
97
100
- The loop at `[1]` counts the number of `IOHIDElementValue`s in the buffer, and stores this count in `cookieCount`.
98
101
- The check at `[2]` (combined with the condition of the while loop) will make sure that the `length` field of each header does not extend out of the bounds of the `elementData` buffer (nor can it fall short of the end of the buffer, although this is less relevant).
skipped 4 lines
103
106
104
107
So what's the issue? This function behaves entirely correctly when `elementData` is non-volatile, the issue comes when the method is called with shared memory. Enter the `IOHIDInterface::SetElementValues_Impl` DriverKit method:
Here, `postElementTransaction` is called with `md->getBytesNoCopy()`, memory shared with userspace, violating the assumption that `elementData` is non-volatile. The content of the `elementData` buffer can change after the loop at `[1]`, but before the loop at `[4]`, so what does this mean for an attacker?