[5] DFG RegExp constructor miscompilation via missing newTarget propagation
Severity: Medium | Component: JSC DFG JIT | 47b17ca
Rated Medium because the observable effect is a JavaScript-level object identity violation (constructor returns the same object instead of a new one) confirmed at confidence 0.92 by the regression test, with no direct memory corruption — escalation depends on how downstream JavaScript code uses the confused identity, which is application-dependent and requires chaining with specific validation patterns.
operationNewRegExpUntyped in DFG now passes regExpConstructor as both the callee and newTarget parameter to constructRegExp, aligning with the ECMAScript spec requirement that new RegExp(...) invocations carry a defined newTarget.
Source/JavaScriptCore/dfg/DFGOperations.cpp
JSGlobalObject* regExpGlobalObject = structure->globalObject();
- OPERATION_RETURN(scope, constructRegExp(regExpGlobalObject, ArgList { args, 2 }, regExpGlobalObject->regExpConstructor()));
+ JSObject* regExpConstructor = regExpGlobalObject->regExpConstructor();
+ OPERATION_RETURN(scope, constructRegExp(regExpGlobalObject, ArgList { args, 2 }, regExpConstructor, regExpConstructor));
JSTests/stress/dfg-miscompiles-new-regexp.js
+function opt(regExp) {
+ return new RegExp(regExp, undefined);
+}
+
+function main() {
+ const regExp = /a/;
+ for (let i = 0; i < testLoopCount; i++) {
+ if (opt(regExp) === regExp) {
+ throw new Error(`Bug triggered at ${i}`);
+ break;
+ }
+ }
+}
+main();
Patch Details
The fix adds a single argument to the constructRegExp call. Before, the call passed three arguments: regExpGlobalObject, ArgList { args, 2 }, and regExpGlobalObject->regExpConstructor(). After, a fourth argument — the same regExpConstructor — is passed as the newTarget parameter. The regExpConstructor is now extracted into a local variable to avoid calling the getter twice.
Background
JSC compiles hot functions through an interpreter (LLInt), then a baseline JIT, then the DFG (mid-tier optimizing JIT). The NewRegExpUntyped node is emitted by the DFG bytecode parser for new RegExp(...) calls where the newTarget differs from the call target, indicating a constructor invocation rather than a plain function call.
Per ECMAScript §22.2.3.1, when RegExp is called as a function (no new), the spec permits returning the input pattern directly if the pattern is already a RegExp and flags is undefined. When called as a constructor (new), it must always create a new RegExp object. The distinction hinges on whether NewTarget is defined. The constructRegExp function uses its fourth parameter as the newTarget — when absent, it follows the function-call path.
Analysis
Missing newTarget propagation in a DFG operation stub, causing constructor semantics to degrade to function-call semantics.
Without newTarget, constructRegExp follows the ES2025 §22.2.3.1 function-call path: when the pattern is already a RegExp and flags is undefined, the spec permits returning the same RegExp object. This means DFG-compiled code for new RegExp(existingRegExp, undefined) could return the original object instead of a fresh copy, violating the invariant that the new operator always produces a distinct object. The bytecode parser correctly identifies this as a new invocation (by checking newTargetNode != callTargetNode, as described in the commit message), but the information is lost at the operation boundary.
Aaa Aaaa Aaaa Aaaaaaaa Aaaaaaa Aaa Aaaaaaaa Aaaaaa Aaaaaaaaaaaa Aaa Aaaaaaa Aaaaaa Aaaaa Aa Aaaa Aaa a Aaaa Aaaaaaaaaaaa Aaaaa Aaa Aaaa Aaa Aaa Aa Aaaaaaa Aaa Aaaa Aaaaaa Aaaaa Aaa Aaaaaaaaaaa Aaa Aaa Aaa Aaaaa Aa Aaaaaaaa Aaaaa Aaaaaaaaaaaa Aaaaaa Aaa Aaaaa Aaaaaa Aaaaaa Aaaaaaa Aaaaaaaaaaa Aa Aaaaaa Aaaaaaaaaaaa Aaaaaaa Aaaaaaaa Aaa Aaaaaaaa Aaaa Aaaaa Aaaa Aaaaa Aaaaa Aa Aaaaaaaaaaa Aaaa Aaaaa Aa Aaaaaaa Aaaa Aaaaaaaaaa Aaaa Aaaaa Aaa Aaaaa Aaaaaa Aaaaaaa Aaa Aaaaaaaaaaa Aaaaa Aaaaaaaaaa Aa Aaaaaaaaaaa Aa Aaaaaaaa Aaaaa Aaaaaa Aaaaa Aaaaaaaaaaaaa
Aaaa Aaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaa Aaaaaaaa Aaaaaaaaa Aa Aaa Aaaaa Aaaaaaaa Aa Aaaaaaaaaaaa Aaaaa Aaa Aaaaaa Aa Aaaaaaaa Aa Aaaaaaaaaaaaaaaa Aaaaaa Aaaaaaaa Aaa Aaaaa Aaaaaaaaa Aaaaaa Aaa Aaaaaaaa a Aa Aaaaaa Aaaaaa Aaaaaaaaaa Aa Aaaaaaaaa Aaa Aa Aaaaaaa Aaaaaaa
Aaaa Aaaaaaa a Aaaaaaa Aaaaaaaa Aa Aaa Aaaaaaaaaaa Aaaaaa Aaaaaaaaa Aaaaaaaa Aa Aaaaaaa a Aa Aaaaa Aaaaaaaa Aaaaaa Aaaaa Aaa Aaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaa Aaaaaaa Aaaa Aaa Aaaaaaaaa Aaaaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaa Aaaaaaaa Aaaaaaaaaaaa Aaaaaaaa Aaa Aaa Aaaaaaaaaa Aaa Aaa Aaaa Aaaaaaaaaa
🔒Explores the downstream security implications of object identity confusion and how shared mutable state could be weaponized
Subscribe to read more
Audit directions
a Aaaaaaaa Aaa Aaaaaaaaa Aaaaa Aaaa Aaaaaaaaa Aaaaaaaaaaa Aaaaaaaaa Aaa Aaa Aaa Aaaaaaaaa Aaaaaaaaa Aaaaaaaaaa Aaaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaaaa Aa Aaaaaaaaaaaaaaaaa Aaa Aaaaa Aaaaa Aaaaaaaaaaaaaa Aaaaaaa Aaa Aaaaaa Aaaaaaa Aaa Aaaaaaaaa Aaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa Aa Aaaaaaaaaaaaaaaaa Aaa Aaaaaa Aaaa Aaaa Aa a Aaaaaaaaaaa Aaaaaa Aaaaaa Aaa Aaaaaaaaaaa Aaaaaaaaaa
a Aaaaaaaa Aaaaaaaaaaaaa Aaaaaaaaaa Aaaaaaa Aaa Aaaaaaaa Aaaaaaa Aaa Aaaaaaa Aaaaaaaaa Aaaaaa Aaa Aaaaaaaa Aaaaaa Aaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaaaaaa Aaaaaaaaaaa Aa Aaa Aa Aaaaaa Aaa Aaa Aaaaaaa Aaaaaaaaa Aaa Aaa Aaaaa Aaa Aaaaa Aaaaa Aaa Aaaaa Aaaa Aaaaaaaaaaa Aaaa Aaaa Aaaaaaaaa Aaaaaa Aaaaa Aaa Aaaa Aaaaaaa Aaaa Aaaaaaa Aaaa Aaaaaaaaaaa Aaa Aaaaaa Aaaaa Aaaaaaaaa Aaaaa Aaaaaaaaa Aaaaaaaaa Aaaaaaaaaa Aaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaa Aaaaaaaaa Aaa Aaaaaaaaaaaaaaa Aaaaaaaaaaaa
a Aaaaaaaa Aaaaaaaaaa Aaaaaaaaaaaa Aaaa Aaaaaaaaaaaaaaaaaaaaaaaaa Aaaa Aaaaa a Aaaaaaa Aaaaaaaaaaaa Aaa Aaaaaaa Aaaaaaaaaaaa Aaa Aaaa Aaaa Aaaaa Aaaaa a Aaaaaaaa Aaaa Aaa Aaaaaa Aaa Aaaaa Aaaaaaaaa Aaaa Aa a Aaaaaaaaa Aaa Aaaaaa Aaaaaaaa Aaaaaaaaa Aa Aaa Aaaaaaaaaaa Aaa Aaaaaaaaaaaaaa Aaaaaaaaaaaa Aaaaa Aaaaa Aaaaaaaaaaaaa Aaa Aaaaaaaaa Aaaaa Aaaaaaaaaaaaa
🔒Multiple audit patterns identified for DFG call-vs-construct semantic mismatches, with concrete search targets across operation stubs
Subscribe to read more