initial commit

This commit is contained in:
emanuele 2020-03-10 16:19:50 +01:00
commit 82af0dce4a
38 changed files with 3810 additions and 0 deletions

11
README.md Normal file
View File

@ -0,0 +1,11 @@
# TRRespass
### ./drama
Inside the `drama` folder you can find a tool that helps you reverse engineer the DRAM memory mappings used by the memory controller.
Read the README in the folder for more details
### ./hammersuite
Inside the `hammersuite` folder you can find the fuzzer we used.
Again, read the README in the folder for more details

58
drama/Makefile Normal file
View File

@ -0,0 +1,58 @@
SDIR=src
IDIR=$(SDIR)/include
LDIR=lib
BUILD=obj
ODIR=src/.obj
CFLAGS=-I$(IDIR) #-ggdb
# CXX=g++
LDFLAGS=
OUT=tester
LDEPS=
GB_PAGE=/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
HUGEPAGE=/mnt/huge
all: $(OUT)
.PHONY: clean
SOURCES := $(wildcard $(SDIR)/*.c)
OBJECTS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(SOURCES))
$(ODIR)/%.o: $(SDIR)/%.c
mkdir -p $(ODIR)
$(CXX) -o $@ -c $< $(CFLAGS) $(LDFLAGS) $(LDEPS)
$(OUT): $(OBJECTS)
mkdir -p $(BUILD)
$(CXX) -o $(BUILD)/$@ $^ $(CFLAGS) $(LDFLAGS) $(LDEPS)
chmod +x $(BUILD)/$@
clean:
rm -rf $(BUILD)
rm -rf $(ODIR)
setup:
echo "Mounting hugetlbfs"
echo 1 | sudo tee -a $(GB_PAGE)
@if ! [ -d $(HUGEPAGE) ]; then\
sudo mkdir $(HUGEPAGE);\
sudo mount -t hugetlbfs -o pagesize=1G none $(HUGEPAGE);\
sudo chown pit:pit $(HUGEPAGE);\
fi
teardown:
@if [ -d $(HUGEPAGE) ]; then\
sudo umount -f $(HUGEPAGE);\
sudo rm -r $(HUGEPAGE);\
fi
echo 0 | sudo tee -a $(GB_PAGE)
run:
sudo $(BUILD)/$(OUT)

37
drama/README.md Normal file
View File

@ -0,0 +1,37 @@
# DRAMA
This tool can be used to reverse engineer the DRAM mapping functions of the test system.
It carries out two main tasks:
- Recovering the bank conflicts functions
- Recovering the row address function
The tool is a bit hackish since it makes some strong assumptions on the bits used for different purposes.
For instance it assumes that the bits from the physical address used to select the row are among the high bits.
This seems to be a valid assumption on all the Intel consumer platforms we tested.
However on server platforms and AMD ryzen machines it doesn't yield great results.
Once recovered the functions these can be used in `hammersuite` as explained in the the other README.
## Usage
```
./test [-h] [-s sets] [-r rounds] [-t threshold] [-o o_file] [-v] [--mem mem_size]
-h = this help message
-s sets = number of expected sets (default: 32)
-r rounds = number of rounds per tuple (default: 1000)
-t threshold = time threshold for conflicts (default: 340)
-o o_file = output file for mem profiling (default: access.csv)
--mem mem_size = allocation size (default: 5368709120)
-v = verbose
```
We recommend running it as verbose (`-v`) to get more insights on what's going on.
Otherwise it will simply output to stdout first the bank conflicts functions and then the row address mask.
**Number of sets:**
- The number of expected sets is defined by the memory configuration. For instance in a common dual-rank, single-channel configuration you would expect 32 banks (i.e., sets) in total. You can pass any value you want to the script. If this value is unknown 16 is usually a safe bet.
**Time threshold:**
- You can identify the time threshold by running the tool the first time with `-o` and plotting the results with the histogram.py script available in the repo. Once you know the threshold you can dinamycally pass it to the binary.

6
drama/hugepage.sh Executable file
View File

@ -0,0 +1,6 @@
gbFile="/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages"
mkdir /mnt/huge
mount -t hugetlbfs -o pagesize=1G none /mnt/huge
su -c 'echo 1 >'$gbFile
cat $gbFile

View File

@ -0,0 +1,14 @@
#include "utils.h"
#include "unistd.h"
typedef struct {
char* v_addr;
uint64_t p_addr;
} addr_tuple;
//----------------------------------------------------------
// Functions
void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, char* o_file, uint64_t flags);

95
drama/src/include/utils.h Normal file
View File

@ -0,0 +1,95 @@
#pragma once
#include <stdint.h>
#include <stdio.h>
#define BIT(x) (1ULL<<(x))
#define KB(x) ((x)<<10ULL)
#define MB(x) ((x)<<20ULL)
#define GB(x) ((x)<<30ULL)
#define CL_SHIFT 6
#define CL_SIZE 64
#define F_CLEAR 0L
#define F_VERBOSE BIT(0)
#define F_EXPORT BIT(1)
#define MEM_SHIFT (30L)
#define MEM_MASK 0b11111ULL << MEM_SHIFT
#define F_ALLOC_HUGE BIT(MEM_SHIFT)
#define F_ALLOC_HUGE_1G F_ALLOC_HUGE | BIT(MEM_SHIFT+1)
#define F_ALLOC_HUGE_2M F_ALLOC_HUGE | BIT(MEM_SHIFT+2)
#define F_POPULATE BIT(MEM_SHIFT+3)
//----------------------------------------------------------
// Static functions
static inline __attribute__((always_inline)) void clflush(volatile void *p)
{
asm volatile("clflush (%0)\n"
:: "r" (p) : "memory");
}
static inline __attribute__((always_inline)) void mfence()
{
asm volatile ("mfence" : : : "memory");
}
static inline __attribute__((always_inline)) void lfence()
{
asm volatile ("lfence" : : : "memory");
}
static inline __attribute__((always_inline)) uint64_t rdtscp(void)
{
uint64_t lo, hi;
asm volatile("rdtscp\n"
: "=a" (lo), "=d" (hi)
:: "%rcx");
return (hi << 32) | lo;
}
static inline __attribute__((always_inline)) uint64_t rdtsc(void)
{
uint64_t lo, hi;
asm volatile("rdtsc\n"
: "=a" (lo), "=d" (hi)
:: "%rcx");
return (hi << 32) | lo;
}
//----------------------------------------------------------
// Memory alloc
typedef struct {
char* buffer;
uint64_t size;
uint64_t flags;
} mem_buff_t;
int alloc_buffer(mem_buff_t* mem);
int free_buffer(mem_buff_t* mem);
//----------------------------------------------------------
// Helpers
int gt(const void * a, const void * b);
double mean(uint64_t* vals, size_t size);
uint64_t median(uint64_t* vals, size_t size);
char* bit_string(uint64_t val);

134
drama/src/main.c Normal file
View File

@ -0,0 +1,134 @@
#include "stdio.h"
// #include <x86intrin.h> /* for rdtsc, rdtscp, clflush */
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sched.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdbool.h>
#include <getopt.h>
#include "utils.h"
#include "rev-mc.h"
#define SETS_std (2*16) // 1rk-1ch
#define ROUNDS_std 1000
#define THRESHOLD_std 340
#define MEM_SIZE_std GB(5L)
#define O_FILE_std "access.csv"
#define FIELDS "base,probe,rounds,time"
//-----------------------------------------------
// GLOBALS
//-----------------------------------------------
void print_usage() {
fprintf(stderr, "[ LOG ] - Usage ./test [-h] [-s sets] [-r rounds] [-t threshold] [-o o_file] [-v] [--mem mem_size]\n");
fprintf(stderr, " -h = this help message\n");
fprintf(stderr, " -s sets = number of expected sets (default: %d)\n", SETS_std);
fprintf(stderr, " -r rounds = number of rounds per tuple (default: %d)\n", ROUNDS_std);
fprintf(stderr, " -t threshold = time threshold for conflicts (default: %d)\n", THRESHOLD_std);
fprintf(stderr, " -o o_file = output file for mem profiling (default: %s)\n", O_FILE_std);
fprintf(stderr, " --mem mem_size = allocation size (default: %ld)\n", (uint64_t) MEM_SIZE_std);
fprintf(stderr, " -v = verbose\n\n");
}
//-----------------------------------------------
int main(int argc, char** argv) {
uint64_t flags = 0ULL;
size_t sets_cnt = SETS_std;
size_t rounds = ROUNDS_std;
size_t m_size = MEM_SIZE_std;
size_t threshold = THRESHOLD_std;
char* o_file = (char*) O_FILE_std;
flags |= F_POPULATE;
if(geteuid() != 0) {
fprintf(stderr, "[ERROR] - You need to run as root to access pagemap!\n");
exit(1);
}
while (1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
static struct option long_options[] =
{
/* These options set a flag. */
{"mem", required_argument, 0, 0},
{0, 0, 0, 0}
};
int arg = getopt_long (argc, argv, "o:s:r:t:hv",
long_options, &option_index);
if (arg == -1)
break;
switch(arg) {
case 0:
switch (option_index){
case 0:
m_size = atoi(optarg); // TODO proper parsing of this
break;
default:
break;
}
break;
case 'o':
o_file = (char*) malloc(sizeof(char)*strlen(optarg));
strncpy(o_file, optarg, strlen(optarg));
flags |= F_EXPORT;
break;
case 's':
sets_cnt = atoi(optarg);
break;
case 'r':
rounds = atoi(optarg);
break;
case 't':
threshold = atoi(optarg);
break;
case 'v':
flags |= F_VERBOSE;
break;
case 'h':
default:
print_usage();
return 0;
}
}
rev_mc(sets_cnt, threshold, rounds, m_size, o_file, flags);
return 0;
}
/*I'm refactoring the set struct to add also timing in there. */

501
drama/src/rev-mc.c Normal file
View File

@ -0,0 +1,501 @@
#include <stdbool.h>
#include <time.h>
#include <stdlib.h>
#include <sched.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <inttypes.h>
#include <string.h>
#include <vector>
#include <functional>
#include <algorithm>
#include <bitset>
#include "rev-mc.h"
#define BOOL_XOR(a,b) ((a) != (b))
#define O_HEADER "base,probe,time\n"
#define ALIGN_TO(X, Y) ((X) & (~((1LL<<(Y))-1LL))) // Mask out the lower Y bits
#define LS_BITMASK(X) ((1LL<<(X))-1LL) // Mask only the lower X bits
#define SET_SIZE 40 // elements per set
#define VALID_THRESH 0.75f
#define SET_THRESH 0.95f
#define BITSET_SIZE 256 // bitset used to exploit bitwise operations
#define ROW_SET_CNT 5
// from https://stackoverflow.com/questions/1644868/define-macro-for-debug-printing-in-c
#define verbose_printerr(fmt, ...) \
do { if (flags & F_VERBOSE) { fprintf(stderr, fmt, ##__VA_ARGS__); } } while(0)
typedef std::vector<addr_tuple> set_t;
//-------------------------------------------
bool is_in(char* val, std::vector<char*> arr);
bool found_enough(std::vector<set_t> sets, uint64_t set_cnt, size_t set_size);
void filter_sets(std::vector<set_t>& sets, size_t set_size);
void print_sets(std::vector<set_t> sets);
void verify_sets(std::vector<set_t>& sets, uint64_t threshold, size_t rounds);
//-------------------------------------------
uint64_t time_tuple(volatile char* a1, volatile char* a2, size_t rounds) {
uint64_t* time_vals = (uint64_t*) calloc(rounds, sizeof(uint64_t));
uint64_t t0;
sched_yield();
for (size_t i = 0; i < rounds; i++) {
mfence();
t0 = rdtscp();
*a1;
*a2;
time_vals[i] = rdtscp() - t0;
lfence();
clflush(a1);
clflush(a2);
}
uint64_t mdn = median(time_vals, rounds);
free(time_vals);
return mdn;
}
//----------------------------------------------------------
char* get_rnd_addr(char* base, size_t m_size, size_t align) {
return (char*) ALIGN_TO((uint64_t) base, (uint64_t) align) + ALIGN_TO(rand() % m_size, (uint64_t) align);
}
//----------------------------------------------------------
uint64_t get_pfn(uint64_t entry) {
return ((entry) & 0x3fffffffffffff);
}
//----------------------------------------------------------
uint64_t get_phys_addr(uint64_t v_addr)
{
uint64_t entry;
uint64_t offset = (v_addr/4096) * sizeof(entry);
uint64_t pfn;
int fd = open("/proc/self/pagemap", O_RDONLY);
assert(fd >= 0);
int bytes_read = pread(fd, &entry, sizeof(entry), offset);
close(fd);
assert(bytes_read == 8);
assert(entry & (1ULL << 63));
pfn = get_pfn(entry);
assert(pfn != 0);
return (pfn*4096) | (v_addr & 4095);
}
//----------------------------------------------------------
addr_tuple gen_addr_tuple(char* v_addr) {
return (addr_tuple) { v_addr, get_phys_addr((uint64_t) v_addr)};
}
//----------------------------------------------------------
// https://www.cs.umd.edu/~gasarch/TOPICS/factoring/fastgauss.pdf
// gaussian elimination in GF2
std::vector<uint64_t> reduce_masks(std::vector<uint64_t> masks) {
size_t height, width, height_t, width_t;
height = masks.size();
width = 0;
for (auto m:masks) {
uint64_t max_one = 64 - __builtin_clzl(m);
width = (max_one > width)? max_one:width;
}
height_t = width;
width_t = height;
std::vector<std::vector<bool>> mtx(height, std::vector<bool>(width));
std::vector<std::vector<bool>> mtx_t(height_t, std::vector<bool>(width_t));
std::vector<uint64_t> filtered_masks;
for (size_t i =0; i<height;i++) {
for (size_t j=0; j<width; j++) {
mtx[i][width - j - 1] = (masks[i] & (1ULL<<(j)));
}
}
for (size_t i =0; i<height;i++) {
for (size_t j=0; j<width; j++) {
mtx_t[j][i] = mtx[i][j];
}
}
int64_t pvt_col = 0;
while (pvt_col < width_t) {
for (uint64_t row = 0; row < height_t; row++) {
if (mtx_t[row][pvt_col]) {
filtered_masks.push_back(masks[pvt_col]);
for (size_t c=0; c<width_t; c++) {
if (c == pvt_col)
continue;
if (!(mtx_t[row][c]))
continue;
// column sum
for (size_t r=0; r<height_t; r++) {
mtx_t[r][c] = BOOL_XOR(mtx_t[r][c], mtx_t[r][pvt_col]);
}
}
break;
}
}
pvt_col++;
}
return filtered_masks;
}
//----------------------------------------------------------
// from https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
uint64_t next_bit_permutation(uint64_t v) {
uint64_t t = v | (v - 1);
return (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctzl(v) + 1));
}
//----------------------------------------------------------
std::vector<uint64_t> find_functions(std::vector<set_t> sets, size_t max_fn_bits, size_t msb, uint64_t flags) {
std::vector<uint64_t> masks;
verbose_printerr("~~~~~~~~~~ Candidate functions ~~~~~~~~~~\n");
for (size_t bits = 1L; bits <= max_fn_bits; bits++) {
uint64_t fn_mask = ((1L<<(bits))-1); // avoid the first 6 bits since they are the cacheline bits
uint64_t last_mask = (fn_mask<<(msb-bits));
fn_mask <<= CL_SHIFT;
verbose_printerr("[ LOG ] - #Bits: %ld \n", bits);
while (fn_mask != last_mask) {
if (fn_mask & LS_BITMASK(6)){
fn_mask = next_bit_permutation(fn_mask);
continue;
}
for (size_t idx = 0; idx<sets.size(); idx++) {
set_t curr_set = sets[idx];
size_t inner_cnt = 0;
for (size_t i = 1; i < curr_set.size(); i++) {
uint64_t res_base = __builtin_parityl(curr_set[0].p_addr & fn_mask);
uint64_t res_probe = __builtin_parityl(curr_set[i].p_addr & fn_mask);
if (res_base != res_probe) {
goto next_mask;
}
}
}
verbose_printerr("\t Candidate: 0x%0lx \t\t bits: %s\n", fn_mask, bit_string(fn_mask));
masks.push_back(fn_mask);
next_mask:
fn_mask = next_bit_permutation(fn_mask);
}
}
verbose_printerr("~~~~~~~~~~ Found Functions ~~~~~~~~~~\n");
masks = reduce_masks(masks);
if (flags & F_VERBOSE) {
for (auto m: masks) {
fprintf(stderr, "\t Valid Function: 0x%0lx \t\t bits: %s\n", m, bit_string(m));
}
}
for (auto m: masks) {
fprintf(stdout, "0x%lx\n", m);
}
return masks;
}
std::vector<int> find_set_bits(uint64_t val) {
std::vector<int> set_bits;
for (int i = 0; i<64; i++) {
if (!(val & (1ULL << i)))
continue;
set_bits.push_back(i);
}
return set_bits;
}
//----------------------------------------------------------
std::vector<uint8_t> get_dram_fn(uint64_t addr, std::vector<uint64_t> fn_masks) {
std::vector<uint8_t> addr_dram;
for (auto fn:fn_masks) {
addr_dram.push_back(__builtin_parityl( addr & fn));
}
return addr_dram;
}
//----------------------------------------------------------
/*
It currently finds some of the interesting bits for the row addressing.
@TODO still need to figure out which bits are used for the row addressing and which
are from the bank selection. This is currently done manually
*/
uint64_t find_row_mask(std::vector<set_t>& sets, std::vector<uint64_t> fn_masks, mem_buff_t mem, uint64_t threshold, uint64_t flags) {
addr_tuple base_addr = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0));
std::vector<set_t> same_row_sets;
verbose_printerr("~~~~~~~~~~ Looking for row bits ~~~~~~~~~~\n");
for (int i = 0; i < 2; i++) {
verbose_printerr("[LOG] - Set #%d\n", i);
addr_tuple base_addr = sets[i][0];
std::vector<uint8_t> base_dram = get_dram_fn((uint64_t)base_addr.p_addr, fn_masks);
same_row_sets.push_back({base_addr});
uint64_t cnt = 0;
while (cnt < ROW_SET_CNT) {
addr_tuple tmp = gen_addr_tuple(get_rnd_addr(mem.buffer, mem.size, 0));
if (get_dram_fn((uint64_t) tmp.p_addr, fn_masks) != base_dram)
continue;
uint64_t time = time_tuple((volatile char*)base_addr.v_addr, (volatile char*)tmp.v_addr, 1000);
if (time > threshold)
continue;
verbose_printerr("[LOG] - %lx - %lx\t Time: %ld <== GOTCHA\n", base_addr.p_addr, tmp.p_addr, time);
same_row_sets[i].push_back(tmp);
cnt++;
}
}
uint64_t row_mask = LS_BITMASK(16); // use 16 bits for the row
uint64_t last_mask = (row_mask<<(40-16));
row_mask <<= CL_SHIFT; // skip the lowest 6 bits since they're used for CL addressing
while (row_mask < last_mask) {
if (row_mask & LS_BITMASK(CL_SHIFT)){
row_mask = next_bit_permutation(row_mask);
continue;
}
for (auto addr_pool:same_row_sets) {
addr_tuple base_addr = addr_pool[0];
for (int i = 1; i < addr_pool.size(); i++) {
addr_tuple tmp = addr_pool[i];
if ((tmp.p_addr & row_mask) != (base_addr.p_addr & row_mask)) {
goto next_mask;
}
}
}
break;
next_mask:
row_mask = next_bit_permutation(row_mask);
}
// super hackish way to recover the real row mask
for (auto m:fn_masks) {
uint64_t lsb = (1<<(__builtin_ctzl(m)+1));
if (lsb & row_mask) {
row_mask ^= (1<<__builtin_ctzl(m));
}
}
verbose_printerr("[LOG] - Row mask: 0x%0lx \t\t bits: %s\n", row_mask, bit_string(row_mask));
printf("0x%lx\n", row_mask);
}
//----------------------------------------------------------
void rev_mc(size_t sets_cnt, size_t threshold, size_t rounds, size_t m_size, char* o_file, uint64_t flags) {
time_t t;
int o_fd = 0;
int huge_fd = 0;
std::vector<set_t> sets;
std::vector<char*> used_addr;
std::vector<uint64_t> fn_masks;
srand((unsigned) time(&t));
if (flags & F_EXPORT) {
if (o_file == NULL) {
fprintf(stderr, "[ERROR] - Missing export file name\n");
exit(1);
}
if((o_fd = open(o_file, O_CREAT|O_RDWR)) == -1) {
perror("[ERROR] - Unable to create export file");
exit(1);
}
dprintf(o_fd, O_HEADER);
}
mem_buff_t mem = {
.buffer = NULL,
.size = m_size,
.flags = flags ,
};
alloc_buffer(&mem);
while (!found_enough(sets, sets_cnt, SET_SIZE)) {
char* rnd_addr = get_rnd_addr(mem.buffer, mem.size, CL_SHIFT);
if (is_in(rnd_addr, used_addr))
continue;
used_addr.push_back(rnd_addr);
addr_tuple tp = gen_addr_tuple(rnd_addr);
bool found_set = false;
for (size_t idx = 0; idx < sets.size(); idx++) {
uint64_t time = 0;
addr_tuple tmp = sets[idx][0];
time = time_tuple((volatile char*) tmp.v_addr, (volatile char*)tp.v_addr, rounds);
if (flags & F_EXPORT) {
dprintf(o_fd, "%lx,%lx,%ld\n",(uint64_t) tp.v_addr, (uint64_t) tmp.v_addr,time);
}
if (time > threshold) {
verbose_printerr("[LOG] - [%ld] Set: %03ld -\t %lx - %lx\t Time: %ld\n", used_addr.size(), idx, tp.p_addr, tmp.p_addr, time);
sets[idx].push_back(tp);
found_set = true;
break;
}
}
if (!found_set) {
sets.push_back({tp});
verbose_printerr( "[LOG] - Set: %03ld -\t %p <== NEW!!\n", sets.size(), tp.v_addr);
}
}
filter_sets(sets, SET_SIZE);
#ifdef DEBUG_SETS
fprintf(stderr, "[ LOG ] - Cleansing sets. This may take a while... stay put\n");
verify_sets(sets, threshold, rounds);
fprintf(stderr, "[ LOG ] - Done\n");
#endif
if (flags & F_VERBOSE) {
print_sets(sets);
}
fn_masks = find_functions(sets, 6, 30, flags);
uint64_t row_mask = find_row_mask(sets, fn_masks, mem, threshold, flags);
free_buffer(&mem);
}
// Fin.
//----------------------------------------------------------
// Helpers
bool is_in(char* val, std::vector<char*> arr) {
for (auto v: arr) {
if (val == v) {
return true;
}
}
return false;
}
//----------------------------------------------------------
bool found_enough(std::vector<set_t> sets, uint64_t set_cnt, size_t set_size) {
size_t found_sets = 0;
for (int i =0; i < sets.size(); i++) {
set_t curr_set = sets[i];
if (curr_set.size() > set_size) {
found_sets += 1;
}
}
if (found_sets > set_cnt) {
fprintf(stderr, "[ERROR] - Found too many sets. Is %ld the correct number of sets?\n", set_cnt);
exit(1);
}
return (found_sets >= (set_cnt * SET_THRESH)) ? true : false;
}
void filter_sets(std::vector<set_t>& sets, size_t set_size) {
for (auto s = sets.begin(); s < sets.end(); s++) {
if (s->size() < set_size) {
sets.erase(s);
s -= 1;
}
}
}
void print_sets(std::vector<set_t> sets) {
for (int idx = 0; idx < sets.size(); idx++) {
fprintf(stderr, "[LOG] - Set: %d\tSize: %ld\n", idx, sets[idx].size());
for (auto tmp: sets[idx]) {
fprintf(stderr, "\tv_addr:%p - p_addr:%p\n", tmp.v_addr, (void*) tmp.p_addr);
}
}
}
#ifdef DEBUG_SETS
void verify_sets(std::vector<set_t>& sets, uint64_t threshold, size_t rounds) {
for (auto s: sets) {
// test every address against all the addresses in the set
for (auto tp_base = s.begin(); tp_base < s.end(); tp_base++) {
uint64_t conflicts = 0;
for (auto tp_probe = s.begin(); tp_probe < s.end(); tp_probe++) {
if (tp_base == tp_probe)
continue;
uint64_t time = time_tuple((volatile char*) tp_base->v_addr,(volatile char*) tp_probe->v_addr, rounds);
if (time>threshold){
conflicts += 1;
}
}
if (!(conflicts > VALID_THRESH*s.size())) {
fprintf(stderr, "[ LOG ] - Removing: %p\n", tp_base->v_addr);
s.erase(tp_base--); // reset the iterator
}
}
}
}
#endif

87
drama/src/utils.c Normal file
View File

@ -0,0 +1,87 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include "utils.h"
//-----------------------------------------------
// Memory alloc
int alloc_buffer(mem_buff_t* mem) {
if (mem->buffer != NULL) {
fprintf(stderr, "[ERROR] - Memory already allocated\n");
}
uint64_t alloc_flags = MAP_PRIVATE | MAP_POPULATE | MAP_ANONYMOUS;
mem->buffer = (char*) mmap(NULL, mem->size, PROT_READ | PROT_WRITE, alloc_flags, -1, 0);
if (mem->buffer == MAP_FAILED) {
perror("[ERROR] - mmap() failed");
exit(1);
}
if (mem->flags & F_VERBOSE) {
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
fprintf(stderr, "[ MEM ] - Buffer: %p\n", mem->buffer);
fprintf(stderr, "[ MEM ] - Size: %ld\n", mem->size);
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
}
return 0;
}
int free_buffer(mem_buff_t* mem) {
return munmap(mem->buffer, mem->size);
}
//-----------------------------------------------
// Helpers
double mean(uint64_t* vals, size_t size) {
uint64_t avg = 0;
for (size_t i = 0; i < size; i++) {
avg += vals[i];
}
return ((double)avg) / size;
}
int gt(const void * a, const void * b) {
return ( *(int*)a - *(int*)b );
}
uint64_t median(uint64_t* vals, size_t size) {
qsort(vals, size, sizeof(uint64_t), gt);
return ((size%2)==0) ? vals[size/2] : (vals[(size_t)size/2]+vals[((size_t)size/2+1)])/2;
}
char* bit_string(uint64_t val) {
static char bit_str[256];
char itoa_str[8];
strcpy(bit_str, "");
for (int shift = 0; shift < 64; shift++) {
if ((val >> shift) & 1) {
if (strcmp(bit_str, "") != 0) {
strcat(bit_str, "+ ");
}
sprintf(itoa_str, "%d ", shift);
strcat(bit_str, itoa_str);
}
}
return bit_str;
}

1
hammersuite/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.log

59
hammersuite/Makefile Normal file
View File

@ -0,0 +1,59 @@
SDIR=src
IDIR=$(SDIR)/include
LDIR=lib
BUILD=obj
ODIR=src/.obj
DATA_DIR=$(PWD)/data/
CFLAGS=-I$(IDIR) -msse4.2 -ggdb -DDATA_DIR=\"$(DATA_DIR)\"
# CXX=g++
LDFLAGS=
OUT=tester
LDEPS=
GB_PAGE=/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
HUGEPAGE=/mnt/huge
all: $(OUT)
.PHONY: clean
SOURCES := $(wildcard $(SDIR)/*.c)
OBJECTS := $(patsubst $(SDIR)/%.c, $(ODIR)/%.o, $(SOURCES))
$(ODIR)/%.o: $(SDIR)/%.c
mkdir -p $(ODIR)
$(CXX) -o $@ -c $< $(CFLAGS) $(LDFLAGS) $(LDEPS)
$(OUT): $(OBJECTS)
mkdir -p $(BUILD)
$(CXX) -o $(BUILD)/$@ $^ $(CFLAGS) $(LDFLAGS) $(LDEPS)
chmod +x $(BUILD)/$@
clean:
rm -rf $(BUILD)
rm -rf $(ODIR)
setup:
echo "Mounting hugetlbfs"
echo 2 | sudo tee -a $(GB_PAGE)
@if ! [ -d $(HUGEPAGE) ]; then\
sudo mkdir $(HUGEPAGE);\
sudo mount -t hugetlbfs -o pagesize=1G none $(HUGEPAGE);\
sudo chown pit:pit $(HUGEPAGE);\
fi
teardown:
@if [ -d $(HUGEPAGE) ]; then\
sudo umount -f $(HUGEPAGE);\
sudo rm -r $(HUGEPAGE);\
fi
echo 0 | sudo tee -a $(GB_PAGE)
run:
sudo $(BUILD)/$(OUT)

72
hammersuite/README.md Normal file
View File

@ -0,0 +1,72 @@
# TRRespass
## About
TRRespass code implements several tests to reveal the presence of the Rowhammer vulnerability.
Since several defenses may be in place in the memory controller and/or at DRAM level, TRRespass tests for non-conventional hammering patterns that may be able to bypass defenses.
## Requirements
### Physical to DRAM mapping functions
The functions resolving the mapping of a physical address into a DRAM address are kept in the following structures:
`include/types.h:`
```c
typedef struct {
uint64_t lst[HASH_FN_CNT];
uint64_t len;
} AddrFns;
typedef struct {
AddrFns h_fns;
uint64_t row_mask;
uint64_t col_mask;
} DRAMLayout
```
The mapping functions must be defined in ```main.c```:
```c
DRAMLayout g_mem_layout = {{{0x4080,0x48000,0x90000,0x120000,0x1b300}, 5}, row_mask, ROW_SIZE-1};
```
AMD publicly documents mapping functions in th "BIOS and Kernel Developers Guide (BKDG)"
Contrariwise, Intel does not. We provide a tool to retrieve the mapping functions, based on the techniques described in [1]. It's a bit hackish but it works.
The tool is available in the folder ./drama (read the README in the folder).
### Huge pages support
1GB Huge Page support is required to gain physically continuis memory and perform templating.
## Usage
Commands must be run with `sudo` privileges.
- Full options list
```
sudo ./obj/test --help
```
- Common usage
```
sudo ./obj/tester -r 1000000 -v -V ff -T 00 --no-overwrite -o DIMM00
```
1. By default double-sided RH is tested.
2. `-r` indicates the number of accesses for each target row.
3. The options `-V `and `-T` specify the victim row(s) and the target rows data pattern. By default the data pattern is randomly generated.
4. The `-o` argument allow to specify a prefix for the output file. `--no-overwrite` avoids to overwrite an output file with the same name if present.
5. Testing for non-conventional hammering patterns (i.e., black-box fuzzing)
```
sudo ./obj/tester -v --fuzzing
```
This will test the RH vulnerability against randomly generated hammering patterns.
At the moment the tool exports the results in files we call Fliptables (the export choice is currently hardcoded as a #define). You can use `hammerstats.py` in the `../py` folder to print out statistics about the number of bit flips.
The format is not so human friendly but it was helping us to print out statistics using some pre-existing toolchains we had.
#### References
[1] "DRAMA: Exploiting DRAM Addressing for Cross-CPU Attacks", Usenix Sec 16, Pessl et al.

6
hammersuite/hugepage.sh Executable file
View File

@ -0,0 +1,6 @@
gbFile="/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages"
mkdir /mnt/huge
mount -t hugetlbfs -o pagesize=1G none /mnt/huge
su -c 'echo 1 >'$gbFile
cat $gbFile

19
hammersuite/performance.sh Executable file
View File

@ -0,0 +1,19 @@
if [ "$1" == "" ]; then
echo "Expected argument (powersave/ performance)"
exit
fi
max_proc=$(cat /proc/cpuinfo | grep processor | tail -n 1 | cut -d ":" -f 2)
mode=$1
echo Max proc: $max_proc
echo Setting mode: $mode
for i in $(seq 0 $max_proc)
do
echo $mode | sudo tee -a /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor > /dev/null
echo cat /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor = $(cat /sys/devices/system/cpu/cpu$i/cpufreq/scaling_governor)
done

View File

@ -0,0 +1,78 @@
#include "addr-mapper.h"
#include "utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
static size_t g_rmap_len = 0;
static size_t g_base_row = 0;
static size_t g_bks = 0;
static size_t g_rows = 0;
RowMap get_row_map(ADDRMapper * mapper, DRAMAddr * d_addr)
{
size_t idx =
(d_addr->row - g_base_row) * get_banks_cnt() + d_addr->bank;
assert(idx < g_bks * g_rows);
return mapper->row_maps[idx];
}
DRAM_pte get_dram_pte(ADDRMapper * mapper, DRAMAddr * d_addr)
{
RowMap rmap = get_row_map(mapper, d_addr);
return rmap.lst[(d_addr->col) >> 6];
}
RowMap gen_row_map(DRAMAddr d_src, MemoryBuffer * mem)
{
RowMap rmap;
DRAM_pte *dst = (DRAM_pte *) malloc(sizeof(DRAM_pte) * g_rmap_len);
d_src.col = 0;
for (size_t col = 0; col < g_rmap_len; col++, d_src.col += (1 << 6)) {
dst[col].d_addr = d_src;
dst[col].v_addr = phys_2_virt(dram_2_phys(d_src), mem);
}
rmap.lst = dst;
rmap.len = g_rmap_len;
return rmap;
}
size_t rmap_idx(size_t bk, size_t row)
{
return row * get_banks_cnt() + bk;
}
void init_addr_mapper(ADDRMapper * mapper, MemoryBuffer * mem,
DRAMAddr * d_base, size_t h_rows)
{
mapper->row_maps =
(RowMap *) malloc(sizeof(RowMap) * h_rows * get_banks_cnt());
mapper->base_row = d_base->row;
g_base_row = d_base->row;
g_bks = get_banks_cnt();
g_rows = h_rows;
// set global rmap_len
g_rmap_len = ROW_SIZE / CL_SIZE;
// create ptes list for every
DRAMAddr d_tmp = {.bank = 0,.row = 0,.col = 0 };
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
d_tmp.bank = bk;
for (size_t row = 0; row < h_rows; row++) {
d_tmp.row = g_base_row + row;
mapper->row_maps[rmap_idx(bk, row)] =
gen_row_map(d_tmp, mem);
}
}
}
void tear_down_addr_mapper(ADDRMapper * mapper)
{
for (int i = 1; i < g_rows * g_bks; i++) {
free(mapper->row_maps[i].lst);
}
free(mapper->row_maps);
}

View File

@ -0,0 +1,74 @@
#include "allocator.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include "utils.h"
int alloc_buffer(MemoryBuffer * mem)
{
if (mem->buffer != NULL) {
fprintf(stderr, "[ERROR] - Memory already allocated\n");
}
if (mem->align < _SC_PAGE_SIZE) {
mem->align = 0;
}
uint64_t alloc_size = mem->align ? mem->size + mem->align : mem->size;
uint64_t alloc_flags = MAP_PRIVATE | MAP_POPULATE;
if (mem->flags & F_ALLOC_HUGE) {
if (mem->fd == 0) {
fprintf(stderr,
"[ERROR] - Missing file descriptor to allocate hugepage\n");
exit(1);
}
alloc_flags |=
(mem->flags & F_ALLOC_HUGE_1G) ? MAP_ANONYMOUS | MAP_HUGETLB
| (30 << MAP_HUGE_SHIFT)
: (mem->flags & F_ALLOC_HUGE_2M) ? MAP_ANONYMOUS |
MAP_HUGETLB | (21 << MAP_HUGE_SHIFT)
: MAP_ANONYMOUS;
} else {
mem->fd = -1;
alloc_flags |= MAP_ANONYMOUS;
}
mem->buffer = (char *)mmap(NULL, mem->size, PROT_READ | PROT_WRITE,
alloc_flags, mem->fd, 0);
if (mem->buffer == MAP_FAILED) {
perror("[ERROR] - mmap() failed");
exit(1);
}
if (mem->align) {
size_t error = (uint64_t) mem->buffer % mem->align;
size_t left = error ? mem->align - error : 0;
munmap(mem->buffer, left);
mem->buffer += left;
assert((uint64_t) mem->buffer % mem->align == 0);
}
if (mem->flags & F_VERBOSE) {
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
fprintf(stderr, "[ MEM ] - Buffer: %p\n", mem->buffer);
fprintf(stderr, "[ MEM ] - Size: %ld\n", alloc_size);
fprintf(stderr, "[ MEM ] - Alignment: %ld\n", mem->align);
fprintf(stderr, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
}
return 0;
}
int free_buffer(MemoryBuffer * mem)
{
free(mem->physmap);
return munmap(mem->buffer, mem->size);
}

View File

@ -0,0 +1,131 @@
#include "dram-address.h"
#include <stdio.h>
#include <stdlib.h>
#include "utils.h"
#define DEBUG_REVERSE_FN 1
extern DRAMLayout g_mem_layout;
uint64_t get_dram_row(physaddr_t p_addr)
{
return (p_addr & g_mem_layout.
row_mask) >> __builtin_ctzl(g_mem_layout.row_mask);
}
uint64_t get_dram_col(physaddr_t p_addr)
{
return (p_addr & g_mem_layout.
col_mask) >> __builtin_ctzl(g_mem_layout.col_mask);
}
DRAMAddr phys_2_dram(physaddr_t p_addr)
{
DRAMAddr res = { 0, 0, 0 };
for (int i = 0; i < g_mem_layout.h_fns.len; i++) {
res.bank |=
(__builtin_parityl(p_addr & g_mem_layout.h_fns.lst[i]) <<
i);
}
res.row = get_dram_row(p_addr);
res.col = get_dram_col(p_addr);
return res;
}
physaddr_t dram_2_phys(DRAMAddr d_addr)
{
physaddr_t p_addr = 0;
uint64_t col_val = 0;
p_addr = (d_addr.row << __builtin_ctzl(g_mem_layout.row_mask)); // set row bits
p_addr |= (d_addr.col << __builtin_ctzl(g_mem_layout.col_mask)); // set col bits
for (int i = 0; i < g_mem_layout.h_fns.len; i++) {
uint64_t masked_addr = p_addr & g_mem_layout.h_fns.lst[i];
// if the address already respects the h_fn then just move to the next func
if (__builtin_parity(masked_addr) == ((d_addr.bank >> i) & 1L)) {
continue;
}
// else flip a bit of the address so that the address respects the dram h_fn
// that is get only bits not affecting the row.
uint64_t h_lsb = __builtin_ctzl((g_mem_layout.h_fns.lst[i]) &
~(g_mem_layout.col_mask) &
~(g_mem_layout.row_mask));
p_addr ^= 1 << h_lsb;
}
#if DEBUG_REVERSE_FN
int correct = 1;
for (int i = 0; i < g_mem_layout.h_fns.len; i++) {
if (__builtin_parity(p_addr & g_mem_layout.h_fns.lst[i]) !=
((d_addr.bank >> i) & 1L)) {
correct = 0;
break;
}
}
if (d_addr.row != ((p_addr &
g_mem_layout.row_mask) >>
__builtin_ctzl(g_mem_layout.row_mask)))
correct = 0;
if (!correct)
fprintf(stderr,
"[DEBUG] - Mapping function for 0x%lx not respected\n",
p_addr);
#endif
return p_addr;
}
void set_global_dram_layout(DRAMLayout & mem_layout)
{
g_mem_layout = mem_layout;
}
DRAMLayout *get_dram_layout()
{
return &g_mem_layout;
}
bool d_addr_eq(DRAMAddr * d1, DRAMAddr * d2)
{
return (d1->bank == d2->bank) && (d1->row == d2->row)
&& (d1->col == d2->col);
}
bool d_addr_eq_row(DRAMAddr * d1, DRAMAddr * d2)
{
return (d1->bank == d2->bank) && (d1->row == d2->row);
}
uint64_t get_banks_cnt()
{
return 1 << g_mem_layout.h_fns.len;
}
char *dram_2_str(DRAMAddr * d_addr)
{
static char ret_str[1024];
sprintf(ret_str, "DRAM(bk: %ld (%s), row: %08ld, col: %08ld)",
d_addr->bank, int_2_bin(d_addr->bank), d_addr->row,
d_addr->col);
return ret_str;
}
char *dramLayout_2_str(DRAMLayout * mem_layout)
{
static char ret_str[1024];
sprintf(ret_str, "{0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx} - 0x%lx\n",
mem_layout->h_fns.lst[0], mem_layout->h_fns.lst[1],
mem_layout->h_fns.lst[2], mem_layout->h_fns.lst[3],
mem_layout->h_fns.lst[4], mem_layout->h_fns.lst[5],
mem_layout->row_mask);
return ret_str;
}

View File

@ -0,0 +1,943 @@
#include "include/hammer-suite.h"
#include "include/memory.h"
#include "include/utils.h"
#include "include/allocator.h"
#include "include/dram-address.h"
#include "include/addr-mapper.h"
#include "include/params.h"
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sched.h>
#include <limits.h>
#include <math.h>
#define REFRESH_VAL "stdrefi"
#define OUT_HEAD "f_og, f_new, vict_addr, aggr_addr\n"
#define ROW_FIELD 1
#define COL_FIELD 1<<1
#define BK_FIELD 1<<2
#define P_FIELD 1<<3
#define ALL_FIELDS (ROW_FIELD | COL_FIELD | BK_FIELD)
#define FLIPTABLE
/*
h_patt = hammer pattern (e.g., DOUBLE_SIDED)
d_patt = data pattern (e.g., RANDOM)
vict_addr = DRAMAddr for the bit flip in format bkXX.XrXXXX.cXXX
f_og = byte original value
f_new = byte after bit flip
f_mask = bitmask of the bit flip
base_row = initial row being hammered *in this round*
row_cnt = number of rows being hammered *in this round*
t_refi = t_refi
h_rounds = number of hammering rounds
aggr_addr = addresses of aggressor rows in format bkXX.rXXXX.cXX
*/
#define SHADOW_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE)
#define DEBUG
#define NOP asm volatile ("NOP":::);
#define NOP10 NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP
#define NOP100 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10 NOP10
#define NOP1000 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100 NOP100
extern ProfileParams *p;
int g_bk;
FILE *out_fd = NULL;
static uint64_t CL_SEED = 0x7bc661612e71168c;
static inline __attribute((always_inline))
char *cl_rand_gen(DRAMAddr * d_addr)
{
static uint64_t cl_buff[8];
for (int i = 0; i < 8; i++) {
cl_buff[i] =
__builtin_ia32_crc32di(CL_SEED,
(d_addr->row + d_addr->bank +
(d_addr->col + i*8)));
}
return (char *)cl_buff;
}
typedef struct {
DRAMAddr *d_lst;
size_t len;
size_t rounds;
} HammerPattern;
typedef struct {
DRAMAddr d_vict;
uint8_t f_og;
uint8_t f_new;
HammerPattern *h_patt;
} FlipVal;
typedef struct {
MemoryBuffer *mem;
SessionConfig *cfg;
DRAMAddr d_base; // base address for hammering
ADDRMapper *mapper; // dram mapper
int (*hammer_test) (void *self);
} HammerSuite;
char *dAddr_2_str(DRAMAddr d_addr, uint8_t fields)
{
static char ret_str[64];
char tmp_str[10];
bool first = true;
memset(ret_str, 0x00, 64);
if (fields & ROW_FIELD) {
first = false;
sprintf(tmp_str, "r%05ld", d_addr.row);
strcat(ret_str, tmp_str);
}
if (fields & BK_FIELD) {
if (!first) {
strcat(ret_str, ".");
}
sprintf(tmp_str, "bk%02ld", d_addr.bank);
strcat(ret_str, tmp_str);
first = false;
}
if (fields & COL_FIELD) {
if (!first) {
strcat(ret_str, ".");
}
sprintf(tmp_str, "col%04ld", d_addr.col);
strcat(ret_str, tmp_str);
first = false;
}
return ret_str;
}
char *hPatt_2_str(HammerPattern * h_patt, int fields)
{
static char patt_str[256];
char *dAddr_str;
memset(patt_str, 0x00, 256);
for (int i = 0; i < h_patt->len; i++) {
dAddr_str = dAddr_2_str(h_patt->d_lst[i], fields);
strcat(patt_str, dAddr_str);
if (i + 1 != h_patt->len) {
strcat(patt_str, "/");
}
}
return patt_str;
}
void print_start_attack(HammerPattern *h_patt)
{
fprintf(out_fd, "%s : ", hPatt_2_str(h_patt, ROW_FIELD | BK_FIELD));
fflush(out_fd);
}
void print_end_attack()
{
fprintf(out_fd, "\n");
fflush(out_fd);
}
void export_flip(FlipVal * flip)
{
if (p->g_flags & F_VERBOSE) {
fprintf(stdout, "[FLIP] - (%02x => %02x)\t vict: %s \taggr: %s \n",
flip->f_og, flip->f_new, dAddr_2_str(flip->d_vict, ALL_FIELDS),
hPatt_2_str(flip->h_patt, ROW_FIELD | BK_FIELD));
fflush(stdout);
}
#ifdef FLIPTABLE
fprintf(out_fd, "%02x,%02x,%s ", flip->f_og, flip->f_new,
dAddr_2_str(flip->d_vict, ALL_FIELDS));
#else
fprintf(out_fd, "%02x,%02x,%s,%s\n", flip->f_og, flip->f_new,
dAddr_2_str(flip->d_vict, ALL_FIELDS), hPatt_2_str(flip->h_patt,
ROW_FIELD | BK_FIELD | P_FIELD));
#endif
fflush(out_fd);
}
void export_cfg(HammerSuite * suite)
{
SessionConfig *cfg = suite->cfg;
if (p->g_flags & F_VERBOSE) {
fprintf(stdout,
"Config: { h_cfg: %s, d_cfg: %s, h_rows: %ld, h_rounds: %ld, base: %s}\n",
config_str[cfg->h_cfg], data_str[cfg->d_cfg],
cfg->h_rows, cfg->h_rounds, dAddr_2_str(suite->d_base,
ROW_FIELD |
BK_FIELD));
}
fprintf(out_fd,
"# { h_cfg: %s, d_cfg: %s, h_rows: %ld, h_rounds: %ld, base: %s}\n",
config_str[cfg->h_cfg], data_str[cfg->d_cfg], cfg->h_rows,
cfg->h_rounds, dAddr_2_str(suite->d_base,
ROW_FIELD | BK_FIELD));
fflush(out_fd);
}
void swap(char **lst, int i, int j)
{
char *tmp = lst[i];
lst[i] = lst[j];
lst[j] = tmp;
}
int random_int(int min, int max)
{
int number = min + rand() % (max - min);
return number;
}
uint64_t hammer_it(HammerPattern* patt, MemoryBuffer* mem) {
char** v_lst = (char**) malloc(sizeof(char*)*patt->len);
for (size_t i = 0; i < patt->len; i++) {
v_lst[i] = phys_2_virt(dram_2_phys(patt->d_lst[i]), mem);
}
sched_yield();
if (p->threshold > 0) {
uint64_t t0 = 0, t1 = 0;
// Threshold value depends on your system
while (abs((int64_t) t1 - (int64_t) t0) < p->threshold) {
t0 = rdtscp();
*(volatile char *)v_lst[0];
clflushopt(v_lst[0]);
t1 = rdtscp();
}
}
uint64_t cl0, cl1;
cl0 = realtime_now();
for ( int i = 0; i < patt->rounds; i++) {
mfence();
for (size_t j = 0; j < patt->len; j++) {
*(volatile char*) v_lst[j];
}
for (size_t j = 0; j < patt->len; j++) {
clflushopt(v_lst[j]);
}
}
cl1 = realtime_now();
free(v_lst);
return (cl1-cl0) / 1000000;
}
void __test_fill_random(char *addr, size_t size)
{
int fd;
if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
perror("[ERROR] - Unable to open /dev/urandom");
exit(1);
}
if (read(fd, addr, size) == -1) {
perror("[ERROR] - Unable to read /dev/urandom");
exit(1);
}
close(fd);
}
// DRAMAddr needs to be a copy in order to leave intact the original address
void fill_stripe(DRAMAddr d_addr, uint8_t val, ADDRMapper * mapper)
{
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
d_addr.col = col;
DRAM_pte d_pte = get_dram_pte(mapper, &d_addr);
memset(d_pte.v_addr, val, CL_SIZE);
}
}
void fill_row(HammerSuite * suite, DRAMAddr * d_addr, HammerData data_patt)
{
if (p->vpat != (void *)NULL && p->tpat != (void *)NULL) {
fill_stripe(*d_addr, (uint8_t) * p->tpat, suite->mapper);
return;
}
switch (data_patt) {
case RANDOM:
// rows are already filled for random data patt
break;
case ONE_TO_ZERO:
fill_stripe(*d_addr, 0x00, suite->mapper);
break;
case ZERO_TO_ONE:
fill_stripe(*d_addr, 0xff, suite->mapper);
break;
default:
// fprintf(stderr, "[ERROR] - Wrong data pattern %d\n", data_patt);
// exit(1);
break;
}
}
void cl_rand_fill(DRAM_pte * pte)
{
char *rand_data = cl_rand_gen(&pte->d_addr);
memcpy(pte->v_addr, rand_data, CL_SIZE);
}
uint64_t cl_rand_comp(DRAM_pte * pte)
{
char *rand_data = cl_rand_gen(&pte->d_addr);
uint64_t res = 0;
for (int i = 0; i < CL_SIZE; i++) {
if (*(pte->v_addr + i) != rand_data[i]) {
res |= 1UL<<i;
}
}
return res;
}
void init_random(HammerSuite * suite)
{
int fd;
if ((fd = open("/dev/urandom", O_RDONLY)) == -1) {
perror("[ERROR] - Unable to open /dev/urandom");
exit(1);
}
if (CL_SEED == 0) {
if (read(fd, &CL_SEED, sizeof(CL_SEED)) == -1) {
perror("[ERROR] - Unable to read /dev/urandom");
exit(1);
}
}
// fprintf(out_fd,"#seed: %lx\n", CL_SEED);
close(fd);
ADDRMapper *mapper = suite->mapper;
DRAMAddr d_tmp;
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
d_tmp.bank = bk;
for (size_t row = 0; row < suite->cfg->h_rows; row++) {
d_tmp.row = suite->mapper->base_row + row;
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
d_tmp.col = col;
DRAM_pte d_pte = get_dram_pte(mapper, &d_tmp);
cl_rand_fill(&d_pte);
}
}
}
}
void init_stripe(HammerSuite * suite, uint8_t val)
{
ADDRMapper *mapper = suite->mapper;
DRAMAddr d_tmp;
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
d_tmp.bank = bk;
for (size_t row = 0; row < suite->cfg->h_rows; row++) {
d_tmp.row = suite->mapper->base_row + row;
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
d_tmp.col = col;
DRAM_pte d_pte = get_dram_pte(mapper, &d_tmp);
memset(d_pte.v_addr, val, CL_SIZE);
}
}
}
}
void init_chunk(HammerSuite * suite)
{
if (p->vpat != (void *)NULL && p->tpat != (void *)NULL) {
init_stripe(suite, (uint8_t) * p->vpat);
return;
}
SessionConfig *cfg = suite->cfg;
switch (cfg->d_cfg) {
case RANDOM:
init_random(suite);
break;
case ONE_TO_ZERO:
init_stripe(suite, 0xff);
break;
case ZERO_TO_ONE:
init_stripe(suite, 0x00);
break;
default:
fprintf(stderr, "[ERROR] - Wrong data pattern %d\n",
cfg->d_cfg);
exit(1);
break;
}
}
void scan_random(HammerSuite * suite, HammerPattern * h_patt, size_t adj_rows)
{
ADDRMapper *mapper = suite->mapper;
SessionConfig *cfg = suite->cfg;
DRAMAddr d_tmp;
FlipVal flip;
d_tmp.bank = h_patt->d_lst[0].bank;
for (size_t row = 0; row < cfg->h_rows; row++) {
d_tmp.row = suite->mapper->base_row + row;
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
d_tmp.col = col;
DRAM_pte pte = get_dram_pte(mapper, &d_tmp);
clflush(pte.v_addr);
cpuid();
uint64_t res = cl_rand_comp(&pte);
if (res) {
char *rand_data = cl_rand_gen(&pte.d_addr);
for (int off = 0; off < CL_SIZE; off++) {
if (!((res >> off) & 1))
continue;
d_tmp.col += off;
flip.d_vict = d_tmp;
flip.f_og = (uint8_t) rand_data[off];
flip.f_new = *(uint8_t *) (pte.v_addr + off);
flip.h_patt = h_patt;
assert(flip.f_og != flip.f_new);
export_flip(&flip);
}
memcpy((char *)(pte.v_addr), rand_data, CL_SIZE);
}
}
}
}
int find_flip(HammerSuite * suite, HammerPattern * h_patt, FlipVal *orig)
{
ADDRMapper *mapper = suite->mapper;
SessionConfig *cfg = suite->cfg;
DRAMAddr d_tmp;
FlipVal flip;;
d_tmp.bank = orig->d_vict.bank;
d_tmp.row = orig->d_vict.row;
d_tmp.col = orig->d_vict.col;
DRAM_pte pte = get_dram_pte(mapper, &d_tmp);
clflush(pte.v_addr);
cpuid();
int off = cl_rand_comp(&pte);
if (off != -1) {
return 1;
}
return 0;
}
bool in_hPatt(DRAMAddr * d_addr, HammerPattern * h_patt)
{
for (int i = 0; i < h_patt->len; i++) {
if (d_addr_eq_row(&h_patt->d_lst[i], d_addr))
return true;
}
return false;
}
uint64_t cl_stripe_cmp(DRAM_pte * pte, uint8_t val)
{
uint64_t res = 0;
#ifdef POINTER_CHAISING
for (int_t i = 8; i < CL_SIZE; i++) {
#else
for (int i = 0; i < CL_SIZE; i++) {
#endif
if (*(uint8_t*) (pte->v_addr + i) != val) {
res |= 1UL<<i;
}
}
return res;
}
void scan_stripe(HammerSuite * suite, HammerPattern * h_patt, size_t adj_rows,
uint8_t val)
{
ADDRMapper *mapper = suite->mapper;
SessionConfig *cfg = suite->cfg;
DRAMAddr d_tmp;
FlipVal flip;
d_tmp.bank = h_patt->d_lst[0].bank;
uint8_t t_val = val;
for (size_t row = 0; row < cfg->h_rows; row++) {
d_tmp.row = suite->mapper->base_row + row;
t_val = val;
if (in_hPatt(&d_tmp, h_patt))
if (p->tpat != (void *)NULL && p->vpat != (void *)NULL)
t_val = (uint8_t) * p->tpat;
else
t_val ^= 0xff;
for (size_t col = 0; col < ROW_SIZE; col += (1 << 6)) {
d_tmp.col = col;
DRAM_pte pte = get_dram_pte(mapper, &d_tmp);
clflush(pte.v_addr);
cpuid();
uint64_t res = cl_stripe_cmp(&pte, t_val);
if (res) {
for (int off = 0; off < CL_SIZE; off++) {
if (!((res >> off) & 1))
continue;
d_tmp.col += off;
flip.d_vict = d_tmp;
flip.f_og = (uint8_t) t_val;
flip.f_new = *(uint8_t *) (pte.v_addr + off);
flip.h_patt = h_patt;
export_flip(&flip);
memset(pte.v_addr + off, t_val, 1);
}
memset((char *)(pte.v_addr), t_val, CL_SIZE);
}
}
}
}
// TODO adj_rows should tell how many rows to scan out of the bank. Not currently used
void scan_rows(HammerSuite * suite, HammerPattern * h_patt, size_t adj_rows)
{
if (p->vpat != (void *)NULL && p->tpat != (void *)NULL) {
scan_stripe(suite, h_patt, adj_rows, (uint8_t) * p->vpat);
return;
}
SessionConfig *cfg = suite->cfg;
switch (cfg->d_cfg) {
case RANDOM:
// rows are already filled for random data patt
scan_random(suite, h_patt, adj_rows);
break;
case ONE_TO_ZERO:
scan_stripe(suite, h_patt, adj_rows, 0xff);
break;
case ZERO_TO_ONE:
scan_stripe(suite, h_patt, adj_rows, 0x00);
break;
default:
fprintf(stderr, "[ERROR] - Wrong data pattern %d\n",
cfg->d_cfg);
exit(1);
break;
}
}
int free_triple_sided_test(HammerSuite * suite)
{
MemoryBuffer *mem = suite->mem;
SessionConfig *cfg = suite->cfg;
DRAMAddr d_base = suite->d_base;
d_base.col = 0;
HammerPattern h_patt;
h_patt.len = 3;
h_patt.rounds = cfg->h_rounds;
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
init_chunk(suite);
fprintf(stderr, "CL_SEED: %lx\n", CL_SEED);
h_patt.d_lst[0] = d_base;
for (int r0 = 1; r0 < cfg->h_rows; r0++) {
for (int r1 = r0; r1 < cfg->h_rows; r1++) {
if (r0 == r1)
continue;
h_patt.d_lst[1].row = h_patt.d_lst[0].row + r0;
h_patt.d_lst[2].row = h_patt.d_lst[0].row + r1;
h_patt.d_lst[0].bank = 0;
h_patt.d_lst[1].bank = 0;
h_patt.d_lst[2].bank = 0;
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
h_patt.d_lst[0].bank = bk;
h_patt.d_lst[1].bank = bk;
h_patt.d_lst[2].bank = bk;
// fill all the aggressor rows
for (int idx = 0; idx < 3; idx++) {
fill_row(suite, &h_patt.d_lst[idx], cfg->d_cfg);
}
uint64_t time = hammer_it(&h_patt, mem);
fprintf(stderr, "%ld ", time);
scan_rows(suite, &h_patt, 0);
for (int idx = 0; idx < 3; idx++) {
fill_row(suite, &h_patt.d_lst[idx], (HammerData) ((int)cfg->
d_cfg ^ (int) REVERSE));
}
}
fprintf(stderr, "\n");
}
}
free(h_patt.d_lst);
}
int assisted_double_sided_test(HammerSuite * suite)
{
MemoryBuffer *mem = suite->mem;
SessionConfig *cfg = suite->cfg;
DRAMAddr d_base = suite->d_base;
d_base.col = 0;
HammerPattern h_patt;
h_patt.len = 3;
h_patt.rounds = cfg->h_rounds;
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
init_chunk(suite);
fprintf(stderr, "CL_SEED: %lx\n", CL_SEED);
h_patt.d_lst[0] = d_base;
for (int r0 = 1; r0 < cfg->h_rows; r0++) {
h_patt.d_lst[1].row = d_base.row + r0;
h_patt.d_lst[2].row = h_patt.d_lst[1].row + 2;
h_patt.d_lst[0].row =
d_base.row + get_rnd_int(0, cfg->h_rows - 1);
while (h_patt.d_lst[0].row == h_patt.d_lst[1].row
|| h_patt.d_lst[0].row == h_patt.d_lst[2].row)
h_patt.d_lst[0].row =
d_base.row + get_rnd_int(0, cfg->h_rows - 1);
if (h_patt.d_lst[2].row >= d_base.row + cfg->h_rows)
break;
h_patt.d_lst[0].bank = 0;
h_patt.d_lst[1].bank = 0;
h_patt.d_lst[2].bank = 0;
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
h_patt.d_lst[0].bank = bk;
h_patt.d_lst[1].bank = bk;
h_patt.d_lst[2].bank = bk;
// fill all the aggressor rows
for (int idx = 0; idx < 3; idx++) {
fill_row(suite, &h_patt.d_lst[idx], cfg->d_cfg);
// fprintf(stderr, "d_addr: %s\n", dram_2_str(&h_patt.d_lst[idx]));
}
// fprintf(stderr, "d_addr: %s\n", dram_2_str(&h_patt.d_lst[idx]));
uint64_t time = hammer_it(&h_patt, mem);
fprintf(stderr, "%ld ", time);
scan_rows(suite, &h_patt, 0);
for (int idx = 0; idx<3; idx++) {
fill_row(suite, &h_patt.d_lst[idx], (HammerData) ((int)cfg->d_cfg ^ (int)REVERSE));
}
}
fprintf(stderr, "\n");
}
free(h_patt.d_lst);
}
int n_sided_test(HammerSuite * suite)
{
MemoryBuffer *mem = suite->mem;
SessionConfig *cfg = suite->cfg;
DRAMAddr d_base = suite->d_base;
d_base.col = 0;
/* d_base.row = 20480; */
/* d_base.row = 16400; */
HammerPattern h_patt;
h_patt.len = cfg->aggr_n;
h_patt.rounds = cfg->h_rounds;
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
init_chunk(suite);
fprintf(stderr, "CL_SEED: %lx\n", CL_SEED);
h_patt.d_lst[0] = d_base;
const int mem_to_hammer = 256 << 20;
const int n_rows = mem_to_hammer / ((8<<10) * get_banks_cnt());
fprintf(stderr, "Hammering %d rows per bank\n", n_rows);
for (int r0 = 1; r0 < n_rows; r0++) {
h_patt.d_lst[0].row = d_base.row + r0;
int k = 1;
for (; k < cfg->aggr_n; k++) {
h_patt.d_lst[k].row = h_patt.d_lst[k - 1].row + 2;
h_patt.d_lst[k].bank = 0;
}
if (h_patt.d_lst[k - 1].row >= d_base.row + cfg->h_rows)
break;
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
for (size_t bk = 0; bk < get_banks_cnt(); bk++) {
for (int s = 0; s < cfg->aggr_n; s++) {
h_patt.d_lst[s].bank = bk;
}
#ifdef FLIPTABLE
print_start_attack(&h_patt);
#endif
// fill all the aggressor rows
for (int idx = 0; idx < cfg->aggr_n; idx++) {
fill_row(suite, &h_patt.d_lst[idx], cfg->d_cfg);
}
uint64_t time = hammer_it(&h_patt, mem);
fprintf(stderr, "%ld ", time);
scan_rows(suite, &h_patt, 0);
for (int idx = 0; idx<h_patt.len; idx++) {
fill_row(suite, &h_patt.d_lst[idx], (HammerData)
((int)cfg->d_cfg ^ (int)REVERSE));
#ifdef FLIPTABLE
print_end_attack();
#endif
}
}
fprintf(stderr, "\n");
}
free(h_patt.d_lst);
}
void fuzz(HammerSuite *suite, int d, int v)
{
int i;
HammerPattern h_patt;
SessionConfig *cfg = suite->cfg;
h_patt.rounds = cfg->h_rounds;
h_patt.len = cfg->aggr_n;
h_patt.d_lst = (DRAMAddr *) malloc(sizeof(DRAMAddr) * h_patt.len);
memset(h_patt.d_lst, 0x00, sizeof(DRAMAddr) * h_patt.len);
init_chunk(suite);
int offset = random_int(1, 32);
h_patt.d_lst[0] = suite->d_base;
h_patt.d_lst[0].row = suite->d_base.row + offset;
h_patt.d_lst[1] = suite->d_base;
h_patt.d_lst[1].row = h_patt.d_lst[0].row + v + 1;
for (i = 2; i < h_patt.len-1; i+=2) {
h_patt.d_lst[i] = suite->d_base;
h_patt.d_lst[i].row = h_patt.d_lst[i-1].row + d + 1;
h_patt.d_lst[i+1] = suite->d_base;
h_patt.d_lst[i+1].row = h_patt.d_lst[i].row + v + 1;
}
if (h_patt.len % 2) {
h_patt.d_lst[h_patt.len-1] = suite->d_base;
h_patt.d_lst[h_patt.len-1].row = h_patt.d_lst[h_patt.len-2].row + d + 1;
}
fprintf(stderr, "[HAMMER] - %s: ", hPatt_2_str(&h_patt, ROW_FIELD));
for (int bk = 0; bk < get_banks_cnt(); bk++)
{
for (int idx = 0; idx < h_patt.len; idx++) {
h_patt.d_lst[idx].bank = bk;
}
#ifdef FLIPTABLE
print_start_attack(&h_patt);
#endif
for (int idx = 0; idx < h_patt.len; idx++)
fill_row(suite, &h_patt.d_lst[idx], suite->cfg->d_cfg);
uint64_t time = hammer_it(&h_patt, suite->mem);
fprintf(stderr, "%lu ",time);
scan_rows(suite, &h_patt, 0);
for (int idx = 0; idx<h_patt.len; idx++) {
fill_row(suite, &h_patt.d_lst[idx], (HammerData)
((int)suite->cfg->d_cfg ^ (int)REVERSE));
}
#ifdef FLIPTABLE
print_end_attack();
#endif
}
fprintf(stdout, "\n");
free(h_patt.d_lst);
}
void create_dir(const char* dir_name)
{
struct stat st = {0};
if (stat(dir_name, &st) == -1) {
mkdir(dir_name, 0777);
}
}
void fuzzing_session(SessionConfig * cfg, MemoryBuffer * mem)
{
int d, v, aggrs;
srand(CL_SEED);
DRAMAddr d_base = phys_2_dram(virt_2_phys(mem->buffer, mem));
fprintf(stdout, "[INFO] d_base.row:%lu\n", d_base.row);
/* Init FILES */
create_dir(DATA_DIR);
char *out_name = (char *)malloc(500);
char rows_str[10];
strcpy(out_name, DATA_DIR);
strcat(out_name, p->g_out_prefix);
strcat(out_name, ".");
strcat(out_name, "fuzzing");
strcat(out_name, ".");
sprintf(rows_str, "%08ld", d_base.row);
strcat(out_name, rows_str);
strcat(out_name, ".");
sprintf(rows_str, "%ld", cfg->h_rounds);
strcat(out_name, rows_str);
strcat(out_name, ".");
strcat(out_name, REFRESH_VAL);
strcat(out_name, ".csv");
if (p->g_flags & F_NO_OVERWRITE) {
int cnt = 0;
char *tmp_name = (char *)malloc(500);
strncpy(tmp_name, out_name, strlen(out_name));
while (access(tmp_name, F_OK) != -1) {
cnt++;
sprintf(tmp_name, "%s.%02d", out_name, cnt);
}
strncpy(out_name, tmp_name, strlen(tmp_name));
free(tmp_name);
}
out_fd = fopen(out_name, "w+");
assert(out_fd != NULL);
HammerSuite *suite = (HammerSuite *) malloc(sizeof(HammerSuite));
suite->mem = mem;
suite->cfg = cfg;
suite->d_base = d_base;
suite->mapper = (ADDRMapper *) malloc(sizeof(ADDRMapper));
init_addr_mapper(suite->mapper, mem, &suite->d_base, cfg->h_rows);
while(1) {
cfg->aggr_n = random_int(2, 32);
d = random_int(0, 16);
v = random_int(1, 4);
fuzz(suite, d, v);
}
}
void hammer_session(SessionConfig * cfg, MemoryBuffer * memory)
{
MemoryBuffer mem = *memory;
DRAMAddr d_base = phys_2_dram(virt_2_phys(mem.buffer, &mem));
d_base.row += cfg->base_off;
create_dir(DATA_DIR);
char *out_name = (char *)malloc(500);
char rows_str[10];
strcpy(out_name, DATA_DIR);
strcat(out_name, p->g_out_prefix);
strcat(out_name, ".");
if (cfg->h_cfg) {
char config[15];
sprintf(config, config_str[cfg->h_cfg], cfg->aggr_n);
strcat(out_name, config);
} else {
strcat(out_name, config_str[cfg->h_cfg]);
}
strcat(out_name, ".");
sprintf(rows_str, "%08ld", d_base.row);
strcat(out_name, rows_str);
strcat(out_name, ".");
sprintf(rows_str, "%03ld", cfg->h_rows);
strcat(out_name, rows_str);
strcat(out_name, ".");
sprintf(rows_str, "%ld", cfg->h_rounds);
strcat(out_name, rows_str);
strcat(out_name, ".");
strcat(out_name, REFRESH_VAL);
strcat(out_name, ".csv");
if (p->g_flags & F_NO_OVERWRITE) {
int cnt = 0;
char *tmp_name = (char *)malloc(500);
strncpy(tmp_name, out_name, strlen(out_name));
while (access(tmp_name, F_OK) != -1) {
cnt++;
sprintf(tmp_name, "%s.%02d", out_name, cnt);
}
strncpy(out_name, tmp_name, strlen(tmp_name));
free(tmp_name);
}
out_fd = fopen(out_name, "w+");
fprintf(stderr,
"[LOG] - Hammer session! access pattern: %s\t data pattern: %s\n",
config_str[cfg->h_cfg], data_str[cfg->d_cfg]);
fprintf(stderr, "[LOG] - File: %s\n", out_name);
HammerSuite *suite = (HammerSuite *) malloc(sizeof(HammerSuite));
suite->cfg = cfg;
suite->mem = &mem;
suite->d_base = d_base;
suite->mapper = (ADDRMapper *) malloc(sizeof(ADDRMapper));
init_addr_mapper(suite->mapper, &mem, &suite->d_base, cfg->h_rows);
#ifndef FLIPTABLE
export_cfg(suite); // export the configuration of the experiment to file.
fprintf(out_fd, OUT_HEAD);
#endif
switch (cfg->h_cfg) {
case ASSISTED_DOUBLE_SIDED:
{
suite->hammer_test =
(int (*)(void *))assisted_double_sided_test;
break;
}
case FREE_TRIPLE_SIDED:
{
suite->hammer_test =
(int (*)(void *))free_triple_sided_test;
break;
}
case N_SIDED:
{
assert(cfg->aggr_n > 1);
suite->hammer_test = (int (*)(void *))n_sided_test;
break;
}
default:
{
suite->hammer_test = (int (*)(void *))n_sided_test;
}
}
suite->hammer_test(suite);
fclose(out_fd);
tear_down_addr_mapper(suite->mapper);
free(suite);
}

View File

@ -0,0 +1,26 @@
#pragma once
#include "memory.h"
#include "types.h"
#include "dram-address.h"
typedef struct {
DRAMAddr d_addr;
char *v_addr;
} DRAM_pte;
typedef struct {
DRAM_pte *lst;
size_t len;
} RowMap;
typedef struct {
size_t base_row; // used as an offset
RowMap *row_maps;
} ADDRMapper;
void init_addr_mapper(ADDRMapper * mapper, MemoryBuffer * mem,
DRAMAddr * d_base, size_t h_rows);
RowMap get_row_map(ADDRMapper * mapper, DRAMAddr * d_addr);
DRAM_pte get_dram_pte(ADDRMapper * mapper, DRAMAddr * d_addr);
void tear_down_addr_mapper(ADDRMapper * mapper);

View File

@ -0,0 +1,6 @@
#pragma once
#include "types.h"
int alloc_buffer(MemoryBuffer * mem);
int free_buffer(MemoryBuffer * mem);

View File

@ -0,0 +1,33 @@
#pragma once
#include "types.h"
#define HASH_FN_CNT 6
typedef struct {
uint64_t lst[HASH_FN_CNT];
uint64_t len;
} AddrFns;
typedef struct {
AddrFns h_fns;
uint64_t row_mask;
uint64_t col_mask;
} DRAMLayout;
typedef struct {
/* bank is a simplified addressing of <ch,dimm,rk,bg,bk>
where all this will eventually map to a specific bank */
uint64_t bank;
uint64_t row;
uint64_t col;
} DRAMAddr;
physaddr_t dram_2_phys(DRAMAddr d_addr);
DRAMAddr phys_2_dram(physaddr_t p_addr);
char *dram_2_str(DRAMAddr * d_addr);
char *dramLayout_2_str(DRAMLayout * mem_layout);
DRAMLayout *get_dram_layout();
uint64_t get_banks_cnt();
bool d_addr_eq(DRAMAddr * d1, DRAMAddr * d2);
bool d_addr_eq_row(DRAMAddr * d1, DRAMAddr * d2);

View File

@ -0,0 +1,8 @@
#pragma once
#include <stdint.h>
#include "types.h"
void hammer_session(SessionConfig * cfg, MemoryBuffer * memory);
void fuzzing_session(SessionConfig * cfg, MemoryBuffer * memory);

View File

@ -0,0 +1,8 @@
#pragma once
#include "types.h"
#include <stddef.h>
void set_physmap(MemoryBuffer * mem);
physaddr_t virt_2_phys(char *v_addr, MemoryBuffer * mem);
char *phys_2_virt(physaddr_t p_addr, MemoryBuffer * mem);

View File

@ -0,0 +1,42 @@
/*
* Copyright (c) 2018 Vrije Universiteit Amsterdam
*
* This program is licensed under the GPL2+.
*/
#ifndef PARAMS_H
#define PARAMS_H 1
#include <stddef.h>
#include <stdint.h>
#define ROUNDS_std 1000000
#define HUGETLB_std "/mnt/huge/buff"
#define CONFIG_NAME_std "tmp/s_cfg.bin"
#define O_FILE_std "DIMM00"
#define ALLOC_SIZE 1<<30
#define ALIGN_std 2<<20
#define PATT_LEN 1024
#define AGGR_std 2
#define HUGE_YES
typedef struct ProfileParams {
uint64_t g_flags = 0;
char *g_out_prefix;
char *tpat = (char *)NULL;
char *vpat = (char *)NULL;
int threshold = 0;
int fuzzing = 0; // start fuzzing!!
size_t m_size = ALLOC_SIZE;
size_t m_align = ALIGN_std;
size_t rounds = ROUNDS_std;
size_t base_off = 0;
char *huge_file = (char *)HUGETLB_std;
int huge_fd;
char *conf_file = (char *)CONFIG_NAME_std;
int aggr = AGGR_std;
} ProfileParams;
int process_argv(int argc, char *argv[], ProfileParams *params);
#endif /* params.h */

View File

@ -0,0 +1,54 @@
#pragma once
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#define STRIPE_SHIFT 0 // not needed anymore
#define O2Z (0b01 << STRIPE_SHIFT) // ONE_TO_ZERO
#define Z2O (0b10 << STRIPE_SHIFT) // ZERO_TO_ONE
#define REVERSE_VAL (O2Z ^ Z2O) // if you xor REVERSE with one of the stripe val it will give you the opposite
static const char *config_str[] =
{ "assisted-dbl", "free-triple", "%i_sided"};
static const char *data_str[] = { "random", "i2o", "o2i" };
typedef enum {
ASSISTED_DOUBLE_SIDED,
FREE_TRIPLE_SIDED,
N_SIDED,
} HammerConfig;
typedef enum {
RANDOM,
ONE_TO_ZERO = O2Z,
ZERO_TO_ONE = Z2O,
REVERSE = REVERSE_VAL
} HammerData;
typedef uint64_t physaddr_t;
/* not necessarily page-aligned addresses.
used only to keep track of virt<->phys mapping. */
typedef struct {
char *v_addr;
physaddr_t p_addr;
} pte_t;
typedef struct {
HammerConfig h_cfg;
HammerData d_cfg;
size_t h_rows;
size_t h_rounds;
size_t base_off; // offset from the beginning of the contig chunk
int aggr_n;
} SessionConfig;
typedef struct {
char *buffer; // base addr
pte_t *physmap; // list of virt<->phys mapping for every page
int fd; // fd in the case of mmap hugetlbfs
uint64_t size; // in bytes
uint64_t align;
uint64_t flags; // from params
} MemoryBuffer;

View File

@ -0,0 +1,128 @@
#pragma once
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#define BIT_SET(x) (1ULL<<(x))
#define BIT_VAL(b,val) (((val) >> (b)) & 1)
#define KB(x) ((x)<<10ULL)
#define MB(x) ((x)<<20ULL)
#define GB(x) ((x)<<30ULL)
#define CL_SHIFT 6
#define CL_SIZE 64
#define PAGE_SIZE 4096
#define ROW_SIZE (8<<10)
#define ALIGN_TO(X, Y) ((X) & (~((1LL<<(Y))-1LL))) // Mask out the lower Y bits
#define LS_BITMASK(X) ((1LL<<(X))-1LL) // Mask only the lower X bits
// Flags
#define F_CLEAR 0L
#define F_VERBOSE BIT_SET(0)
#define F_EXPORT BIT_SET(1)
#define F_CONFIG BIT_SET(2)
#define F_NO_OVERWRITE BIT_SET(3)
#define MEM_SHIFT (30L)
#define MEM_MASK 0b11111ULL << MEM_SHIFT
#define F_ALLOC_HUGE BIT_SET(MEM_SHIFT)
#define F_ALLOC_HUGE_1G F_ALLOC_HUGE | BIT_SET(MEM_SHIFT+1)
#define F_ALLOC_HUGE_2M F_ALLOC_HUGE | BIT_SET(MEM_SHIFT+2)
#define F_POPULATE BIT_SET(MEM_SHIFT+3)
#define NOT_FOUND ((void*) -1)
#define NOT_OPENED -1
#define TIMESPEC_NSEC(ts) ((ts)->tv_sec * 1e9 + (ts)->tv_nsec)
//----------------------------------------------------------
// Static functions
static inline __attribute__ ((always_inline))
void clflush(volatile void *p)
{
asm volatile ("clflush (%0)\n"::"r" (p):"memory");
}
static inline __attribute__ ((always_inline))
void clflushopt(volatile void *p)
{
#ifdef DDR3
asm volatile ("clflush (%0)\n"::"r" (p):"memory");
#else
asm volatile ("clflushopt (%0)\n"::"r" (p):"memory");
#
#endif
}
static inline __attribute__ ((always_inline))
void cpuid()
{
asm volatile ("cpuid":::"rax", "rbx", "rcx", "rdx");
}
static inline __attribute__ ((always_inline))
void mfence()
{
asm volatile ("mfence":::"memory");
}
static inline __attribute__ ((always_inline))
void sfence()
{
asm volatile ("sfence":::"memory");
}
static inline __attribute__ ((always_inline))
void lfence()
{
asm volatile ("lfence":::"memory");
}
static inline __attribute__ ((always_inline))
uint64_t rdtscp(void)
{
uint64_t lo, hi;
asm volatile ("rdtscp\n":"=a" (lo), "=d"(hi)
::"%rcx");
return (hi << 32) | lo;
}
static inline __attribute__ ((always_inline))
uint64_t rdtsc(void)
{
uint64_t lo, hi;
asm volatile ("rdtsc\n":"=a" (lo), "=d"(hi)
::"%rcx");
return (hi << 32) | lo;
}
static inline __attribute__ ((always_inline))
uint64_t realtime_now()
{
struct timespec now_ts;
clock_gettime(CLOCK_MONOTONIC, &now_ts);
return TIMESPEC_NSEC(&now_ts);
}
// void set_physmap(mem_buff_t* mem);
// pte_t get_pte(char* v_addr, mem_buff_t* mem);
// addr_tuple reverse_addr_tuple(uint64_t p_addr, mem_buff_t* mem);
//----------------------------------------------------------
// Helpers
int gt(const void *a, const void *b);
double mean(uint64_t * vals, size_t size);
uint64_t median(uint64_t * vals, size_t size);
char *bit_string(uint64_t val);
char *int_2_bin(uint64_t val);
char *get_rnd_addr(char *base, size_t m_size, size_t align);
int get_rnd_int(int min, int max);

111
hammersuite/src/main.c Normal file
View File

@ -0,0 +1,111 @@
#include "stdio.h"
// #include <x86intrin.h> /* for rdtsc, rdtscp, clflush */
#include <stdint.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <sched.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdbool.h>
#include <getopt.h>
#include <errno.h>
#include <stdint.h>
#include "include/utils.h"
#include "include/types.h"
#include "include/allocator.h"
#include "include/memory.h"
#include "include/dram-address.h"
#include "include/hammer-suite.h"
#include "include/params.h"
ProfileParams *p;
DRAMLayout g_mem_layout = {{{0x4080,0x48000,0x90000,0x120000,0x1b300}, 5}, 0xffffc0000, ROW_SIZE-1};
void read_config(SessionConfig * cfg, char *f_name)
{
FILE *fp = fopen(f_name, "rb");
int p_size;
size_t res;
assert(fp != NULL);
res = fread(cfg, sizeof(SessionConfig), 1, fp);
assert(res == 1);
fclose(fp);
return;
}
void gmem_dump()
{
FILE *fp = fopen("g_mem_dump.bin", "wb+");
fwrite(&g_mem_layout, sizeof(DRAMLayout), 1, fp);
fclose(fp);
#ifdef DEBUG
DRAMLayout tmp;
fp = fopen("g_mem_dump.bin", "rb");
fread(&tmp, sizeof(DRAMLayout), 1, fp);
fclose(fp);
assert(tmp->h_fns->len == g_mem_layout->h_fns->len);
assert(tmp->bank == g_mem_layout->bank);
assert(tmp->row == g_mem_layout->row);
assert(tmp->col == g_mem_layout->col);
#endif
}
int main(int argc, char **argv)
{
srand(time(NULL));
p = (ProfileParams*)malloc(sizeof(ProfileParams));
if (p == NULL) {
fprintf(stderr, "[ERROR] Memory allocation\n");
exit(1);
}
if(process_argv(argc, argv, p) == -1) {
free(p);
exit(1);
}
MemoryBuffer mem = {
.buffer = NULL,
.physmap = NULL,
.fd = p->huge_fd,
.size = p->m_size,
.align = p->m_align,
.flags = p->g_flags & MEM_MASK
};
alloc_buffer(&mem);
set_physmap(&mem);
gmem_dump();
SessionConfig s_cfg;
memset(&s_cfg, 0, sizeof(SessionConfig));
if (p->g_flags & F_CONFIG) {
read_config(&s_cfg, p->conf_file);
} else {
// HARDCODED values
s_cfg.h_rows = PATT_LEN;
s_cfg.h_rounds = p->rounds;
s_cfg.h_cfg = N_SIDED;
s_cfg.d_cfg = RANDOM;
s_cfg.base_off = p->base_off;
s_cfg.aggr_n = p->aggr;
}
if (p->fuzzing) {
fuzzing_session(&s_cfg, &mem);
} else {
hammer_session(&s_cfg, &mem);
}
close(p->huge_fd);
return 0;
}

108
hammersuite/src/memory.c Normal file
View File

@ -0,0 +1,108 @@
#include "memory.h"
#include "utils.h"
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#define DEF_RNG_LEN (8<<10)
#define DEBUG
#define DEBUG_LINE fprintf(stderr, "[DEBUG] - GOT HERE\n");
static physaddr_t base_phys = 0L;
uint64_t get_pfn(uint64_t entry)
{
return ((entry) & 0x7fffffffffffffff);
}
physaddr_t get_physaddr(uint64_t v_addr, int pmap_fd)
{
uint64_t entry;
uint64_t offset = (v_addr / 4096) * sizeof(entry);
uint64_t pfn;
bool to_open = false;
// assert(fd >= 0);
if (pmap_fd == NOT_OPENED) {
pmap_fd = open("/proc/self/pagemap", O_RDONLY);
assert(pmap_fd >= 0);
to_open = true;
}
// int rd = fread(&entry, sizeof(entry), 1 ,fp);
int bytes_read = pread(pmap_fd, &entry, sizeof(entry), offset);
assert(bytes_read == 8);
assert(entry & (1ULL << 63));
if (to_open) {
close(pmap_fd);
}
pfn = get_pfn(entry);
assert(pfn != 0);
return (pfn << 12) | (v_addr & 4095);
}
int phys_cmp(const void *p1, const void *p2)
{
return ((pte_t *) p1)->p_addr - ((pte_t *) p2)->p_addr;
}
// WARNING optimization works only with contiguous memory!!
void set_physmap(MemoryBuffer * mem)
{
int l_size = mem->size / PAGE_SIZE;
pte_t *physmap = (pte_t *) malloc(sizeof(pte_t) * l_size);
int pmap_fd = open("/proc/self/pagemap", O_RDONLY);
assert(pmap_fd >= 0);
base_phys = get_physaddr((uint64_t) mem->buffer, pmap_fd);
for (uint64_t tmp = (uint64_t) mem->buffer, idx = 0;
tmp < (uint64_t) mem->buffer + mem->size; tmp += PAGE_SIZE) {
pte_t tmp_pte = { (char *)tmp, get_physaddr(tmp, pmap_fd) };
physmap[idx] = tmp_pte;
idx++;
}
qsort(physmap, mem->size / PAGE_SIZE, sizeof(pte_t), phys_cmp);
close(pmap_fd);
mem->physmap = physmap;
}
physaddr_t virt_2_phys(char *v_addr, MemoryBuffer * mem)
{
for (int i = 0; i < mem->size / PAGE_SIZE; i++) {
if (mem->physmap[i].v_addr ==
(char *)((uint64_t) v_addr & ~((uint64_t) (PAGE_SIZE - 1))))
{
return mem->physmap[i].
p_addr | ((uint64_t) v_addr &
((uint64_t) PAGE_SIZE - 1));
}
}
return (physaddr_t) NOT_FOUND;
}
char *phys_2_virt(physaddr_t p_addr, MemoryBuffer * mem)
{
physaddr_t p_page = p_addr & ~(((uint64_t) PAGE_SIZE - 1));
pte_t src_pte = {.v_addr = 0,.p_addr = p_page };
pte_t *res_pte =
(pte_t *) bsearch(&src_pte, mem->physmap, mem->size / PAGE_SIZE,
sizeof(pte_t), phys_cmp);
if (res_pte == NULL)
return (char *)NOT_FOUND;
return (char *)((uint64_t) res_pte->
v_addr | ((uint64_t) p_addr &
(((uint64_t) PAGE_SIZE - 1))));
}

218
hammersuite/src/params.c Normal file
View File

@ -0,0 +1,218 @@
/*
* Copyright (c) 2016 Andrei Tatar
* Copyright (c) 2017-2018 Vrije Universiteit Amsterdam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "include/params.h"
#include "include/utils.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#include <getopt.h>
void print_usage(char *bin_name)
{
fprintf(stderr,
"[ HELP ] - Usage ./%s [-h] [-r rounds] [-a aggr] [-o o_file] [-v] [--mem mem_size] [--[huge/HUGE] f_name] [--conf f_name] [--align val] [--off val] [--no-overwrite] [--fuzzing]\n",
bin_name);
fprintf(stderr, "\t-h\t\t\t= this help message\n");
fprintf(stderr, "\t-v\t\t\t= verbose\n\n");
fprintf(stderr, "\t-r rounds\t\t= number of rounds per tuple\t\t\t(default: %d)\n", ROUNDS_std);
fprintf(stderr, "\t-a --aggr\t\t= number of aggressors\t\t\t\t(default: %d)\n", AGGR_std);
fprintf(stderr, "\t-o --o_file_prefix\t= prefix for output files\t\t\t(default: %s)\n", O_FILE_std);
fprintf(stderr, "\t--mem mem_size\t\t= allocation size\t\t\t\t(default: %ld)\n",
(uint64_t) ALLOC_SIZE);
fprintf(stderr, "\t--huge f_name\t\t= hugetlbfs entry (1GB if HUGE)\t\t\t(default: %s)\n",
HUGETLB_std);
fprintf(stderr, "\t--conf f_name\t\t= SessionConfig file\t\t\t\t(default: %s)\n",
CONFIG_NAME_std);
fprintf(stderr, "\t--align val\t\t= alignment of the buffer\t\t\t(default: %ld)\n",
(uint64_t) ALIGN_std);
fprintf(stderr, "\t--off val\t\t= offset from first row\t\t\t\t(default: 0)\n");
fprintf(stderr, "\t--no-overwrite\t\t= don't overwrite previous file\n");
fprintf(stderr, "\t-V --victim-pattern\t= hex value for the victim patter\n");
fprintf(stderr, "\t-T --target-pattern\t= hex value for the target pattern\n");
fprintf(stderr, "\t-f --fuzzing\t\t= Start fuzzing (--aggr will be ignored)\n");
fprintf(stderr, "\t-t --threshold\t\t= Align the hammering to refresh ops,\n\t\t\t\t looking at the memory latency in CPU cycles.\t(default: 0)\n");
}
static int str2pat(const char *str, char **pat)
{
char *endp = NULL;
char tmp[3];
char *p;
tmp[2] = '\0';
size_t len = strlen(str);
if (len % 2) {
return EINVAL;
}
len /= 2;
p = (char *)malloc(len);
for (size_t i = 0; i < len; i++) {
tmp[0] = str[2 * i];
tmp[1] = str[2 * i + 1];
errno = 0;
((uint8_t *) p)[i] = (uint8_t) strtol(tmp, &endp, 16);
if (errno) {
free(p);
return errno;
}
if (*endp != '\0') {
free(p);
return EINVAL;
}
}
*pat = p;
return 0;
}
int process_argv(int argc, char *argv[], ProfileParams *p)
{
/* Default */
p->g_flags = 0;
p->tpat = (char *)NULL;
p->vpat = (char *)NULL;
p->threshold = 0;
p->fuzzing = 0; // start fuzzing!!
p->m_size = ALLOC_SIZE;
p->m_align = ALIGN_std;
p->rounds = ROUNDS_std;
p->base_off = 0;
p->huge_file = (char *)HUGETLB_std;
p->conf_file = (char *)CONFIG_NAME_std;
p->aggr = AGGR_std;
const struct option long_options[] = {
/* These options set a flag. */
{"mem", required_argument, 0, 0},
{"align", required_argument, 0, 0},
{"huge", optional_argument, 0, 0},
{"HUGE", optional_argument, 0, 0},
{"conf", optional_argument, 0, 0},
{"off", required_argument, 0, 0},
{"no-overwrite", no_argument, 0, 0},
{.name = "target-pattern",.has_arg = required_argument,.flag = NULL,.val='T'},
{.name = "victim-pattern",.has_arg = required_argument,.flag = NULL,.val = 'V'},
{.name = "aggr",.has_arg = required_argument,.flag = NULL,.val='a'},
{.name = "fuzzing",.has_arg = no_argument,.flag = &p->fuzzing,.val = 1},
{.name = "threshold",.has_arg = required_argument,.flag = NULL,.val = 't'},
{0, 0, 0, 0}
};
p->g_out_prefix = (char *)O_FILE_std;
p->g_flags |= F_POPULATE;
while (1) {
int this_option_optind = optind ? optind : 1;
int option_index = 0;
int arg = getopt_long(argc, argv, "o:d:r:hvV:T:a:ft:",
long_options, &option_index);
if (arg == -1)
break;
switch (arg) {
case 0:
switch (option_index) {
case 0:
p->m_size = atoi(optarg);
break;
case 1:
p->m_align = atoi(optarg);
break;
case 2:
p->g_flags |= F_ALLOC_HUGE_2M;
case 3:
p->huge_file = (char *)malloc(sizeof(char) * strlen(optarg));
strncpy(p->huge_file, optarg, strlen(optarg));
p->g_flags |= F_ALLOC_HUGE_1G;
break;
case 4:
p->g_flags |= F_CONFIG;
if (!optarg)
break;
p->conf_file = (char *)malloc(sizeof(char) * strlen(optarg));
strncpy(p->conf_file, optarg, strlen(optarg));
break;
case 5:
p->base_off = atoi(optarg);
break;
case 6:
p->g_flags |= F_NO_OVERWRITE;
break;
default:
break;
}
break;
case 'o':
p->g_out_prefix = (char *)malloc(sizeof(char) * strlen(optarg));
strncpy(p->g_out_prefix, optarg, strlen(optarg));
p->g_flags |= F_EXPORT;
break;
case 'r':
p->rounds = atoi(optarg);
break;
case 'v':
p->g_flags |= F_VERBOSE;
break;
case 'a':
p->aggr = atoi(optarg);
break;
case 'T':
if (str2pat(optarg, &(p->vpat))) {
fprintf(stderr, "Invalid target fill pattern: %s\n", optarg);
return -1;
}
break;
case 'V':
if (str2pat(optarg, &(p->tpat))) {
fprintf(stderr, "Invalid victim fill pattern: %s\n", optarg);
return -1;
}
break;
case 'f':
p->fuzzing = 1;
break;
case 't':
p->threshold = atoi(optarg);
break;
case 'h':
default:
print_usage(argv[0]);
return -1;
}
}
#ifdef HUGE_YES
p->g_flags |= F_ALLOC_HUGE_1G;
#endif
if (p->g_flags & (F_ALLOC_HUGE_2M | F_ALLOC_HUGE_1G)) {
if ((p->huge_fd = open(p->huge_file, O_CREAT | O_RDWR, 0755)) == -1) {
perror("[ERROR] - Unable to open hugetlbfs");
return -1;
}
}
return 0;
}

81
hammersuite/src/utils.c Normal file
View File

@ -0,0 +1,81 @@
#include "utils.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <string.h>
#include <time.h>
#define PAGE_BITS 12
#define FLAGS (MAP_PRIVATE | MAP_POPULATE | MAP_HUGETLB | (30<<MAP_HUGE_SHIFT))
char *get_rnd_addr(char *base, size_t m_size, size_t align)
{
return (char *)((((uint64_t) base) + (rand() % m_size)) &
(~((uint64_t) align - 1)));
}
int get_rnd_int(int min, int max)
{
return rand() % (max + 1 - min) + min;
}
double mean(uint64_t * vals, size_t size)
{
uint64_t avg = 0;
for (size_t i = 0; i < size; i++) {
avg += vals[i];
}
return ((double)avg) / size;
}
int gt(const void *a, const void *b)
{
return (*(int *)a - *(int *)b);
}
uint64_t median(uint64_t * vals, size_t size)
{
qsort(vals, size, sizeof(uint64_t), gt);
return ((size % 2) ==
0) ? vals[size / 2] : (vals[(size_t) size / 2] +
vals[((size_t) size / 2 + 1)]) / 2;
}
char *bit_string(uint64_t val)
{
static char bit_str[256];
char itoa_str[8];
strcpy(bit_str, "");
for (int shift = 0; shift < 64; shift++) {
if ((val >> shift) & 1) {
if (strcmp(bit_str, "") != 0) {
strcat(bit_str, "+ ");
}
sprintf(itoa_str, "%d ", shift);
strcat(bit_str, itoa_str);
}
}
return bit_str;
}
char *int_2_bin(uint64_t val)
{
static char bit_str[256];
char itoa_str[8];
strcpy(bit_str, "0b");
for (int shift = 64 - __builtin_clzl(val); shift >= 0; --shift) {
sprintf(itoa_str, "%d", (int)(val >> shift) & 1);
strcat(bit_str, itoa_str);
}
return bit_str;
}

29
py/hammerstats.py Executable file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# Copyright (c) 2016 Andrei Tatar
# Copyright (c) 2018 Vrije Universiteit Amsterdam
#
# This program is licensed under the GPL2+.
import sys
from hammertime import fliptable
if __name__ == '__main__':
if len(sys.argv) < 2:
print("Missing arguments")
print("usage: {} PROFILE_PATH [...]".format(sys.argv[0]))
else:
try:
for fn in sys.argv[1:]:
print('Stats for {}:'.format(fn))
ft = fliptable.Fliptable.load_file(fn)
natks = str(len(ft))
print('Hammers: {}'.format(natks))
flips = [len(x.flips) for x in ft if x.flips]
print('w/flips: {{:{}d}}'.format(len(natks)).format(len(flips)))
print('Total Bit Flips: {}'.format(sum(flips)))
except KeyboardInterrupt:
print('Interrupted, exiting...')

View File

@ -0,0 +1,3 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
__all__ = ['sim', 'fliptable', 'dramtrans']

10
py/hammertime/_native.c Normal file
View File

@ -0,0 +1,10 @@
#include "stdint.h"
uint64_t parity(uint64_t v) {
return __builtin_parityl(v);
}
uint64_t ctzl(uint64_t v) {
return __builtin_ctzl(v);
}

BIN
py/hammertime/_native.so Executable file

Binary file not shown.

171
py/hammertime/dramtrans.py Normal file
View File

@ -0,0 +1,171 @@
#!/usr/bin/env python3
import os
import sys
import struct
import ctypes
import ctypes.util
import functools
HASH_FN_CNT = 6
_native = None
@functools.total_ordering
class DRAMAddr(ctypes.Structure):
_fields_ = [('bank', ctypes.c_uint64),
('row', ctypes.c_uint64),
('col', ctypes.c_uint64)]
def __str__(self):
return 'b{0.bank:02d}.r{0.row:06d}.c{0.col:04d}'.format(self)
def __repr__(self):
return self.__str__()
# def __repr__(self):
# return '{0}(b={1.bank},r={1.row},c={1.col})'.format(type(self).__name__, self)
def __eq__(self, other):
if isinstance(other, DRAMAddr):
return self.numeric_value == other.numeric_value
else:
return NotImplemented
def __lt__(self, other):
if isinstance(other, DRAMAddr):
return self.numeric_value < other.numeric_value
else:
return NotImplemented
def __hash__(self):
return self.numeric_value
def __len__(self):
return len(self._fields_)
def __getitem__(self, key):
if isinstance(key, int):
return getattr(self, self._fields_[key][0])
elif isinstance(key, slice):
start, stop, step = key.indices(len(self._fields_))
return tuple(getattr(self, self._fields_[k][0]) for k in range(start, stop, step))
else:
raise TypeError('{} object cannot be indexed by {}'.format(type(self).__name__, type(key).__name__))
def same_bank(self, other):
return self.bank == other.bank
@property
def numeric_value(self):
return (self.col + (self.row << 16) + (self.bank << 32))
def __add__(self, other):
if isinstance(other, DRAMAddr):
return type(self)(
self.bank + other.bank,
self.row + other.row,
self.col + other.col
)
else:
return NotImplemented
def __sub__(self, other):
if isinstance(other, DRAMAddr):
return type(self)(
self.bank - other.bank,
self.row - other.row,
self.col - other.col
)
else:
return NotImplemented
class _AddrFns(ctypes.Structure):
_fields_ = [("lst", ctypes.c_uint64* HASH_FN_CNT),
("len", ctypes.c_uint64)]
class _DRAMLayout(ctypes.Structure):
_fields_ = [("h_fns", _AddrFns),
("row_mask", ctypes.c_uint64),
("col_mask", ctypes.c_uint64)]
def __init__(self, upack):
self.h_fns.lst = (ctypes.c_uint64*6) (*[0x2040,0x44000,0x88000,0x110000,0x220000,0x00])
self.h_fns.len = 5
self.row_mask = 0xffffc0000
self.col_mask = ((1<<13)-1)
# self.h_fns.lst = upack[0:HASH_FN_CNT]
# self.h_fns.len = upack[HASH_FN_CNT]
# self.row_mask = upack[HASH_FN_CNT+1]
# self.col_mask = upack[HASH_FN_CNT+2]
@property
def num_banks(self):
return 1<<self.AddrFns.len
def get_dram_row(self, p_addr):
return (p_addr & self.row_mask) >> _native.ctzl(self.row_mask)
def get_dram_row(self, p_addr):
return (p_addr & self.col_mask) >> _native.ctzl(self.col_mask)
class MemorySystem(ctypes.Structure):
_fields_ = [('mem_layout', _DRAMLayout)]
def __init__(self):
if _native == None:
load_native_funcs()
return super().__init__()
def load(self, s):
DRAMLAyout_fmt = f"{HASH_FN_CNT}QQQQ"
self.mem_layout = _DRAMLayout(struct.unpack(DRAMLAyout_fmt, s))
def load_file(self, fname):
with open(fname, 'rb') as f:
return self.load(f.read())
def num_banks(self):
return self.mem_layout.num_banks
def resolve(self, p_addr):
mem = self.mem_layout
d_addr = DRAMAddr(0,0,0)
for i in range(mem.h_fns.len):
d_addr.bank |= (_native.parity(p_addr & mem.h_fns.lst[i]) << i)
d_addr.row = mem.get_dram_row(p_addr)
d_addr.col = mem.get_dram_col(p_addr)
return d_addr
def resolve_reverse(self, d_addr):
mem = self.mem_layout
p_addr = (d_addr.row << _native.ctzl(mem.row_mask))
p_addr |= (d_addr.col << _native.ctzl(mem.col_mask))
for i in range(mem.h_fns.len):
masked_addr = p_addr & mem.h_fns.lst[i]
if _native.parity(masked_addr) == ((d_addr.bank >> i) & 1):
continue
# else flip a bit of the address so that the address respects the dram h_fn
# that is get only bits not affecting the row.
h_lsb = _native.ctzl((mem.h_fns.lst[i]) & ~(mem.col_mask) & ~(mem.row_mask))
p_addr ^= 1<<h_lsb
return p_addr
def load_native_funcs():
global _native
path = os.path.dirname(os.path.abspath(__file__))
_native = ctypes.CDLL(os.path.join(path, "_native.so"))
_native.parity.restype = ctypes.c_uint64
_native.parity.argtypes= [ctypes.c_uint64]
_native.ctzl.restype = ctypes.c_uint64
_native.ctzl.argtypes= [ctypes.c_uint64]

285
py/hammertime/fliptable.py Normal file
View File

@ -0,0 +1,285 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# Copyright (c) 2016 Andrei Tatar
# Copyright (c) 2017-2018 Vrije Universiteit Amsterdam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Module providing utilities for working with profile output and fliptables."""
import re
import pprint as pp
from collections import namedtuple
from hammertime.dramtrans import DRAMAddr
RE_DICT = re.compile("(?P<key>[-\d\w]+)\s*:\s*(?P<val>[-.\d\w]+)")
#ADDR_RE = re.compile("r(?P<row>\d+)\.bk(?P<bk>\d+)(\.col(?P<col>\d+))?(\.p(?P<prob>\d+))?")
ADDR_RE = re.compile("r(?P<row>\d+)\.bk(?P<bank>\d+)(\.col(?P<col>\d+))?")
#ADDR_FMT = r'\((\w+)\s+(\w+)\s*(\w+)?\)'
#ADDR_RE = re.compile(ADDR_FMT)
# 87,c7,r16447.bk18.col3994
BFLIP_FMT = r'(?P<exp>\w{2}),(?P<got>\w{2}),(?P<addr>[.\w\d]+)'
BFLIP_RE = re.compile(BFLIP_FMT)
ADDR_TRG_RE = re.compile("b(?P<bk>[\d\w]+)\.r(?P<row>[\d\w]+)")
#VICT_FMT = r'{} (?:{}\s?)+'.format(ADDR_FMT, BFLIP_FMT)
#VICT_RE = re.compile(VICT_FMT)
class Corruption(namedtuple('Corruption', ['addr', 'got', 'exp'])):
def __str__(self):
return '({0.addr}|{0.got:02x}|{0.exp:02x})'.format(self)
def __repr__(self):
return self.__str__()
def to_flips(self):
flips = []
pup = ~self.exp & self.got & 0xff
pdn = self.exp & ~self.got & 0xff
bit = 0
while pup:
mask = 1 << bit
if (pup & mask):
flips.append(Flip(self.addr, bit, True))
pup &= ~mask
bit += 1
bit = 0
while pdn:
mask = 1 << bit
if (pdn & mask):
flips.append(Flip(self.addr, bit, False))
pdn &= ~mask
bit += 1
return flips
class Flip(namedtuple('Flip', ['addr', 'bit', 'pullup'])):
def to_corruption(self, og=None):
fmask = 1 << (self.bit % 8)
if og is None:
pat = 0 if self.pullup else 0xff
else:
pat = og
val = pat | fmask if self.pullup else pat & ~fmask
return Corruption(addr=self.addr, got=val, exp=pat)
def to_physmem(self, msys):
return type(self)(msys.resolve_reverse(self.addr), self.bit, self.pullup)
Diff = namedtuple('Diff', ['self_only', 'common', 'other_only'])
class Attack(namedtuple('Attack', ['targets', 'flips'])):
def diff(self, other):
if not isinstance(other, type(self)):
raise TypeError('Attack instance expected for diff')
elif not self.targets == other.targets:
raise ValueError('Cannot diff attacks with different targets')
else:
return Diff(
type(self)(self.targets, self.flips - other.flips),
type(self)(self.targets, self.flips & other.flips),
type(self)(self.targets, other.flips - self.flips)
)
def merge(self, other):
if not isinstance(other, type(self)):
raise TypeError('Attack instance expected for merge')
elif not self.targets == other.targets:
raise ValueError('Cannot merge attacks with different targets')
else:
return type(self)(self.targets, self.flips | other.flips)
def to_corruptions(self, pat=None):
return ((x.addr, x.to_corruption(pat)) for x in self)
def to_physmem(self, msys):
return type(self)(
targets=tuple(msys.resolve_reverse(x) for x in self.targets),
flips={x.to_physmem(msys) for x in self.flips}
)
def __iter__(self):
return iter(sorted(self.flips))
@classmethod
def __is_flip_far(cls, targets, victim):
close = False
for t in targets:
if abs(t.row - victim.row) == 1:
close = True
if not close:
print("Found flip far away from target row")
print(targets)
print(victim)
@classmethod
def decode_line(cls, line, msys=None):
def parse_addr(s):
res = ADDR_RE.match(s)
if res == None:
raise Exception("Couldn't parse")
return DRAMAddr(**({k:int(v) for (k,v) in res.groupdict().items() if v != None}))
def parse_addr_trg(s):
res = ADDR_TRG_RE.match(s)
if res == None:
raise Exception("Couldn't parse")
return DRAMAddr(**({k:int(v) for (k,v) in res.groupdict().items() if v != None}))
targ, vict, *o = line.split(':')
flips = set()
targets = [parse_addr(s) for s in line.split('/')]
for x in BFLIP_RE.finditer(vict):
dct = x.groupdict()
dct['addr'] = parse_addr(dct['addr'])
dct['got'] = int(dct['got'], 16)
dct['exp'] = int(dct['exp'], 16)
cls.__is_flip_far(targets, dct['addr'])
flips.update(Corruption(**(dct)).to_flips())
# vaddr = DRAMAddr(*(int(v, 16) for v in x.group(*range(1,4)) if v is not None))
# vcorr = [Corruption(*(int(v, 16) for v in y.groups())) for y in BFLIP_RE.finditer(x.group(0))]
# for corr in vcorr:
# flips.update(corr.to_flips(vaddr, msys))
return cls(
targets= targets,
flips=flips
)
def encode(self, patterns=None):
if patterns is None:
patterns = [[0xff], [0]]
corrs = [
[x for x in self.to_corruptions(pat) if x[1].exp != x[1].got]
for pat in patterns
]
tstr = ' '.join(str(x) for x in self.targets) + ' : '
return '\n'.join(
tstr +
' '.join(' '.join(str(x) for x in c) for c in corr)
for corr in corrs
)
def decode_lines(lineiter):
curatk = None
for line in lineiter:
atk = Attack.decode_line(line)
if curatk is None:
curatk = atk
else:
try:
curatk = curatk.merge(atk)
except ValueError:
yield curatk
curatk = atk
if curatk is not None:
yield curatk
class Parameters(namedtuple("Parameters", ['h_cfg', 'd_cfg', 'h_rows', 'h_rounds', 'd_base'])):
""""""
@classmethod
def parse_params(cls, string):
matches = RE_DICT.findall(string)
res = {}
for m in matches:
res[m[0]] = m[1]
return cls(*res.values())
class Fliptable:
def __init__(self, attacks):
self.attacks=attacks
def __eq__(self, other):
if isinstance(other, type(self)):
return self.attacks == other.attacks
else:
return NotImplemented
def __len__(self):
return len(self.attacks)
def __iter__(self):
return iter(self.attacks)
def __str__(self):
return '\n'.join(atk.encode() for atk in self)
def __add__(self, other):
return Fliptable(self.attacks + other.attacks)
def diff(self, other):
if not isinstance(other, type(self)):
raise ValueError('Fliptable required for diff')
uself = []
uother = []
common = []
satks = iter(self)
oatks = iter(other)
sa = next(satks, None)
oa = next(oatks, None)
while sa is not None or oa is not None:
# Degenerate cases
if sa is None:
uother.append(oa)
uother.extend(oatks)
break
if oa is None:
uself.append(sa)
uself.extend(satks)
break
try:
adiff = sa.diff(oa)
if adiff.self_only.flips:
uself.append(adiff.self_only)
common.append(adiff.common)
if adiff.other_only.flips:
uother.append(adiff.other_only)
sa = next(satks, None)
oa = next(oatks, None)
except ValueError:
if sa.targets < oa.targets:
uself.append(sa)
sa = next(satks, None)
elif sa.targets > oa.targets:
uother.append(oa)
oa = next(oatks, None)
FT = type(self)
return Diff(FT(uself), FT(common), FT(uother))
def to_physmem(self, msys):
return type(self)([x.to_physmem(msys) for x in self])
@classmethod
def load_file(cls, fname):
with open(fname, 'r') as f:
# params_line = f.readline().remove("#") # read the first line which contains the parameters of the flip table
# params = Parameters.parse_params(params_line)
# flips = pd.read_csv(f)
# attacks = attacks_generator()
return cls(list(decode_lines(f)))
def save_file(self, fname):
with open(fname, 'w') as f:
f.write(str(self))

137
py/hammertime/sim.py Normal file
View File

@ -0,0 +1,137 @@
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
# Copyright (c) 2016 Andrei Tatar
# Copyright (c) 2017-2018 Vrije Universiteit Amsterdam
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import math
import itertools
import functools
from collections import namedtuple
from hammertime import fliptable
from hammertime import dramtrans
import sys
scan_time = 60000 #ns to scan a row (terrible implementation)
fill_time = 1500 #ns to fill a row
class PageBitFlip(namedtuple('PageBitFlip', ['byte_offset', 'mask'])):
"""Represents a byte with one or more flipped bits at a particular offset within a page"""
class VictimPage(namedtuple('VictimPage', ['pfn', 'pullups', 'pulldowns'])):
"""Represents the results of one rowhammer attack on one particular physical page"""
class ExploitModel:
def check_page(self, vpage):
raise NotImplementedError()
def check_attack(self, attack):
for vpage in attack:
if self.check_page(vpage):
yield vpage.pfn
def check_attacks(self, attacks):
for atk in attacks:
yield tuple(self.check_attack(atk))
def _map_attack(atk, msys, pagesize=0x1000):
pfnof = lambda x: x.addr // pagesize
byteof = lambda x: x.addr % pagesize + x.bit // 8
vict_pages = []
for pfn, pflips in itertools.groupby(atk.to_physmem(msys), pfnof):
ups = set()
downs = set()
for byte, iflips in itertools.groupby(pflips, byteof):
bflips = list(iflips)
pup = functools.reduce(lambda x,y: x | y, (1 << (x.bit % 8) for x in bflips if x.pullup), 0)
pdn = functools.reduce(lambda x,y: x | y, (1 << (x.bit % 8) for x in bflips if not x.pullup), 0)
if pup:
ups.add(PageBitFlip(byte, pup))
if pdn:
downs.add(PageBitFlip(byte, pdn))
if ups or downs:
vict_pages.append(VictimPage(pfn, ups, downs))
return vict_pages
class BaseEstimator:
def __init__(self):
self.clear()
def iter_attacks(self):
"""
Return an iterator over possible attacks
An attack consists of a sequence of VictimPages that have bits flipped
as part of a single Rowhammer attack.
"""
raise NotImplementedError()
def run_exploit(self, model):
self.results = list(model.check_attacks(self.iter_attacks()))
def clear(self):
self.results = []
def print_stats(self):
if self.results:
succ = sum(1 for x in self.results if x)
npages = sum(len(x) for x in self.results if x)
prop = succ / len(self.results)
print('{} total attacks (over {} KiB), of which {} successful ({:5.1f} %)'.format(
len(self.results), len(self.results) * 8, succ, 100.0 * prop
))
print('{} exploitable pages found'.format(npages))
if prop != 0:
mna = 1 / prop
print('Minimum (contiguous) memory required: {} KiB'.format(math.ceil(mna) * 8))
print('Mean number of attacks until successful: {:.1f}'.format(mna))
print('Mean time to successful attack: {:.1f} seconds (assuming {:.1f}ms/attack)'.format(mna * self.atk_time * 10**-3, self.atk_time))
class FliptableEstimator(BaseEstimator):
def __init__(self, fliptbl, memsys, h_time):
self.fliptbl = fliptbl
self.msys = memsys
super().__init__()
self.atk_time = self.compute_atk_time(h_time)
def iter_attacks(self):
for atk in self.fliptbl:
yield _map_attack(atk, self.msys)
def compute_atk_time(self, h_time):
n_tgts = len(self.fliptbl.attacks[0].targets)
s_time = scan_time * n_tgts/2 * 3
f_time = fill_time * n_tgts
return (f_time + s_time + h_time) / 10**6 # results in ms
@classmethod
def main(cls, profile_file, msys_file, model, h_time):
"""Set up an estimator, run an exploit and print out statistics"""
ftbl = fliptable.Fliptable.load_file(profile_file)
msys = dramtrans.MemorySystem()
msys.load_file(msys_file)
est = cls(ftbl, msys, h_time)
est.run_exploit(model)
est.print_stats()

26
py/histogram.py Executable file
View File

@ -0,0 +1,26 @@
import sys
import csv
import matplotlib.pyplot as plt
num_bins = 200
if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python histogram.py inputFile")
exit(1)
csvFile = open(sys.argv[1], "r")
csvFile.readline() # skip first line
reader = csv.reader(csvFile, delimiter=',')
x = []
for row in reader:
x.append(int(row[len(row) - 1]))
print(f'Number of point {len(x)}')
fig, ax = plt.subplots()
n, bins, patches = ax.hist(x, num_bins, density=False)
ax.set_xlabel("Access time [ns]")
ax.set_ylabel("proportion of cases")
plt.show()