commit 82af0dce4af4b9a9210d06d0d12a47f50e2bd007 Author: emanuele Date: Tue Mar 10 16:19:50 2020 +0100 initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..8847e34 --- /dev/null +++ b/README.md @@ -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 diff --git a/drama/Makefile b/drama/Makefile new file mode 100644 index 0000000..7784df8 --- /dev/null +++ b/drama/Makefile @@ -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) diff --git a/drama/README.md b/drama/README.md new file mode 100644 index 0000000..4103e38 --- /dev/null +++ b/drama/README.md @@ -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. diff --git a/drama/hugepage.sh b/drama/hugepage.sh new file mode 100755 index 0000000..be6306e --- /dev/null +++ b/drama/hugepage.sh @@ -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 diff --git a/drama/src/include/rev-mc.h b/drama/src/include/rev-mc.h new file mode 100644 index 0000000..66aae3f --- /dev/null +++ b/drama/src/include/rev-mc.h @@ -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); diff --git a/drama/src/include/utils.h b/drama/src/include/utils.h new file mode 100644 index 0000000..34c46e2 --- /dev/null +++ b/drama/src/include/utils.h @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + + +#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); + diff --git a/drama/src/main.c b/drama/src/main.c new file mode 100644 index 0000000..f24f0e0 --- /dev/null +++ b/drama/src/main.c @@ -0,0 +1,134 @@ + +#include "stdio.h" +// #include /* for rdtsc, rdtscp, clflush */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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. */ diff --git a/drama/src/rev-mc.c b/drama/src/rev-mc.c new file mode 100644 index 0000000..3c52ace --- /dev/null +++ b/drama/src/rev-mc.c @@ -0,0 +1,501 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include + +#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 set_t; + +//------------------------------------------- +bool is_in(char* val, std::vector arr); +bool found_enough(std::vector sets, uint64_t set_cnt, size_t set_size); +void filter_sets(std::vector& sets, size_t set_size); +void print_sets(std::vector sets); +void verify_sets(std::vector& 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 reduce_masks(std::vector 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> mtx(height, std::vector(width)); + std::vector> mtx_t(height_t, std::vector(width_t)); + std::vector filtered_masks; + + for (size_t i =0; i> (__builtin_ctzl(v) + 1)); +} + + + +//---------------------------------------------------------- +std::vector find_functions(std::vector sets, size_t max_fn_bits, size_t msb, uint64_t flags) { + + std::vector 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 find_set_bits(uint64_t val) { + std::vector set_bits; + for (int i = 0; i<64; i++) { + if (!(val & (1ULL << i))) + continue; + + set_bits.push_back(i); + } + return set_bits; +} + +//---------------------------------------------------------- +std::vector get_dram_fn(uint64_t addr, std::vector fn_masks) { + std::vector 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& sets, std::vector 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 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 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 sets; + std::vector used_addr; + std::vector 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 arr) { + for (auto v: arr) { + if (val == v) { + return true; + } + } + return false; +} + +//---------------------------------------------------------- +bool found_enough(std::vector 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& 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 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& 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 + diff --git a/drama/src/utils.c b/drama/src/utils.c new file mode 100644 index 0000000..1e39a44 --- /dev/null +++ b/drama/src/utils.c @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/hammersuite/.gitignore b/hammersuite/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/hammersuite/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/hammersuite/Makefile b/hammersuite/Makefile new file mode 100644 index 0000000..836f9b6 --- /dev/null +++ b/hammersuite/Makefile @@ -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) diff --git a/hammersuite/README.md b/hammersuite/README.md new file mode 100644 index 0000000..f7ee96a --- /dev/null +++ b/hammersuite/README.md @@ -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 Developer’s 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. \ No newline at end of file diff --git a/hammersuite/hugepage.sh b/hammersuite/hugepage.sh new file mode 100755 index 0000000..be6306e --- /dev/null +++ b/hammersuite/hugepage.sh @@ -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 diff --git a/hammersuite/performance.sh b/hammersuite/performance.sh new file mode 100755 index 0000000..3cf40f3 --- /dev/null +++ b/hammersuite/performance.sh @@ -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 + + + diff --git a/hammersuite/src/addr-mapper.c b/hammersuite/src/addr-mapper.c new file mode 100644 index 0000000..95ecaff --- /dev/null +++ b/hammersuite/src/addr-mapper.c @@ -0,0 +1,78 @@ +#include "addr-mapper.h" +#include "utils.h" + +#include +#include +#include + +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); +} diff --git a/hammersuite/src/allocator.c b/hammersuite/src/allocator.c new file mode 100644 index 0000000..1876ada --- /dev/null +++ b/hammersuite/src/allocator.c @@ -0,0 +1,74 @@ +#include "allocator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} diff --git a/hammersuite/src/dram-address.c b/hammersuite/src/dram-address.c new file mode 100644 index 0000000..3927646 --- /dev/null +++ b/hammersuite/src/dram-address.c @@ -0,0 +1,131 @@ +#include "dram-address.h" + +#include +#include + +#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; +} diff --git a/hammersuite/src/hammer-suite.c b/hammersuite/src/hammer-suite.c new file mode 100644 index 0000000..502864f --- /dev/null +++ b/hammersuite/src/hammer-suite.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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<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<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; idxd_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; idxcfg->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); +} diff --git a/hammersuite/src/include/addr-mapper.h b/hammersuite/src/include/addr-mapper.h new file mode 100644 index 0000000..85a1f37 --- /dev/null +++ b/hammersuite/src/include/addr-mapper.h @@ -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); diff --git a/hammersuite/src/include/allocator.h b/hammersuite/src/include/allocator.h new file mode 100644 index 0000000..506efd1 --- /dev/null +++ b/hammersuite/src/include/allocator.h @@ -0,0 +1,6 @@ +#pragma once + +#include "types.h" + +int alloc_buffer(MemoryBuffer * mem); +int free_buffer(MemoryBuffer * mem); diff --git a/hammersuite/src/include/dram-address.h b/hammersuite/src/include/dram-address.h new file mode 100644 index 0000000..e7710de --- /dev/null +++ b/hammersuite/src/include/dram-address.h @@ -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 + 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); diff --git a/hammersuite/src/include/hammer-suite.h b/hammersuite/src/include/hammer-suite.h new file mode 100644 index 0000000..6655326 --- /dev/null +++ b/hammersuite/src/include/hammer-suite.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +#include "types.h" + +void hammer_session(SessionConfig * cfg, MemoryBuffer * memory); +void fuzzing_session(SessionConfig * cfg, MemoryBuffer * memory); diff --git a/hammersuite/src/include/memory.h b/hammersuite/src/include/memory.h new file mode 100644 index 0000000..9cade6e --- /dev/null +++ b/hammersuite/src/include/memory.h @@ -0,0 +1,8 @@ +#pragma once + +#include "types.h" +#include + +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); diff --git a/hammersuite/src/include/params.h b/hammersuite/src/include/params.h new file mode 100644 index 0000000..9c89d10 --- /dev/null +++ b/hammersuite/src/include/params.h @@ -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 +#include + +#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 */ diff --git a/hammersuite/src/include/types.h b/hammersuite/src/include/types.h new file mode 100644 index 0000000..b0320d2 --- /dev/null +++ b/hammersuite/src/include/types.h @@ -0,0 +1,54 @@ +#pragma once + +#include +#include +#include + +#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; diff --git a/hammersuite/src/include/utils.h b/hammersuite/src/include/utils.h new file mode 100644 index 0000000..ceb80b3 --- /dev/null +++ b/hammersuite/src/include/utils.h @@ -0,0 +1,128 @@ +#pragma once + +#include +#include +#include + +#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); diff --git a/hammersuite/src/main.c b/hammersuite/src/main.c new file mode 100644 index 0000000..0ce28a3 --- /dev/null +++ b/hammersuite/src/main.c @@ -0,0 +1,111 @@ +#include "stdio.h" +// #include /* for rdtsc, rdtscp, clflush */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/hammersuite/src/memory.c b/hammersuite/src/memory.c new file mode 100644 index 0000000..8935d27 --- /dev/null +++ b/hammersuite/src/memory.c @@ -0,0 +1,108 @@ +#include "memory.h" +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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)))); +} diff --git a/hammersuite/src/params.c b/hammersuite/src/params.c new file mode 100644 index 0000000..ec78da8 --- /dev/null +++ b/hammersuite/src/params.c @@ -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 . + * + */ + +#include "include/params.h" +#include "include/utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/hammersuite/src/utils.c b/hammersuite/src/utils.c new file mode 100644 index 0000000..4213299 --- /dev/null +++ b/hammersuite/src/utils.c @@ -0,0 +1,81 @@ +#include "utils.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PAGE_BITS 12 + +#define FLAGS (MAP_PRIVATE | MAP_POPULATE | MAP_HUGETLB | (30<> 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; +} diff --git a/py/hammerstats.py b/py/hammerstats.py new file mode 100755 index 0000000..f3149e9 --- /dev/null +++ b/py/hammerstats.py @@ -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...') diff --git a/py/hammertime/__init__.py b/py/hammertime/__init__.py new file mode 100644 index 0000000..3eb2379 --- /dev/null +++ b/py/hammertime/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python3 +# -*- coding: UTF-8 -*- +__all__ = ['sim', 'fliptable', 'dramtrans'] diff --git a/py/hammertime/_native.c b/py/hammertime/_native.c new file mode 100644 index 0000000..9002808 --- /dev/null +++ b/py/hammertime/_native.c @@ -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); +} diff --git a/py/hammertime/_native.so b/py/hammertime/_native.so new file mode 100755 index 0000000..5de7b1e Binary files /dev/null and b/py/hammertime/_native.so differ diff --git a/py/hammertime/dramtrans.py b/py/hammertime/dramtrans.py new file mode 100644 index 0000000..af0eec2 --- /dev/null +++ b/py/hammertime/dramtrans.py @@ -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<> _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<. + +"""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[-\d\w]+)\s*:\s*(?P[-.\d\w]+)") +#ADDR_RE = re.compile("r(?P\d+)\.bk(?P\d+)(\.col(?P\d+))?(\.p(?P\d+))?") +ADDR_RE = re.compile("r(?P\d+)\.bk(?P\d+)(\.col(?P\d+))?") +#ADDR_FMT = r'\((\w+)\s+(\w+)\s*(\w+)?\)' +#ADDR_RE = re.compile(ADDR_FMT) +# 87,c7,r16447.bk18.col3994 +BFLIP_FMT = r'(?P\w{2}),(?P\w{2}),(?P[.\w\d]+)' +BFLIP_RE = re.compile(BFLIP_FMT) +ADDR_TRG_RE = re.compile("b(?P[\d\w]+)\.r(?P[\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)) diff --git a/py/hammertime/sim.py b/py/hammertime/sim.py new file mode 100644 index 0000000..9ef9317 --- /dev/null +++ b/py/hammertime/sim.py @@ -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 . + +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() diff --git a/py/histogram.py b/py/histogram.py new file mode 100755 index 0000000..49b715b --- /dev/null +++ b/py/histogram.py @@ -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()