spacehub

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

claude-code / rowhammer.c
134 lines · 4.7 KBRaw
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>
48static inline void flush(volatile void *p) { _mm_clflush((const void *)p); }
49static 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.
56static 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.
67static 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
83int 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}