Spectre Demo

Finally, the actual Spectre demo. Please check that the previous steps were successful before trying out this demo.

Here's what will happen when you click run: first, the code will set up the memory layout. It will create a JavaScript array followed by a bunch of Uint8Arrays. The page offset of the JavaScript array is inferred just as on the previous page. This tells us the page offsets of all Uint8Arrays too, since they are at a fixed offset behind the JavaScript array.

The internal object of a Uint8Array stores a length field and a pointer to the data. We choose one of the arrays so that these two fields are on separate cache lines. This will allow us to flush the length from the cache but still follow the pointer to the backing store during speculative execution. This is why we need to know the page offset.

The gist of the Spectre gadget to leak a single bit looks like this:

const secretBit = (spectreArray[idx]>>bit)&1; return probeArray[secretBit*0x800];
The code accesses a Uint8Array, masks out a single bit and then uses it to access a probe array. If the bit was 0, the probe array will be accessed at offset 0, otherwise it will be accessed at offset 0x800. We can test for either access using our L1 timer.

The full flow will look like this:

  • Run the gadget a few times using an index that is in bounds of the array to train the branch predictor.
  • Evict the array's length field from the cache. The next time we call the gadget, the CPU will speculate that the length check will be successful.
  • Run the gadget using an out of bounds index and use the L1 timer to leak the bit.

If it worked, you will see a hex dump of the memory on the right. The Uint8Arrays are filled with 0x20 A's each. The result will be noisy, but the A's should be visible clearly. You can reduce the noise using the minimum and maximum Spectre gadget runs option. After running the demo, consider publishing your results using the button on the bottom.

Options

$ tail -f spectre.log
$ hexdump spectre.mem