> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

Inside google/rowhammer-test: How to Corrupt Memory by Reading It

[ View on GitHub ]

Inside google/rowhammer-test: How to Corrupt Memory by Reading It

Hook

What if I told you that simply reading from memory—without any write operations—could corrupt data in completely different memory locations? This isn't a software bug; it's a fundamental hardware vulnerability in modern DRAM.

Context

In 2014, researchers at Google's Project Zero discovered something unsettling: DRAM chips had become so densely packed that electrical charge could leak between adjacent memory rows. By repeatedly accessing one row of memory, attackers could induce bit flips in neighboring rows—a phenomenon dubbed "rowhammer." This wasn't just an academic curiosity; it represented a fundamental break in the memory isolation guarantees that underpin operating system security. An unprivileged process could potentially flip bits in kernel memory, page tables, or other processes' address spaces.

The google/rowhammer-test repository emerged as one of the first publicly available tools to detect this vulnerability. Unlike traditional memory testing utilities that check for manufacturing defects, this tool deliberately exploits the rowhammer effect to determine if your DRAM is vulnerable. It's both a diagnostic tool and a proof-of-concept attack, demonstrating how userspace code can induce hardware-level failures without requiring any special privileges or direct hardware access.

Technical Insight

Rowhammer Attack

No

Yes

Yes

No

Program Start

Allocate Large Memory Block

Initialize Memory with Known Pattern

Randomly Select Address Pairs

Hammer Loop

Read addr1

CLFLUSH addr1

Read addr2

CLFLUSH addr2

Memory Fence

Iterations Complete?

Scan Memory for Bit Flips

Bit Flips Found?

Report Vulnerability

Exit

System architecture — auto-generated

The architecture of rowhammer-test is deceptively simple, which makes it elegant. The entire attack relies on understanding how DRAM banks work: memory cells are organized in rows and columns, and accessing a row requires "activating" it, which charges capacitors. When you repeatedly activate one row (called "hammering"), electromagnetic interference can cause charge to leak into adjacent rows, potentially flipping bits from 0 to 1 or vice versa.

The core of the tool's strategy is probabilistic. Modern CPUs don't expose the physical memory layout or bank organization to userspace, so the tool can't directly determine which memory addresses map to the same bank but different rows. Instead, it allocates a large chunk of memory and randomly picks pairs of addresses to hammer. Since typical DRAM configurations have around 16 banks, there's approximately a 1/16 chance that any two random addresses will hit the same bank—exactly what we need to trigger rowhammer. Here's the heart of the hammering loop:

// Simplified from the actual implementation
void hammer_addresses(uint64_t* addr1, uint64_t* addr2, size_t iterations) {
  for (size_t i = 0; i < iterations; i++) {
    // Read from first address and flush from cache
    *addr1;
    _mm_clflush(addr1);
    
    // Read from second address and flush from cache
    *addr2;
    _mm_clflush(addr2);
    
    // Memory barrier to prevent reordering
    _mm_mfence();
  }
}

The CLFLUSH instruction is critical here. Without it, the CPU cache would serve these reads without ever touching DRAM, rendering the attack useless. CLFLUSH evicts the cache line, forcing the next read to go all the way to physical memory. This is what creates the repeated DRAM row activations that cause the electromagnetic interference. The memory fence (_mm_mfence) prevents the CPU from reordering these operations, ensuring that flushes actually happen between accesses.

The tool wraps this hammering pattern in a continuous monitoring loop. Before hammering, it initializes memory with a known pattern (typically alternating 0xFF and 0x00). After each round of hammering, it scans the entire allocated region looking for any bits that have changed. When it finds a bit flip, it reports the address, expected value, and actual value—proof that rowhammer has successfully corrupted memory.

What makes this approach brilliant is that it sidesteps the need for any privileged information. You don't need to know the DRAM timing parameters, the bank organization, or even whether you're running on DDR3 or DDR4. The probabilistic strategy combined with enough iterations will eventually find vulnerable address pairs if they exist. The trade-off is runtime: you might need to test for hours or even days to thoroughly exercise the memory, since you're relying on random chance to find the right address combinations.

The tool also includes careful memory allocation strategies. It tries to allocate as much memory as possible (often several gigabytes) to increase the statistical likelihood of finding vulnerable address pairs. It uses huge pages when available to reduce TLB pressure during the intense memory access patterns. These optimizations don't change the fundamental approach, but they significantly improve the probability of detection within reasonable time frames.

Gotcha

The most significant limitation is that this is fundamentally a destructive test with no safety net. When rowhammer-test finds a vulnerable address pair and successfully flips bits, those bit flips are happening in your actual physical DRAM. If the flipped bits are in memory being used by other processes, the kernel, or even the testing tool itself, you can expect undefined behavior ranging from application crashes to kernel panics. The repository's README prominently warns that running the tool can cause permanent damage to DRAM modules, though this is relatively rare with modern temperature and refresh management.

The x86-only nature is another hard constraint. The entire approach depends on CLFLUSH, which doesn't exist on ARM, RISC-V, or other architectures. While ARM has cache maintenance instructions (like DC CIVAC), they don't behave identically, and porting this tool to other architectures would require significant rearchitecting. Additionally, the probabilistic testing strategy means you're never quite sure if your DRAM is truly safe or if you just haven't run the test long enough. A negative result (no bit flips found) doesn't guarantee your memory is invulnerable—it might just mean you were unlucky with your random address selections. For comprehensive validation, you'd need to run the tool for extended periods, which increases the risk of collateral damage from any bit flips that do occur.

Verdict

Use if: You're conducting security research on rowhammer vulnerabilities, validating DRAM modules in a lab environment where data loss is acceptable, or need to verify whether specific hardware is vulnerable before deploying it in security-critical applications. This tool provides the most straightforward way to get empirical evidence about rowhammer susceptibility on x86 systems. Skip if: You're on non-x86 architectures, need to test production systems without risking data corruption, require deterministic testing with guaranteed coverage, or want something that can run safely in CI/CD pipelines. For those scenarios, hardware-based memory testing tools or ECC memory validation utilities are more appropriate. This is a loaded gun of a diagnostic tool—powerful and effective, but requiring careful handling and acceptance of the risks involved.