surshar / claude-code
Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.
Built by Anthropic · github.com/anthropics
A standalone C program that demonstrates the Rowhammer DRAM disturbance effect on the local machine's own memory. It hammers pairs of addresses with CLFLUSH/MFENCE to force DRAM row activations, then scans an all-1s buffer for induced bit flips. Includes build/run instructions and notes on x86-only flush, probabilistic results, and ARM porting. https://claude.ai/code/session_01WsBb1UNSn1Ta9H6aBYVYGe
| @@ -1,0 +1,134 @@ | ||
| 1 | +// rowhammer.c — a minimal, educational Rowhammer self-test. | |
| 2 | +// | |
| 3 | +// What it does | |
| 4 | +// ------------ | |
| 5 | +// Rowhammer is a DRAM disturbance effect: repeatedly activating ("hammering") | |
| 6 | +// the same DRAM rows can flip bits in physically adjacent rows. This program | |
| 7 | +// demonstrates the effect on *your own* machine's memory so you can learn how | |
| 8 | +// it works and check whether your RAM is susceptible. It does not attack any | |
| 9 | +// remote target — it only reads/writes a buffer this process allocated itself. | |
| 10 | +// | |
| 11 | +// How it works | |
| 12 | +// ------------ | |
| 13 | +// 1. Allocate a large buffer and fill it with a known pattern (all 1s). | |
| 14 | +// 2. Repeatedly read from two addresses that are likely to live in different | |
| 15 | +// rows of the same DRAM bank. Each read is followed by clflush so the CPU | |
| 16 | +// cache is bypassed and the access actually reaches DRAM, forcing row | |
| 17 | +// activations. | |
| 18 | +// 3. After many millions of hammer iterations, scan the buffer for any bit | |
| 19 | +// that is no longer 1. Any such bit is a Rowhammer-induced flip. | |
| 20 | +// | |
| 21 | +// This is a probabilistic test: vulnerable hardware may need many runs and | |
| 22 | +// different address pairs before a flip appears; robust hardware (e.g. with | |
| 23 | +// ECC or target-row-refresh mitigations) may never flip. | |
| 24 | +// | |
| 25 | +// Build (x86-64, Linux/macOS with clflush support): | |
| 26 | +// cc -O2 -o rowhammer rowhammer.c | |
| 27 | +// | |
| 28 | +// Run: | |
| 29 | +// ./rowhammer # default: 256 MiB, 2,000,000 hammers per pair | |
| 30 | +// ./rowhammer 512 4000000 | |
| 31 | +// | |
| 32 | +// Notes | |
| 33 | +// ----- | |
| 34 | +// * Uses the x86 CLFLUSH instruction; on ARM you would need a cache-clean | |
| 35 | +// equivalent (e.g. DC CIVAC) instead. | |
| 36 | +// * For a serious test you want physically-contiguous, address-mapping-aware | |
| 37 | +// pairs (huge pages, knowledge of the DRAM bank/row geometry). This sample | |
| 38 | +// keeps things simple and portable at the cost of hit rate. | |
| 39 | + | |
| 40 | +#include <stdint.h> | |
| 41 | +#include <stdio.h> | |
| 42 | +#include <stdlib.h> | |
| 43 | +#include <string.h> | |
| 44 | +#include <time.h> | |
| 45 | + | |
| 46 | +#if defined(__x86_64__) || defined(__i386__) | |
| 47 | +#include <x86intrin.h> | |
| 48 | +static inline void flush(volatile void *p) { _mm_clflush((const void *)p); } | |
| 49 | +static inline void fence(void) { _mm_mfence(); } | |
| 50 | +#else | |
| 51 | +#error "This sample uses x86 CLFLUSH; port flush()/fence() for your ISA." | |
| 52 | +#endif | |
| 53 | + | |
| 54 | +// Hammer two addresses: read each, flush each from cache, repeat. The flushes | |
| 55 | +// guarantee the next read misses cache and reactivates the DRAM row. | |
| 56 | +static void hammer(volatile uint64_t *a, volatile uint64_t *b, uint64_t iters) { | |
| 57 | + for (uint64_t i = 0; i < iters; i++) { | |
| 58 | + (void)*a; | |
| 59 | + (void)*b; | |
| 60 | + flush((void *)a); | |
| 61 | + flush((void *)b); | |
| 62 | + fence(); | |
| 63 | + } | |
| 64 | +} | |
| 65 | + | |
| 66 | +// Scan the buffer for any bit that is not set; report flips. | |
| 67 | +static uint64_t count_flips(const uint8_t *buf, size_t bytes) { | |
| 68 | + uint64_t flips = 0; | |
| 69 | + for (size_t i = 0; i < bytes; i++) { | |
| 70 | + uint8_t v = buf[i]; | |
| 71 | + if (v != 0xFF) { | |
| 72 | + for (int bit = 0; bit < 8; bit++) { | |
| 73 | + if (!((v >> bit) & 1)) { | |
| 74 | + flips++; | |
| 75 | + fprintf(stderr, " bit flip at offset %zu, bit %d\n", i, bit); | |
| 76 | + } | |
| 77 | + } | |
| 78 | + } | |
| 79 | + } | |
| 80 | + return flips; | |
| 81 | +} | |
| 82 | + | |
| 83 | +int main(int argc, char **argv) { | |
| 84 | + size_t mib = (argc > 1) ? (size_t)strtoull(argv[1], NULL, 10) : 256; | |
| 85 | + uint64_t iters = (argc > 2) ? strtoull(argv[2], NULL, 10) : 2000000ULL; | |
| 86 | + | |
| 87 | + size_t bytes = mib * 1024 * 1024; | |
| 88 | + uint8_t *buf = aligned_alloc(4096, bytes); | |
| 89 | + if (!buf) { | |
| 90 | + perror("aligned_alloc"); | |
| 91 | + return 1; | |
| 92 | + } | |
| 93 | + | |
| 94 | + printf("Allocated %zu MiB at %p\n", mib, (void *)buf); | |
| 95 | + printf("Filling with all-1s pattern...\n"); | |
| 96 | + memset(buf, 0xFF, bytes); | |
| 97 | + | |
| 98 | + // Choose hammer pairs that straddle the buffer. A real test would derive | |
| 99 | + // these from the DRAM row geometry; here we step through with a stride | |
| 100 | + // large enough to likely cross row boundaries. | |
| 101 | + const size_t stride = 256 * 1024; // 256 KiB apart | |
| 102 | + size_t pairs = 0, total_flips = 0; | |
| 103 | + | |
| 104 | + struct timespec t0; | |
| 105 | + clock_gettime(CLOCK_MONOTONIC, &t0); | |
| 106 | + | |
| 107 | + for (size_t off = 0; off + 2 * stride < bytes; off += stride) { | |
| 108 | + volatile uint64_t *a = (volatile uint64_t *)(buf + off); | |
| 109 | + volatile uint64_t *b = (volatile uint64_t *)(buf + off + stride); | |
| 110 | + hammer(a, b, iters); | |
| 111 | + pairs++; | |
| 112 | + if (pairs % 64 == 0) { | |
| 113 | + printf(" hammered %zu pairs...\n", pairs); | |
| 114 | + } | |
| 115 | + } | |
| 116 | + | |
| 117 | + struct timespec t1; | |
| 118 | + clock_gettime(CLOCK_MONOTONIC, &t1); | |
| 119 | + double secs = (t1.tv_sec - t0.tv_sec) + (t1.tv_nsec - t0.tv_nsec) / 1e9; | |
| 120 | + | |
| 121 | + printf("Hammered %zu pairs in %.1f s. Scanning for flips...\n", pairs, secs); | |
| 122 | + total_flips = count_flips(buf, bytes); | |
| 123 | + | |
| 124 | + if (total_flips == 0) { | |
| 125 | + printf("No bit flips detected. Try more iterations, more memory, or " | |
| 126 | + "a different stride.\n"); | |
| 127 | + } else { | |
| 128 | + printf("DETECTED %zu bit flip(s) — this memory is susceptible.\n", | |
| 129 | + total_flips); | |
| 130 | + } | |
| 131 | + | |
| 132 | + free(buf); | |
| 133 | + return 0; | |
| 134 | +} |