Skip to content

Commit 1ae7e3c

Browse files
committed
[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.
1 parent a1345ad commit 1ae7e3c

6 files changed

Lines changed: 159 additions & 6 deletions

File tree

lib/Runtime/Library/ArrayBuffer.cpp

Lines changed: 140 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ namespace Js
3838
return toReturn;
3939
}
4040

41-
void ArrayBuffer::ClearParentsLength(ArrayBufferParent* parent)
41+
void ArrayBuffer::DetachBufferFromParent(ArrayBufferParent* parent)
4242
{
4343
if (parent == nullptr)
4444
{
@@ -48,23 +48,158 @@ namespace Js
4848
switch (JavascriptOperators::GetTypeId(parent))
4949
{
5050
case TypeIds_Int8Array:
51+
if (Int8VirtualArray::Is(parent))
52+
{
53+
if (VirtualTableInfo<Int8VirtualArray>::HasVirtualTable(parent))
54+
{
55+
VirtualTableInfo<Int8Array>::SetVirtualTable(parent);
56+
}
57+
else
58+
{
59+
Assert(VirtualTableInfo<CrossSiteObject<Int8VirtualArray>>::HasVirtualTable(parent));
60+
VirtualTableInfo<CrossSiteObject<Int8Array>>::SetVirtualTable(parent);
61+
}
62+
}
63+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
64+
break;
65+
5166
case TypeIds_Uint8Array:
67+
if (Uint8VirtualArray::Is(parent))
68+
{
69+
if (VirtualTableInfo<Uint8VirtualArray>::HasVirtualTable(parent))
70+
{
71+
VirtualTableInfo<Uint8Array>::SetVirtualTable(parent);
72+
}
73+
else
74+
{
75+
Assert(VirtualTableInfo<CrossSiteObject<Uint8VirtualArray>>::HasVirtualTable(parent));
76+
VirtualTableInfo<CrossSiteObject<Uint8Array>>::SetVirtualTable(parent);
77+
}
78+
}
79+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
80+
break;
81+
5282
case TypeIds_Uint8ClampedArray:
83+
if (Uint8ClampedVirtualArray::Is(parent))
84+
{
85+
if (VirtualTableInfo<Uint8ClampedVirtualArray>::HasVirtualTable(parent))
86+
{
87+
VirtualTableInfo<Uint8ClampedArray>::SetVirtualTable(parent);
88+
}
89+
else
90+
{
91+
Assert(VirtualTableInfo<CrossSiteObject<Uint8ClampedVirtualArray>>::HasVirtualTable(parent));
92+
VirtualTableInfo<CrossSiteObject<Uint8ClampedArray>>::SetVirtualTable(parent);
93+
}
94+
}
95+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
96+
break;
97+
5398
case TypeIds_Int16Array:
99+
if (Int16VirtualArray::Is(parent))
100+
{
101+
if (VirtualTableInfo<Int16VirtualArray>::HasVirtualTable(parent))
102+
{
103+
VirtualTableInfo<Int16Array>::SetVirtualTable(parent);
104+
}
105+
else
106+
{
107+
Assert(VirtualTableInfo<CrossSiteObject<Int16VirtualArray>>::HasVirtualTable(parent));
108+
VirtualTableInfo<CrossSiteObject<Int16Array>>::SetVirtualTable(parent);
109+
}
110+
}
111+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
112+
break;
113+
54114
case TypeIds_Uint16Array:
115+
if (Uint16VirtualArray::Is(parent))
116+
{
117+
if (VirtualTableInfo<Uint16VirtualArray>::HasVirtualTable(parent))
118+
{
119+
VirtualTableInfo<Uint16Array>::SetVirtualTable(parent);
120+
}
121+
else
122+
{
123+
Assert(VirtualTableInfo<CrossSiteObject<Uint16VirtualArray>>::HasVirtualTable(parent));
124+
VirtualTableInfo<CrossSiteObject<Uint16Array>>::SetVirtualTable(parent);
125+
}
126+
}
127+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
128+
break;
129+
55130
case TypeIds_Int32Array:
131+
if (Int32VirtualArray::Is(parent))
132+
{
133+
if (VirtualTableInfo<Int32VirtualArray>::HasVirtualTable(parent))
134+
{
135+
VirtualTableInfo<Int32Array>::SetVirtualTable(parent);
136+
}
137+
else
138+
{
139+
Assert(VirtualTableInfo<CrossSiteObject<Int32VirtualArray>>::HasVirtualTable(parent));
140+
VirtualTableInfo<CrossSiteObject<Int32Array>>::SetVirtualTable(parent);
141+
}
142+
}
143+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
144+
break;
145+
56146
case TypeIds_Uint32Array:
147+
if (Uint32VirtualArray::Is(parent))
148+
{
149+
if (VirtualTableInfo<Uint32VirtualArray>::HasVirtualTable(parent))
150+
{
151+
VirtualTableInfo<Uint32Array>::SetVirtualTable(parent);
152+
}
153+
else
154+
{
155+
Assert(VirtualTableInfo<CrossSiteObject<Uint32VirtualArray>>::HasVirtualTable(parent));
156+
VirtualTableInfo<CrossSiteObject<Uint32Array>>::SetVirtualTable(parent);
157+
}
158+
}
159+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
160+
break;
161+
57162
case TypeIds_Float32Array:
163+
if (Float32VirtualArray::Is(parent))
164+
{
165+
if (VirtualTableInfo<Float32VirtualArray>::HasVirtualTable(parent))
166+
{
167+
VirtualTableInfo<Float32Array>::SetVirtualTable(parent);
168+
}
169+
else
170+
{
171+
Assert(VirtualTableInfo<CrossSiteObject<Float32VirtualArray>>::HasVirtualTable(parent));
172+
VirtualTableInfo<CrossSiteObject<Float32Array>>::SetVirtualTable(parent);
173+
}
174+
}
175+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
176+
break;
177+
58178
case TypeIds_Float64Array:
179+
if (Float64VirtualArray::Is(parent))
180+
{
181+
if (VirtualTableInfo<Float64VirtualArray>::HasVirtualTable(parent))
182+
{
183+
VirtualTableInfo<Float64Array>::SetVirtualTable(parent);
184+
}
185+
else
186+
{
187+
Assert(VirtualTableInfo<CrossSiteObject<Float64VirtualArray>>::HasVirtualTable(parent));
188+
VirtualTableInfo<CrossSiteObject<Float64Array>>::SetVirtualTable(parent);
189+
}
190+
}
191+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
192+
break;
193+
59194
case TypeIds_Int64Array:
60195
case TypeIds_Uint64Array:
61196
case TypeIds_CharArray:
62197
case TypeIds_BoolArray:
63-
TypedArrayBase::FromVar(parent)->length = 0;
198+
TypedArrayBase::FromVar(parent)->ClearLengthAndBufferOnDetach();
64199
break;
65200

66201
case TypeIds_DataView:
67-
DataView::FromVar(parent)->length = 0;
202+
DataView::FromVar(parent)->ClearLengthAndBufferOnDetach();
68203
break;
69204

70205
default:
@@ -90,14 +225,14 @@ namespace Js
90225

91226
if (this->primaryParent != nullptr)
92227
{
93-
this->ClearParentsLength(this->primaryParent->Get());
228+
this->DetachBufferFromParent(this->primaryParent->Get());
94229
}
95230

96231
if (this->otherParents != nullptr)
97232
{
98233
this->otherParents->Map([&](RecyclerWeakReference<ArrayBufferParent>* item)
99234
{
100-
this->ClearParentsLength(item->Get());
235+
this->DetachBufferFromParent(item->Get());
101236
});
102237
}
103238

lib/Runtime/Library/ArrayBuffer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ namespace Js
5050
DEFINE_VTABLE_CTOR_ABSTRACT(ArrayBuffer, ArrayBufferBase);
5151
#define MAX_ASMJS_ARRAYBUFFER_LENGTH 0x100000000 //4GB
5252
private:
53-
void ClearParentsLength(ArrayBufferParent* parent);
53+
void DetachBufferFromParent(ArrayBufferParent* parent);
5454
public:
5555
template <typename FreeFN>
5656
class ArrayBufferDetachedState : public ArrayBufferDetachedStateBase

lib/Runtime/Library/DataView.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,14 @@ namespace Js
666666
return FALSE;
667667
}
668668

669+
void DataView::ClearLengthAndBufferOnDetach()
670+
{
671+
AssertMsg(this->GetArrayBuffer()->IsDetached(), "Array buffer should be detached if we're calling this method");
672+
673+
this->length = 0;
674+
this->buffer = nullptr;
675+
}
676+
669677
#ifdef _M_ARM
670678
// Provide template specialization (only) for memory access at unaligned float/double address which causes data alignment exception otherwise.
671679
template<>

lib/Runtime/Library/DataView.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ namespace Js
5050
}
5151

5252
uint32 GetByteOffset() const { return byteOffset; }
53+
void ClearLengthAndBufferOnDetach();
5354

5455
static Var NewInstance(RecyclableObject* function, CallInfo callInfo, ...);
5556
static Var EntryGetInt8(RecyclableObject* function, CallInfo callInfo, ...);

lib/Runtime/Library/TypedArray.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1117,6 +1117,14 @@ namespace Js
11171117

11181118
}
11191119

1120+
void TypedArrayBase::ClearLengthAndBufferOnDetach()
1121+
{
1122+
AssertMsg(IsDetachedBuffer(), "Array buffer should be detached if we're calling this method");
1123+
1124+
this->length = 0;
1125+
this->buffer = nullptr;
1126+
}
1127+
11201128
Var TypedArrayBase::EntryGetterBuffer(RecyclableObject* function, CallInfo callInfo, ...)
11211129
{
11221130
PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);

lib/Runtime/Library/TypedArray.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ namespace Js
157157
uint32 GetBytesPerElement() const { return BYTES_PER_ELEMENT; }
158158
byte* GetByteBuffer() const { return buffer; };
159159
bool IsDetachedBuffer() const { return this->GetArrayBuffer()->IsDetached(); }
160+
void ClearLengthAndBufferOnDetach();
160161
static Var CommonSet(Arguments& args);
161162
static Var CommonSubarray(Arguments& args);
162163

0 commit comments

Comments
 (0)