Permalink
Browse files

[CVE-2017-0236] Bug with array buffer detach

Change the vtable of virtual typed arrays to regular typed arrays upon array buffer detach to prevent writes to detached buffer in the jitted code.
  • Loading branch information...
rajatd committed May 8, 2017
1 parent a1345ad commit 1ae7e3ce95515758b4cd7215cb4e48539a0f4031
@@ -38,7 +38,7 @@ namespace Js
return toReturn;
}
void ArrayBuffer::ClearParentsLength(ArrayBufferParent* parent)
void ArrayBuffer::DetachBufferFromParent(ArrayBufferParent* parent)
{
if (parent == nullptr)
{
@@ -48,23 +48,158 @@ namespace Js
switch (JavascriptOperators::GetTypeId(parent))
{
case TypeIds_Int8Array:
if (Int8VirtualArray::Is(parent))
{
if (VirtualTableInfo<Int8VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Int8Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Int8VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Int8Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Uint8Array:
if (Uint8VirtualArray::Is(parent))
{
if (VirtualTableInfo<Uint8VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Uint8Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Uint8VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Uint8Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Uint8ClampedArray:
if (Uint8ClampedVirtualArray::Is(parent))
{
if (VirtualTableInfo<Uint8ClampedVirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Uint8ClampedArray>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Uint8ClampedVirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Uint8ClampedArray>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Int16Array:
if (Int16VirtualArray::Is(parent))
{
if (VirtualTableInfo<Int16VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Int16Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Int16VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Int16Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Uint16Array:
if (Uint16VirtualArray::Is(parent))
{
if (VirtualTableInfo<Uint16VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Uint16Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Uint16VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Uint16Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Int32Array:
if (Int32VirtualArray::Is(parent))
{
if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Int32Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Int32VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Int32Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Uint32Array:
if (Uint32VirtualArray::Is(parent))
{
if (VirtualTableInfo<Uint32VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Uint32Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Uint32VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Uint32Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Float32Array:
if (Float32VirtualArray::Is(parent))
{
if (VirtualTableInfo<Float32VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Float32Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Float32VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Float32Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Float64Array:
if (Float64VirtualArray::Is(parent))
{
if (VirtualTableInfo<Float64VirtualArray>::HasVirtualTable(parent))
{
VirtualTableInfo<Float64Array>::SetVirtualTable(parent);
}
else
{
Assert(VirtualTableInfo<CrossSiteObject<Float64VirtualArray>>::HasVirtualTable(parent));
VirtualTableInfo<CrossSiteObject<Float64Array>>::SetVirtualTable(parent);
}
}
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_Int64Array:
case TypeIds_Uint64Array:
case TypeIds_CharArray:
case TypeIds_BoolArray:
TypedArrayBase::FromVar(parent)->length = 0;
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
case TypeIds_DataView:
DataView::FromVar(parent)->length = 0;
DataView::FromVar(parent)->ClearLengthAndBufferOnDetach();
break;
default:
@@ -90,14 +225,14 @@ namespace Js
if (this->primaryParent != nullptr)
{
this->ClearParentsLength(this->primaryParent->Get());
this->DetachBufferFromParent(this->primaryParent->Get());
}
if (this->otherParents != nullptr)
{
this->otherParents->Map([&](RecyclerWeakReference<ArrayBufferParent>* item)
{
this->ClearParentsLength(item->Get());
this->DetachBufferFromParent(item->Get());
});
}
@@ -50,7 +50,7 @@ namespace Js
DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBuffer, ArrayBufferBase);
#define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 //4GB
private:
void ClearParentsLength(ArrayBufferParent* parent);
void DetachBufferFromParent(ArrayBufferParent* parent);
public:
template <typename FreeFN>
class ArrayBufferDetachedState : public ArrayBufferDetachedStateBase
@@ -666,6 +666,14 @@ namespace Js
return FALSE;
}
void DataView::ClearLengthAndBufferOnDetach()
{
AssertMsg(this->GetArrayBuffer()->IsDetached(), "Array buffer should be detached if we're calling this method");
this->length = 0;
this->buffer = nullptr;
}
#ifdef _M_ARM
// Provide template specialization (only) for memory access at unaligned float/double address which causes data alignment exception otherwise.
template<>
@@ -50,6 +50,7 @@ namespace Js
}
uint32 GetByteOffset() const { return byteOffset; }
void ClearLengthAndBufferOnDetach();
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
static Var EntryGetInt8(RecyclableObject* function, CallInfo callInfo, ...);
@@ -1117,6 +1117,14 @@ namespace Js
}
void TypedArrayBase::ClearLengthAndBufferOnDetach()
{
AssertMsg(IsDetachedBuffer(), "Array buffer should be detached if we're calling this method");
this->length = 0;
this->buffer = nullptr;
}
Var TypedArrayBase::EntryGetterBuffer(RecyclableObject* function, CallInfo callInfo, ...)
{
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
@@ -157,6 +157,7 @@ namespace Js
uint32 GetBytesPerElement() const { return BYTES_PER_ELEMENT; }
byte* GetByteBuffer() const { return buffer; };
bool IsDetachedBuffer() const { return this->GetArrayBuffer()->IsDetached(); }
void ClearLengthAndBufferOnDetach();
static Var CommonSet(Arguments& args);
static Var CommonSubarray(Arguments& args);

0 comments on commit 1ae7e3c

Please sign in to comment.