Skip to content

Commit

Permalink
[CVE-2017-0236] Bug with array buffer detach
Browse files Browse the repository at this point in the history
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 10, 2017
1 parent a1345ad commit 1ae7e3c
Show file tree
Hide file tree
Showing 6 changed files with 159 additions and 6 deletions.
145 changes: 140 additions & 5 deletions lib/Runtime/Library/ArrayBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ namespace Js
return toReturn;
}

void ArrayBuffer::ClearParentsLength(ArrayBufferParent* parent)
void ArrayBuffer::DetachBufferFromParent(ArrayBufferParent* parent)
{
if (parent == nullptr)
{
Expand All @@ -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:
Expand All @@ -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());
});
}

Expand Down
2 changes: 1 addition & 1 deletion lib/Runtime/Library/ArrayBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions lib/Runtime/Library/DataView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<>
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/Library/DataView.h
Original file line number Diff line number Diff line change
Expand Up @@ -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, ...);
Expand Down
8 changes: 8 additions & 0 deletions lib/Runtime/Library/TypedArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/Library/TypedArray.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down

0 comments on commit 1ae7e3c

Please sign in to comment.