Initial cache attack lab commit
This commit is contained in:
		
						commit
						70fb4e150a
					
				| 
						 | 
				
			
			@ -0,0 +1,17 @@
 | 
			
		|||
include ../cpu.mk
 | 
			
		||||
 | 
			
		||||
all: main
 | 
			
		||||
 | 
			
		||||
run: main
 | 
			
		||||
	@taskset -c $(SENDER_CPU) ./main
 | 
			
		||||
 | 
			
		||||
run-reference: reference
 | 
			
		||||
	@taskset -c $(SENDER_CPU) ./reference
 | 
			
		||||
 | 
			
		||||
main: main.c Makefile
 | 
			
		||||
	@gcc main.c -o main
 | 
			
		||||
 | 
			
		||||
.PHONY: clean
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -f main
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,49 @@
 | 
			
		|||
import os
 | 
			
		||||
import json
 | 
			
		||||
import matplotlib.pyplot as plt
 | 
			
		||||
import numpy as np
 | 
			
		||||
from tqdm import tqdm
 | 
			
		||||
from datetime import datetime
 | 
			
		||||
 | 
			
		||||
num_runs = 100
 | 
			
		||||
dict_of_dict_of_lists = dict()
 | 
			
		||||
 | 
			
		||||
graph_repo="data"
 | 
			
		||||
os.makedirs(graph_repo, exist_ok=True)
 | 
			
		||||
 | 
			
		||||
fancy_num_runs = range(0, num_runs, 1)
 | 
			
		||||
for run_id in tqdm(fancy_num_runs):
 | 
			
		||||
    filename = graph_repo+"/run"+str(run_id)+".json"
 | 
			
		||||
    with open(filename) as f:
 | 
			
		||||
        dict_of_dict_of_lists[run_id] = json.load(f)
 | 
			
		||||
 | 
			
		||||
l1_all  = []
 | 
			
		||||
l2_all  = []
 | 
			
		||||
l3_all  = []
 | 
			
		||||
mem_all = []
 | 
			
		||||
 | 
			
		||||
for run_id in tqdm(fancy_num_runs):
 | 
			
		||||
    l1_all  += dict_of_dict_of_lists[run_id]['1'] 
 | 
			
		||||
    l2_all  += dict_of_dict_of_lists[run_id]['2'] 
 | 
			
		||||
    l3_all  += dict_of_dict_of_lists[run_id]['3'] 
 | 
			
		||||
    mem_all += dict_of_dict_of_lists[run_id]['4']
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# MAX 300
 | 
			
		||||
#
 | 
			
		||||
fig_all = plt.figure(figsize=(11.25, 7.5))
 | 
			
		||||
ax_all  = fig_all.add_subplot(1,1,1)
 | 
			
		||||
ax_all.set_xlabel("Access Time")
 | 
			
		||||
ax_all.set_ylabel("Number of Samples")
 | 
			
		||||
 | 
			
		||||
ax_all.hist(l1_all,  label="L1",   bins=np.arange(0, 300 ), alpha=0.5) 
 | 
			
		||||
ax_all.hist(l2_all,  label="L2",   bins=np.arange(0, 300 ), alpha=0.5) 
 | 
			
		||||
ax_all.hist(l3_all,  label="L3",   bins=np.arange(0, 300 ), alpha=0.5) 
 | 
			
		||||
ax_all.hist(mem_all, label="DRAM", bins=np.arange(0, 300 ), alpha=0.5) 
 | 
			
		||||
fig_all.legend()
 | 
			
		||||
 | 
			
		||||
os.makedirs("graphs", exist_ok=True)
 | 
			
		||||
now = datetime.now() 
 | 
			
		||||
date_time = now.strftime("%m:%d:%Y_%H:%M:%S")
 | 
			
		||||
fig_all.savefig(str("graphs/"+date_time+".pdf"))
 | 
			
		||||
plt.close(fig_all)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
#include "utility.h"
 | 
			
		||||
 | 
			
		||||
// TODO: Uncomment the following lines and fill in the correct size
 | 
			
		||||
//#define L1_SIZE [TODO]
 | 
			
		||||
//#define L2_SIZE [TODO]
 | 
			
		||||
//#define L3_SIZE [TODO]
 | 
			
		||||
 
 | 
			
		||||
int main (int ac, char **av) {
 | 
			
		||||
 | 
			
		||||
    // create 4 arrays to store the latency numbers
 | 
			
		||||
    // the arrays are initialized to 0
 | 
			
		||||
    uint64_t dram_latency[SAMPLES] = {0};
 | 
			
		||||
    uint64_t l1_latency[SAMPLES] = {0};
 | 
			
		||||
    uint64_t l2_latency[SAMPLES] = {0};
 | 
			
		||||
    uint64_t l3_latency[SAMPLES] = {0};
 | 
			
		||||
 | 
			
		||||
    // A temporary variable we can use to load addresses
 | 
			
		||||
    // The volatile keyword tells the compiler to not put this variable into a
 | 
			
		||||
    // register- it should always try to load from memory/ cache.
 | 
			
		||||
    volatile char tmp;
 | 
			
		||||
 | 
			
		||||
    // Allocate a buffer of 64 Bytes
 | 
			
		||||
    // the size of an unsigned integer (uint64_t) is 8 Bytes
 | 
			
		||||
    // Therefore, we request 8 * 8 Bytes
 | 
			
		||||
    uint64_t *target_buffer = (uint64_t *)malloc(8*sizeof(uint64_t));
 | 
			
		||||
 | 
			
		||||
    if (NULL == target_buffer) {
 | 
			
		||||
        perror("Unable to malloc");
 | 
			
		||||
        return EXIT_FAILURE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // [1.2] TODO: Uncomment the following line to allocate a buffer of a size
 | 
			
		||||
    // of your chosing. This will help you measure the latencies at L2 and L3.
 | 
			
		||||
    //uint64_t *eviction_buffer = (uint64_t)malloc(TODO);
 | 
			
		||||
 | 
			
		||||
    // Example: Measure L1 access latency, store results in l1_latency array
 | 
			
		||||
    for (int i=0; i<SAMPLES; i++){
 | 
			
		||||
        // Step 1: bring the target cache line into L1 by simply accessing the line
 | 
			
		||||
        tmp = target_buffer[0];
 | 
			
		||||
 | 
			
		||||
        // Step 2: measure the access latency
 | 
			
		||||
        l1_latency[i] = measure_one_block_access_time((uint64_t)target_buffer);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // ======
 | 
			
		||||
    // [1.2] TODO: Measure DRAM Latency, store results in dram_latency array
 | 
			
		||||
    // ======
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    // ======
 | 
			
		||||
    // [1.2] TODO: Measure L2 Latency, store results in l2_latency array
 | 
			
		||||
    // ======
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
    // ======
 | 
			
		||||
    // [1.2] TODO: Measure L3 Latency, store results in l3_latency array
 | 
			
		||||
    // ======
 | 
			
		||||
    //
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // Print the results to the screen
 | 
			
		||||
    // [1.5] Change print_results to print_results_for_python so that your code will work
 | 
			
		||||
    // with the python plotter software
 | 
			
		||||
    print_results(dram_latency, l1_latency, l2_latency, l3_latency);
 | 
			
		||||
 | 
			
		||||
    free(target_buffer);
 | 
			
		||||
 | 
			
		||||
    // [1.2] TODO: Uncomment this line once you uncomment the eviction_buffer creation line
 | 
			
		||||
    //free(eviction_buffer);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
import json
 | 
			
		||||
import os
 | 
			
		||||
import matplotlib.pyplot as plt
 | 
			
		||||
import subprocess
 | 
			
		||||
import csv
 | 
			
		||||
from tqdm import tqdm
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Run your code:
 | 
			
		||||
# Make sure that your code is using print_results_for_python
 | 
			
		||||
#executable_filename = ['make', 'run']
 | 
			
		||||
 | 
			
		||||
# Run reference code:
 | 
			
		||||
executable_filename = ['make', 'run-reference']
 | 
			
		||||
 | 
			
		||||
num_runs = 100
 | 
			
		||||
 | 
			
		||||
graph_repo = "data"
 | 
			
		||||
os.makedirs(graph_repo, exist_ok=True)
 | 
			
		||||
 | 
			
		||||
fancy_num_runs = range(0, num_runs, 1)
 | 
			
		||||
for run_id in tqdm(fancy_num_runs):
 | 
			
		||||
    p = subprocess.run(executable_filename,
 | 
			
		||||
            stdout=subprocess.PIPE, universal_newlines=True)
 | 
			
		||||
 | 
			
		||||
    reader = csv.reader(p.stdout.splitlines())
 | 
			
		||||
 | 
			
		||||
    dict_of_lists = dict()
 | 
			
		||||
 | 
			
		||||
    for store_lev in range(1, 4+1, 1):
 | 
			
		||||
        dict_of_lists[store_lev] = []
 | 
			
		||||
 | 
			
		||||
    store_level = 1
 | 
			
		||||
    for row in reader:
 | 
			
		||||
        temp = row[0].split(' ')[:-1]
 | 
			
		||||
        dict_of_lists[store_level] = list(map(int, temp)) 
 | 
			
		||||
        store_level+=1
 | 
			
		||||
 | 
			
		||||
    #print(dict_of_lists)
 | 
			
		||||
    filename = graph_repo+"/run"+str(run_id)+".json"
 | 
			
		||||
    jsonFile = open(filename, "w")
 | 
			
		||||
    jsonFile.write(json.dumps(dict_of_lists))
 | 
			
		||||
    jsonFile.close()
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,148 @@
 | 
			
		|||
#include <stdint.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <assert.h>
 | 
			
		||||
 | 
			
		||||
#ifndef __UTILITY_H__
 | 
			
		||||
#define __UTILITY_H__
 | 
			
		||||
 | 
			
		||||
#define SAMPLES 10
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Function to read the time stamp counter, which is called tsc for short
 | 
			
		||||
// "rdtscpp" returns a 32bit unsigned integer
 | 
			
		||||
// "rdtscpp64" return a 64 bit unsigned integer
 | 
			
		||||
// Details in https://www.felixcloutier.com/x86/rdtscpp
 | 
			
		||||
static inline uint32_t rdtscpp() {
 | 
			
		||||
    uint32_t rv;
 | 
			
		||||
    asm volatile ("rdtscpp": "=a" (rv) :: "edx", "ecx");
 | 
			
		||||
    return rv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint64_t rdtscpp64() {
 | 
			
		||||
    uint32_t low, high;
 | 
			
		||||
    asm volatile ("rdtscpp": "=a" (low), "=d" (high) :: "ecx");
 | 
			
		||||
    return (((uint64_t)high) << 32) | low;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Function "lfence" wrap the assembly instruction lfence
 | 
			
		||||
// This function performs a serializing operation which ensures that
 | 
			
		||||
// the instructions after "lfence" start execution after
 | 
			
		||||
// all the instructions before "lfence" complete
 | 
			
		||||
// Details in https://www.felixcloutier.com/x86/lfence
 | 
			
		||||
static inline void lfence() {
 | 
			
		||||
    asm volatile("lfence");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Here is an example of using "rdtscp" and "mfence" to
 | 
			
		||||
// measure the time it takes to access a block specified by its virtual address
 | 
			
		||||
// The corresponding pseudo code is
 | 
			
		||||
// =========
 | 
			
		||||
// t1 = rdtscp
 | 
			
		||||
// load addr
 | 
			
		||||
// t2 = rdtscp
 | 
			
		||||
// cycles = t2 - t1
 | 
			
		||||
// return cycles
 | 
			
		||||
// =========
 | 
			
		||||
static inline uint64_t measure_one_block_access_time(uint64_t addr)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t cycles;
 | 
			
		||||
 | 
			
		||||
    asm volatile("mov %1, %%r8\n\t"
 | 
			
		||||
    "mfence\n\t"
 | 
			
		||||
    "lfence\n\t"
 | 
			
		||||
    "rdtscp\n\t"
 | 
			
		||||
    "mov %%eax, %%edi\n\t"
 | 
			
		||||
    "mov (%%r8), %%r8\n\t"
 | 
			
		||||
    "rdtscp\n\t"
 | 
			
		||||
    "sub %%edi, %%eax\n\t"
 | 
			
		||||
    : "=a"(cycles) /*output*/
 | 
			
		||||
    : "r"(addr)    /*input*/
 | 
			
		||||
    : "r8", "edi"); /*reserved register*/
 | 
			
		||||
 | 
			
		||||
    return cycles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline uint64_t one_block_access(uint64_t addr)
 | 
			
		||||
{
 | 
			
		||||
    asm volatile("mov (%0), %%r8\n\t"
 | 
			
		||||
    : /*output*/
 | 
			
		||||
    : "r"(addr)    /*input*/
 | 
			
		||||
    : "r8");    /*reserved register*/
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// A wrapper function of the clflush instruction
 | 
			
		||||
// The instruction evict the given address from the cache to DRAM
 | 
			
		||||
// so that the next time the line is accessed, it will be fetched from DRAM
 | 
			
		||||
// Details in https://www.felixcloutier.com/x86/clflush
 | 
			
		||||
static inline void clflush(void *v) {
 | 
			
		||||
    asm volatile ("clflush 0(%0)": : "r" (v):);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Supporting functions for printing results in different formats
 | 
			
		||||
// Function "compare" is used in the priting functions and you do not need it
 | 
			
		||||
int compare(const void *p1, const void *p2) {
 | 
			
		||||
    uint64_t u1 = *(uint64_t *)p1;
 | 
			
		||||
    uint64_t u2 = *(uint64_t *)p2;
 | 
			
		||||
 | 
			
		||||
    return (int)u1 - (int)u2;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Print out the latencies you measured
 | 
			
		||||
void print_results(uint64_t* dram, uint64_t* l1, uint64_t* l2, uint64_t* l3) {
 | 
			
		||||
    qsort(dram, SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
    qsort(l1, SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
    qsort(l2, SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
    qsort(l3, SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
    printf("             :  L1   L2   L3   Mem   \n");
 | 
			
		||||
    printf("Minimum      : %5ld %5ld %5ld %5ld\n", l1[0], l2[0], l3[0], dram[0]);
 | 
			
		||||
 | 
			
		||||
    printf("Bottom decile: %5ld %5ld %5ld %5ld\n", l1[SAMPLES/10], l2[SAMPLES/10],
 | 
			
		||||
                                                l3[SAMPLES/10], dram[SAMPLES/10]);
 | 
			
		||||
 | 
			
		||||
    printf("Median       : %5ld %5ld %5ld %5ld\n", l1[SAMPLES/2], l2[SAMPLES/2],
 | 
			
		||||
                                                l3[SAMPLES/2], dram[SAMPLES/2]);
 | 
			
		||||
 | 
			
		||||
    printf("Top decile   : %5ld %5ld %5ld %5ld\n", l1[(SAMPLES * 9)/10], l2[(SAMPLES * 9)/10],
 | 
			
		||||
                                                   l3[(SAMPLES * 9)/10], dram[(SAMPLES * 9)/10]);
 | 
			
		||||
 | 
			
		||||
    printf("Maximum      : %5ld %5ld %5ld %5ld\n", l1[SAMPLES-1], l2[SAMPLES-1],
 | 
			
		||||
                                                    l3[SAMPLES-1], dram[SAMPLES-1]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Format the latencies for part 1.5
 | 
			
		||||
void print_results_for_python(uint64_t* dram, uint64_t* l1, uint64_t* l2, uint64_t* l3)
 | 
			
		||||
{
 | 
			
		||||
    qsort(dram, SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
    qsort(l1,   SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
    qsort(l2,   SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
    qsort(l3,   SAMPLES, sizeof(uint64_t), compare);
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; i < SAMPLES; i++) {
 | 
			
		||||
        printf("%ld ", l1[i]);
 | 
			
		||||
    }
 | 
			
		||||
    printf("\n");
 | 
			
		||||
    for (int i = 0; i < SAMPLES; i++) {
 | 
			
		||||
        printf("%ld ", l2[i]);
 | 
			
		||||
    }
 | 
			
		||||
    printf("\n");
 | 
			
		||||
    for (int i = 0; i < SAMPLES; i++) {
 | 
			
		||||
        printf("%ld ", l3[i]);
 | 
			
		||||
    }
 | 
			
		||||
    printf("\n");
 | 
			
		||||
    for (int i = 0; i < SAMPLES; i++) {
 | 
			
		||||
        printf("%ld ", dram[i]);
 | 
			
		||||
    }
 | 
			
		||||
    printf("\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#endif // _UTILITY_H__ 
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
include ../cpu.mk
 | 
			
		||||
 | 
			
		||||
TARGETS=receiver sender 
 | 
			
		||||
UTILS=util.o 
 | 
			
		||||
 | 
			
		||||
all: $(TARGETS)
 | 
			
		||||
 | 
			
		||||
$(UTILS): %.o: %.c %.h
 | 
			
		||||
	$(CC) $(CFLAGS) -c $<
 | 
			
		||||
 | 
			
		||||
%.o: %.c util.h
 | 
			
		||||
	$(CC) $(CFLAGS)  -c $< 
 | 
			
		||||
 | 
			
		||||
$(TARGETS): %:%.o util.o
 | 
			
		||||
	$(CC) $(CFLAGS) $^ -o $@
 | 
			
		||||
 | 
			
		||||
run_sender: sender 
 | 
			
		||||
	taskset -c $(SENDER_CPU) ./sender
 | 
			
		||||
 | 
			
		||||
run_receiver: receiver
 | 
			
		||||
	taskset -c $(RECEIVER_CPU) ./receiver
 | 
			
		||||
 | 
			
		||||
.PHONY:	clean
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	$(RM) *.o $(HELPERS) $(TARGETS) 
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,29 @@
 | 
			
		|||
 | 
			
		||||
#include"util.h"
 | 
			
		||||
// mman library to be used for hugepage allocations (e.g. mmap or posix_memalign only)
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	// Put your covert channel setup code here
 | 
			
		||||
 | 
			
		||||
	printf("Please press enter.\n");
 | 
			
		||||
 | 
			
		||||
	char text_buf[2];
 | 
			
		||||
	fgets(text_buf, sizeof(text_buf), stdin);
 | 
			
		||||
 | 
			
		||||
	printf("Receiver now listening.\n");
 | 
			
		||||
 | 
			
		||||
	bool listening = true;
 | 
			
		||||
	while (listening) {
 | 
			
		||||
 | 
			
		||||
		// Put your covert channel code here
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("Receiver finished.\n");
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,45 @@
 | 
			
		|||
 | 
			
		||||
#include"util.h"
 | 
			
		||||
// mman library to be used for hugepage allocations (e.g. mmap or posix_memalign only)
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
// TODO: define your own buffer size
 | 
			
		||||
#define BUFF_SIZE (1<<21)
 | 
			
		||||
//#define BUFF_SIZE [TODO]
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
  // Allocate a buffer using huge page
 | 
			
		||||
  // See the handout for details about hugepage management
 | 
			
		||||
  void *buf= mmap(NULL, BUFF_SIZE, PROT_READ | PROT_WRITE, MAP_POPULATE | MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0);
 | 
			
		||||
  
 | 
			
		||||
  if (buf == (void*) - 1) {
 | 
			
		||||
     perror("mmap() error\n");
 | 
			
		||||
     exit(EXIT_FAILURE);
 | 
			
		||||
  }
 | 
			
		||||
  // The first access to a page triggers overhead associated with
 | 
			
		||||
  // page allocation, TLB insertion, etc.
 | 
			
		||||
  // Thus, we use a dummy write here to trigger page allocation
 | 
			
		||||
  // so later access will not suffer from such overhead.
 | 
			
		||||
  //*((char *)buf) = 1; // dummy write to trigger page allocation
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
  // TODO:
 | 
			
		||||
  // Put your covert channel setup code here
 | 
			
		||||
 | 
			
		||||
  printf("Please type a message.\n");
 | 
			
		||||
 | 
			
		||||
  bool sending = true;
 | 
			
		||||
  while (sending) {
 | 
			
		||||
      char text_buf[128];
 | 
			
		||||
      fgets(text_buf, sizeof(text_buf), stdin);
 | 
			
		||||
 | 
			
		||||
      // TODO:
 | 
			
		||||
      // Put your covert channel code here
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  printf("Sender finished.\n");
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,102 @@
 | 
			
		|||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
/* Measure the time it takes to access a block with virtual address addr. */
 | 
			
		||||
CYCLES measure_one_block_access_time(ADDR_PTR addr)
 | 
			
		||||
{
 | 
			
		||||
	CYCLES cycles;
 | 
			
		||||
 | 
			
		||||
	asm volatile("mov %1, %%r8\n\t"
 | 
			
		||||
	"lfence\n\t"
 | 
			
		||||
	"rdtsc\n\t"
 | 
			
		||||
	"mov %%eax, %%edi\n\t"
 | 
			
		||||
	"mov (%%r8), %%r8\n\t"
 | 
			
		||||
	"lfence\n\t"
 | 
			
		||||
	"rdtsc\n\t"
 | 
			
		||||
	"sub %%edi, %%eax\n\t"
 | 
			
		||||
	: "=a"(cycles) /*output*/
 | 
			
		||||
	: "r"(addr)
 | 
			
		||||
	: "r8", "edi");	
 | 
			
		||||
 | 
			
		||||
	return cycles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * CLFlushes the given address.
 | 
			
		||||
 * 
 | 
			
		||||
 * Note: clflush is provided to help you debug and should not be used in your
 | 
			
		||||
 * final submission
 | 
			
		||||
 */
 | 
			
		||||
void clflush(ADDR_PTR addr)
 | 
			
		||||
{
 | 
			
		||||
    asm volatile ("clflush (%0)"::"r"(addr));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Converts a string to its binary representation.
 | 
			
		||||
 */
 | 
			
		||||
char *string_to_binary(char *s)
 | 
			
		||||
{
 | 
			
		||||
  if (s == NULL)
 | 
			
		||||
    return 0; /* no input string */
 | 
			
		||||
 | 
			
		||||
  size_t len = strlen(s);
 | 
			
		||||
 | 
			
		||||
  // Each char is one byte (8 bits) and + 1 at the end for null terminator
 | 
			
		||||
  char *binary = malloc(len * 8 + 1);
 | 
			
		||||
  binary[len] = '\0';
 | 
			
		||||
 | 
			
		||||
  for (size_t i = 0; i < len; ++i)
 | 
			
		||||
  {
 | 
			
		||||
    char ch = s[i];
 | 
			
		||||
    for (int j = 7; j >= 0; --j)
 | 
			
		||||
    {
 | 
			
		||||
      if (ch & (1 << j))
 | 
			
		||||
      {
 | 
			
		||||
        strcat(binary, "1");
 | 
			
		||||
      }
 | 
			
		||||
      else
 | 
			
		||||
      {
 | 
			
		||||
        strcat(binary, "0");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return binary;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Converts a binary string to its ASCII representation.
 | 
			
		||||
 */
 | 
			
		||||
char *binary_to_string(char *data)
 | 
			
		||||
{
 | 
			
		||||
  // Each char is 8 bits
 | 
			
		||||
  size_t msg_len = strlen(data) / 8;
 | 
			
		||||
 | 
			
		||||
  // Add one for null terminator at the end
 | 
			
		||||
  char *msg = malloc(msg_len + 1);
 | 
			
		||||
  msg[msg_len] = '\0';
 | 
			
		||||
 | 
			
		||||
  for (int i = 0; i < msg_len; i++)
 | 
			
		||||
  {
 | 
			
		||||
    char tmp[8];
 | 
			
		||||
    int k = 0;
 | 
			
		||||
 | 
			
		||||
    for (int j = i * 8; j < ((i + 1) * 8); j++)
 | 
			
		||||
    {
 | 
			
		||||
      tmp[k++] = data[j];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    msg[i] = strtol(tmp, 0, 2);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return msg;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Converts a string to integer
 | 
			
		||||
 */
 | 
			
		||||
int string_to_int(char* s) 
 | 
			
		||||
{
 | 
			
		||||
  return atoi(s);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
 | 
			
		||||
// You may only use fgets() to pull input from stdin
 | 
			
		||||
// You may use any print function to stdout to print 
 | 
			
		||||
// out chat messages
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
// You may use memory allocators and helper functions 
 | 
			
		||||
// (e.g., rand()).  You may not use system().
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
 | 
			
		||||
#ifndef UTIL_H_
 | 
			
		||||
#define UTIL_H_
 | 
			
		||||
 | 
			
		||||
#define ADDR_PTR uint64_t 
 | 
			
		||||
#define CYCLES uint32_t
 | 
			
		||||
 | 
			
		||||
CYCLES measure_one_block_access_time(ADDR_PTR addr);
 | 
			
		||||
 | 
			
		||||
// You Should Not Use clflush in your final submission
 | 
			
		||||
// It is only used for debug
 | 
			
		||||
void clflush(ADDR_PTR addr);
 | 
			
		||||
 | 
			
		||||
char *string_to_binary(char *s);
 | 
			
		||||
char *binary_to_string(char *data);
 | 
			
		||||
 | 
			
		||||
int string_to_int(char* s);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
include ../cpu.mk
 | 
			
		||||
 | 
			
		||||
TARGETS=attacker
 | 
			
		||||
UTILS=util.o 
 | 
			
		||||
 | 
			
		||||
all: $(TARGETS)
 | 
			
		||||
 | 
			
		||||
$(UTILS): %.o: %.c %.h
 | 
			
		||||
	$(CC) $(CFLAGS) -c $<
 | 
			
		||||
 | 
			
		||||
%.o: %.c util.h
 | 
			
		||||
	$(CC) $(CFLAGS)  -c $< 
 | 
			
		||||
 | 
			
		||||
$(TARGETS): %:%.o util.o
 | 
			
		||||
	$(CC) $(CFLAGS) $^ -o $@
 | 
			
		||||
 | 
			
		||||
run_victim-2:
 | 
			
		||||
	taskset -c $(SENDER_CPU) ./victim-2
 | 
			
		||||
 | 
			
		||||
run_victim-3:
 | 
			
		||||
	taskset -c $(SENDER_CPU) ./victim-3
 | 
			
		||||
 | 
			
		||||
run_victim-4:
 | 
			
		||||
	taskset -c $(SENDER_CPU) ./victim-4
 | 
			
		||||
 | 
			
		||||
run_attacker: attacker
 | 
			
		||||
	taskset -c $(RECEIVER_CPU) ./attacker
 | 
			
		||||
 | 
			
		||||
.PHONY:	clean
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	$(RM) *.o $(HELPERS) $(TARGETS) 
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
#include "util.h"
 | 
			
		||||
// mman library to be used for hugepage allocations (e.g. mmap or posix_memalign only)
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
 | 
			
		||||
int main(int argc, char const *argv[]) {
 | 
			
		||||
    int flag = -1;
 | 
			
		||||
 | 
			
		||||
    // Put your capture-the-flag code here
 | 
			
		||||
 | 
			
		||||
    printf("Flag: %d\n", flag);
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,33 @@
 | 
			
		|||
 | 
			
		||||
#include "util.h"
 | 
			
		||||
 | 
			
		||||
/* Measure the time it takes to access a block with virtual address addr. */
 | 
			
		||||
CYCLES measure_one_block_access_time(ADDR_PTR addr)
 | 
			
		||||
{
 | 
			
		||||
	CYCLES cycles;
 | 
			
		||||
 | 
			
		||||
	asm volatile("mov %1, %%r8\n\t"
 | 
			
		||||
	"lfence\n\t"
 | 
			
		||||
	"rdtsc\n\t"
 | 
			
		||||
	"mov %%eax, %%edi\n\t"
 | 
			
		||||
	"mov (%%r8), %%r8\n\t"
 | 
			
		||||
	"lfence\n\t"
 | 
			
		||||
	"rdtsc\n\t"
 | 
			
		||||
	"sub %%edi, %%eax\n\t"
 | 
			
		||||
	: "=a"(cycles) /*output*/
 | 
			
		||||
	: "r"(addr)
 | 
			
		||||
	: "r8", "edi");	
 | 
			
		||||
 | 
			
		||||
	return cycles;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * CLFlushes the given address.
 | 
			
		||||
 * 
 | 
			
		||||
 * Note: clflush is provided to help you debug and should not be used in your
 | 
			
		||||
 * final submission
 | 
			
		||||
 */
 | 
			
		||||
void clflush(ADDR_PTR addr)
 | 
			
		||||
{
 | 
			
		||||
    asm volatile ("clflush (%0)"::"r"(addr));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
 | 
			
		||||
// You may only use fgets() to pull input from stdin
 | 
			
		||||
// You may use any print function to stdout to print 
 | 
			
		||||
// out chat messages
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
 | 
			
		||||
// You may use memory allocators and helper functions 
 | 
			
		||||
// (e.g., rand()).  You may not use system().
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
 | 
			
		||||
#include <inttypes.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
#ifndef UTIL_H_
 | 
			
		||||
#define UTIL_H_
 | 
			
		||||
 | 
			
		||||
#define ADDR_PTR uint64_t 
 | 
			
		||||
#define CYCLES uint32_t
 | 
			
		||||
 | 
			
		||||
CYCLES measure_one_block_access_time(ADDR_PTR addr);
 | 
			
		||||
 | 
			
		||||
void clflush(ADDR_PTR addr);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
# Hands-On Hardware Side Channels Lab
 | 
			
		||||
 | 
			
		||||
This repository contains all the starting code you will need for the lab. 
 | 
			
		||||
 | 
			
		||||
A randomly-generated password will be emailed to you when the lab is released. Please log in and change your password immediately to something secure using the `passwd` command. Please note that your account will be deleted at the end of this course.
 | 
			
		||||
 | 
			
		||||
## Starting the Lab
 | 
			
		||||
 | 
			
		||||
As stated on the lab handout, you must first change the `SENDER_CPU` and `RECEIVER_CPU` variables in the Makefile to your assigned CPUs. These will be emailed to you along with your lab machine password when the lab is released. **Double check that you have set these values correctly.**
 | 
			
		||||
 | 
			
		||||
After completing these steps, you are now ready to start the lab. Good luck!
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
CC=gcc
 | 
			
		||||
CFLAGS=-O0 -I /usr/local
 | 
			
		||||
$(error Please set SENDER_CPU and RECEIVER_CPU to your assigned values)
 | 
			
		||||
SENDER_CPU=
 | 
			
		||||
RECEIVER_CPU=
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
# Updates repository to latest starter code
 | 
			
		||||
#
 | 
			
		||||
# Adapted from Oliver Beckstein's ASU-CompMethodsPhysics-PHY494 course 2016-2020 placed into the public domain
 | 
			
		||||
 | 
			
		||||
# With GitHub template repositories one needs to use --allow-unrelated-histories
 | 
			
		||||
# at least once. https://help.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template
 | 
			
		||||
 | 
			
		||||
progname="$0"
 | 
			
		||||
REMOTE_NAME="startercode"
 | 
			
		||||
REMOTE_URL="https://github.com/CSAIL-Arch-Sec/SHD-CacheAttackLab.git"
 | 
			
		||||
 | 
			
		||||
# progname, from top dir
 | 
			
		||||
UPDATESH="./deploy/$(basename $progname)"
 | 
			
		||||
 | 
			
		||||
CONTACT_MESSAGE="Contact the instructor and TA with a screen shot of ALL output from running $0."
 | 
			
		||||
 | 
			
		||||
function die () {
 | 
			
		||||
    local msg="$1" err=${2:-1}
 | 
			
		||||
    echo "ERROR: ${msg}."
 | 
			
		||||
    exit $err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# ensure everything relative to top dir
 | 
			
		||||
topdir="$(git rev-parse --show-toplevel)" || die "Failed to get rootdir"
 | 
			
		||||
cd "${topdir}" || die "Failed to get to the git root dir ${rootdir}"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# first time
 | 
			
		||||
# 1. set remote repo
 | 
			
		||||
# 2. merge histories between student (template) and remote skeleton
 | 
			
		||||
 | 
			
		||||
if ! git remote get-url ${REMOTE_NAME} >/dev/null 2>&1; then
 | 
			
		||||
    echo "Adding remote repository '${REMOTE_NAME}'."
 | 
			
		||||
    git remote add ${REMOTE_NAME} ${REMOTE_URL}
 | 
			
		||||
 | 
			
		||||
    echo "Merging histories for the first time..."
 | 
			
		||||
    set -x
 | 
			
		||||
    git pull --allow-unrelated-histories -s recursive -X theirs --no-edit  ${REMOTE_NAME} main || \
 | 
			
		||||
	{ git rev-list -1 MERGE_HEAD >/dev/null 2>&1 && git merge --abort ; \
 | 
			
		||||
	  git remote rm ${REMOTE_NAME}; \
 | 
			
		||||
	  die "Failed to merge histories. ${CONTACT_MESSAGE}" $?; }
 | 
			
		||||
 | 
			
		||||
    set +x
 | 
			
		||||
fi    
 | 
			
		||||
 | 
			
		||||
echo "updating repository... git pull from ${REMOTE_NAME}"
 | 
			
		||||
git pull --no-edit ${REMOTE_NAME} main || die "Failed to pull from ${REMOTE_NAME}. ${CONTACT_MESSAGE}"
 | 
			
		||||
		Loading…
	
		Reference in New Issue