Commit message: None
CVE: CVE-2016-1663
Vulnerability description: The SerializedScriptValue::transferArrayBuffers function in WebKit/Source/bindings/core/v8/SerializedScriptValue.cpp in the V8 bindings in Blink, as used in Google Chrome before 50.0.2661.94, mishandles certain array-buffer data structures, which allows remote attackers to cause a denial of service (use-after-free) or possibly have unspecified other impact via a crafted web site.

Project: Chrome

static void acculumateArrayBuffersForAllWorlds(v8::Isolate* isolate, DOMArrayBuffer* object, Vector<v8::Local<v8::ArrayBuffer>, 4>& buffers)
{
    if (isMainThread()) {
        Vector<RefPtr<DOMWrapperWorld>> worlds;
        DOMWrapperWorld::allWorldsInMainThread(worlds);
        for (size_t i = 0; i < worlds.size(); i++) {
            v8::Local<v8::Object> wrapper = worlds[i]->domDataStore().get(object, isolate);
            if (!wrapper.IsEmpty())
                buffers.append(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
        }
    } else {
        v8::Local<v8::Object> wrapper = DOMWrapperWorld::current(isolate).domDataStore().get(object, isolate);
        if (!wrapper.IsEmpty())
            buffers.append(v8::Local<v8::ArrayBuffer>::Cast(wrapper));
    }
}

Project: Chrome

bool SerializedScriptValue::containsTransferableArrayBuffer() const
{
    return m_arrayBufferContentsArray && !m_arrayBufferContentsArray->isEmpty();
}

Project: Chrome

v8::Local<v8::Value> SerializedScriptValue::deserialize(MessagePortArray* messagePorts)
{
    return deserialize(v8::Isolate::GetCurrent(), messagePorts, 0);
}

Project: Chrome

v8::Local<v8::Value> SerializedScriptValue::deserialize(v8::Isolate* isolate, MessagePortArray* messagePorts, const WebBlobInfoArray* blobInfo)
{
    return SerializedScriptValueFactory::instance().deserialize(this, isolate, messagePorts, blobInfo);
}

Project: Chrome

bool SerializedScriptValue::extractTransferables(v8::Isolate* isolate, v8::Local<v8::Value> value, int argumentIndex, Transferables& transferables, ExceptionState& exceptionState)
{
    if (isUndefinedOrNull(value))
        return true;

    uint32_t length = 0;
    if (value->IsArray()) {
        v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(value);
        length = array->Length();
    } else if (!toV8Sequence(value, length, isolate, exceptionState)) {
        if (!exceptionState.hadException())
            exceptionState.throwTypeError(ExceptionMessages::notAnArrayTypeArgumentOrValue(argumentIndex + 1));
        return false;
    }

    v8::Local<v8::Object> transferableArray = v8::Local<v8::Object>::Cast(value);

    for (unsigned i = 0; i < length; ++i) {
        v8::Local<v8::Value> transferableObject;
        if (!transferableArray->Get(isolate->GetCurrentContext(), i).ToLocal(&transferableObject))
            return false;
        if (isUndefinedOrNull(transferableObject)) {
            exceptionState.throwTypeError("Value at index " + String::number(i) + " is an untransferable " + (transferableObject->IsUndefined() ? "'undefined'" : "'null'") + " value.");
            return false;
        }
        if (V8MessagePort::hasInstance(transferableObject, isolate)) {
            MessagePort* port = V8MessagePort::toImpl(v8::Local<v8::Object>::Cast(transferableObject));
            if (transferables.messagePorts.contains(port)) {
                exceptionState.throwDOMException(DataCloneError, "Message port at index " + String::number(i) + " is a duplicate of an earlier port.");
                return false;
            }
            transferables.messagePorts.append(port);
        } else if (V8ArrayBuffer::hasInstance(transferableObject, isolate)) {
            DOMArrayBuffer* arrayBuffer = V8ArrayBuffer::toImpl(v8::Local<v8::Object>::Cast(transferableObject));
            if (transferables.arrayBuffers.contains(arrayBuffer)) {
                exceptionState.throwDOMException(DataCloneError, "ArrayBuffer at index " + String::number(i) + " is a duplicate of an earlier ArrayBuffer.");
                return false;
            }
            transferables.arrayBuffers.append(arrayBuffer);
        } else if (V8SharedArrayBuffer::hasInstance(transferableObject, isolate)) {
            DOMSharedArrayBuffer* sharedArrayBuffer = V8SharedArrayBuffer::toImpl(v8::Local<v8::Object>::Cast(transferableObject));
            if (transferables.arrayBuffers.contains(sharedArrayBuffer)) {
                exceptionState.throwDOMException(DataCloneError, "SharedArrayBuffer at index " + String::number(i) + " is a duplicate of an earlier SharedArrayBuffer.");
                return false;
            }
            transferables.arrayBuffers.append(sharedArrayBuffer);
        } else if (V8ImageBitmap::hasInstance(transferableObject, isolate)) {
            ImageBitmap* imageBitmap = V8ImageBitmap::toImpl(v8::Local<v8::Object>::Cast(transferableObject));
            if (transferables.imageBitmaps.contains(imageBitmap)) {
                exceptionState.throwDOMException(DataCloneError, "ImageBitmap at index " + String::number(i) + " is a duplicate of an earlier ImageBitmap.");
                return false;
            }
            transferables.imageBitmaps.append(imageBitmap);
        } else {
            exceptionState.throwTypeError("Value at index " + String::number(i) + " does not have a transferable type.");
            return false;
        }
    }
    return true;
}

Project: Chrome

PassRefPtr<SerializedScriptValue> SerializedScriptValue::nullValue()
{
    SerializedScriptValueWriter writer;
    writer.writeNull();
    String wireData = writer.takeWireString();
    return adoptRef(new SerializedScriptValue(wireData));
}

Project: Chrome

void SerializedScriptValue::transferImageBitmaps(v8::Isolate* isolate, const ImageBitmapArray& imageBitmaps, ExceptionState& exceptionState)
{
    if (!imageBitmaps.size())
        return;

    for (size_t i = 0; i < imageBitmaps.size(); ++i) {
        if (imageBitmaps[i]->isNeutered()) {
            exceptionState.throwDOMException(DataCloneError, "ImageBitmap at index " + String::number(i) + " is already neutered.");
            return;
        }
    }

    OwnPtr<ImageBitmapContentsArray> contents = adoptPtr(new ImageBitmapContentsArray);
    HeapHashSet<Member<ImageBitmap>> visited;
    for (size_t i = 0; i < imageBitmaps.size(); ++i) {
        if (visited.contains(imageBitmaps[i]))
            continue;
        visited.add(imageBitmaps[i]);
        contents->append(imageBitmaps[i]->transfer());
    }
    m_imageBitmapContentsArray = contents.release();
}