Rowhammer and the Attacks Below Your Hypervisor
There is one sentence worth internalising before you read anything else about hardware attacks, and it is not dramatic: the VM boundary is a software boundary on shared silicon. Everything a hypervisor enforces — the extended page tables, the vCPU exit handlers, the device model, the seccomp filter around the VMM process — is code executing on a machine whose DRAM chips, memory controller, caches and firmware are shared by every tenant on the box and know nothing about any of it. The boundary is real. It is also implemented at one specific layer, and there are layers underneath.
I'm Ajay; I build PandaStack, a Firecracker microVM platform where every sandbox create is a snapshot restore. I sell isolation, so I have a commercial interest in overstating what a microVM protects you from. This post is the other thing: an inventory of what it does not protect you from, written by someone who has to answer this question in security reviews and would rather answer it honestly than watch a customer discover the gap later.
Where the boundary is enforced, and where it stops
A microVM boundary is enforced by hardware-assisted virtualisation. A guest's page tables translate guest-virtual to guest-physical; a second set of tables owned by the host — EPT on Intel, NPT on AMD — translates guest-physical to host-physical. The guest never names a host physical address. It cannot, architecturally. That is a genuinely strong property, much stronger than a shared kernel with namespaces, and it is why a lot of us moved untrusted code onto microVMs in the first place.
Notice what the property is about, though. It constrains which addresses a guest is allowed to reference. It says nothing about what the physical act of referencing an address does to the chip. When a memory controller activates a DRAM row, that activation is an electrical event in a specific place on a specific die. The die does not consult a page table. It has no notion of tenancy, and the rows physically adjacent to the one you legitimately own may belong to somebody else entirely.
Access control is a statement about names. Physics is a statement about places. Rowhammer is what happens when the two disagree.
Rowhammer, without the hand-waving
A DRAM cell is a capacitor and an access transistor. A one is a charged capacitor, a zero is a discharged one, and the charge leaks, which is why DRAM must be refreshed periodically — the controller walks the rows and rewrites each one to restore its charge. That refresh interval is the whole reason rowhammer exists, because it defines a window in which charge can drift.
Reading DRAM means activating a row: the wordline is driven, the row's contents are dumped into sense amplifiers, and then it is closed. Do that to the same row rapidly and repeatedly and the electrical disturbance couples into physically neighbouring rows, accelerating their leakage. Do it enough times inside one refresh window and a cell in a neighbouring row can lose enough charge to be read back as the wrong value. A bit you were never permitted to touch changes state, and no instruction you executed was illegal. There is no software bug in the chain. Every access was to memory you owned.
Density is why this got worse rather than better. Cells shrank, rows got closer, stored charge got smaller, and the number of activations needed to induce a disturbance has trended down across generations. This is not a defect in one vendor's part that a recall fixes. It is a consequence of the same scaling that gives you cheaper memory, which is why it has survived multiple rounds of "the new generation fixes it".
Why landing it is genuinely hard
Now the non-alarmist half, because a lot of writing on this topic stops at "bits can flip" and lets the reader supply the panic. Flipping a bit somewhere is not an attack. An attack requires flipping a bit that matters, in memory that belongs to a specific victim, in a direction that helps you, before anything notices. Every one of those clauses is expensive.
- You need physical adjacency, and you cannot see it. The mapping from a physical address to a (channel, rank, bank, row) tuple is decided by the memory controller, is vendor- and configuration-specific, and is generally undocumented. Attackers reverse-engineer it with timing experiments before they can hammer anything, and it changes with DIMM population, interleaving settings and platform.
- You need the victim's data to land in a row you are next to. That is the step people skip. Classic exploitation depends on massaging the allocator into placing a high-value structure — historically a page-table entry — in a location the attacker has already surveyed. In a virtualised setting the target sits behind an additional translation layer and inside a different address space, so "get the victim's page next to mine" becomes a scheduling and allocator-grooming problem before it is a DRAM problem.
- You need a susceptible part. Flip behaviour varies by DRAM generation, by manufacturer, by die revision, by temperature and by refresh configuration. Attackers template a machine first — hammer, look for reproducible flips, record which offsets are weak — and a machine that templates poorly is a machine where the attack does not proceed.
- You need sustained, high-rate access. Hammering is not a syscall, it is a loop that must defeat the cache hierarchy so accesses actually reach DRAM, running long enough to beat the refresh window many times over. It is loud in every performance counter you might be watching.
- You need the flip to be useful. A random bit in a random page usually produces a crash, which is a denial of service and an alert, not a compromise.
So the honest framing is: rowhammer is real, it has been demonstrated repeatedly by serious researchers across many settings including cross-VM and remote-over-network variants, and it is not how your platform is going to get breached this quarter. Your platform is going to get breached by a leaked token in a CI log, an SSRF into a metadata endpoint, or a dependency someone typo-squatted. Weight your engineering accordingly. But do not tell a customer that a microVM makes hardware-layer attacks impossible, because it does not, and the person asking usually already knows.
The rest of the family living under the hypervisor
Rowhammer is the headline because it is the most surprising, but it is one member of a category: attacks whose mechanism sits below the layer your isolation is implemented at. The others are less exotic and, for most operators, more relevant.
DMA-capable peripherals are the big one, because a device that can perform direct memory access is, by construction, a second CPU with a view of physical memory that no page table constrains. Historically this meant an attacker with a Thunderbolt or ExpressCard port; in a datacentre it means anything on the PCIe fabric, including a NIC or an accelerator with attacker-influenced firmware, and it means every device you pass through to a guest. The mitigation is the IOMMU, which puts a translation and permission layer between devices and physical memory — and the mitigation only exists if it is actually enabled, actually in a strict mode rather than passthrough, and actually covering the device in question. "Is the IOMMU on" is a question with a real answer that many hosts get wrong.
Cold-boot and physical DRAM extraction attacks target remanence: DRAM does not lose its contents instantly at power-off, especially when cold, so an attacker with physical access can cut power, move the DIMMs and read out keys. This is a datacentre physical-security and hardware-encryption question, not a hypervisor one. It is also the class where the cloud providers' own controls are load-bearing, which is a reason to read a provider's physical-security attestations rather than assume.
Firmware and UEFI persistence is the quiet one. Below your kernel there is platform firmware, a management controller with its own network stack, and device option ROMs, and code that lives there survives reinstalls and does not appear in anything you are monitoring. On your own hardware this is a real programme of work — secure boot, measured boot, firmware update discipline, keeping the management network off anything routable. On a cloud instance you have delegated it, which is fine, but you should know that you have.
And transient execution sits alongside all of this rather than inside it: same property, different mechanism. Speculation leaks across a boundary the architecture says is closed, because the boundary is checked in the architectural world and the leak happens in the microarchitectural one. Same lesson, so it belongs in the same threat model even though the mitigations look nothing alike.
The mitigations, and what each one actually buys
ECC helps. It does not solve.
The standard server answer is ECC memory, and it is worth having — just not for the reason people usually give. Conventional server ECC is single-error-correcting, double-error-detecting per code word: one flipped bit is silently repaired, two are detected and typically escalated to a machine check. Against a naive hammer that produces isolated single-bit flips, that is a real defence.
Three reasons it is not the end of the story. First, the guarantee is per code word: a fault pattern that produces multiple flips inside one word is outside what SECDED can correct, and research has demonstrated that determined attackers can work toward exactly that. Second, correction is observable — timing differences between corrected and uncorrected reads have been shown to leak whether a flip occurred, which turns the safety mechanism into the feedback channel an attacker needs while templating. Third, and most practically, ECC is only a control if somebody is reading the counters. A host that silently corrects errors for eight months and reports them to nobody has bought reliability, not security.
Also, do not confuse DDR5's on-die ECC with system ECC. On-die ECC exists to make dense chips manufacturable by covering internal cell failures; it is not a link-level guarantee and it is not an anti-rowhammer feature, and its presence has been used in marketing in ways that will mislead you if you let it.
TRR, pTRR and refresh games
The DRAM industry's answer is target row refresh: the memory controller or the DIMM tracks aggressively activated rows and issues extra refreshes to their neighbours before the disturbance accumulates. Variants have shipped in memory controllers, in DIMM logic, and in the DRAM devices themselves, and vendors have described the problem as addressed more than once.
The structural weakness is that these mechanisms are samplers, not proofs. They track a bounded number of candidate aggressor rows with a heuristic, because tracking every row exactly would cost area and power. Anything that samples can be evaded by an access pattern that exceeds what it tracks — many-sided hammering, where an attacker distributes activations across more aggressor rows than the tracker has slots for, is the general shape, and academic work has repeatedly bypassed shipped implementations across vendors. Treat TRR as raising cost, not as closing the class.
Halving the refresh interval is the blunt instrument: refresh twice as often and there is half the window for charge to drift. It reduces flip rates rather than eliminating them, and you pay in memory bandwidth and power, on every access, forever, on a machine where the attack will probably never be attempted. Some platforms expose it; almost nobody in general-purpose hosting turns it on. Know that it exists so you can offer it when a customer's threat model genuinely calls for it.
Host hygiene, which is where the actual wins are
The unglamorous list is the one that pays. Keep microcode and platform firmware current across the fleet and know the version each host is running, because half of every microarchitectural mitigation ships as microcode and the kernel half without the microcode half is not a mitigation. Patch host kernels on a cadence you can state as a number. Turn the IOMMU on and keep it strict. Do not pass through devices you do not need, and treat every passthrough as an exception with a name attached. Disable platform features you are not using — legacy interfaces, unnecessary management functionality, anything that expands the firmware attack surface for no operational benefit. Ship the EDAC and machine-check counters off the host into the same place your other telemetry lives, so a host that starts correcting errors becomes a page rather than an archaeology exercise. None of this is about rowhammer specifically, which is precisely the point: it is the same work that protects you from the boring attacks, and it is finite.
#!/usr/bin/env bash
# What is actually in this host, and are the hardware-layer controls live?
# Read-only; run as root. Do this once per machine image, then again after
# every hardware refresh -- the answers change under you.
set -euo pipefail
echo "== DIMM inventory: generation, speed, rank =="
# The DRAM generation is the single biggest input to how any of this applies
# to a given machine. Ranks and population also decide address interleaving.
dmidecode -t memory \
| grep -E 'Size:|Type:|Speed:|Manufacturer:|Part Number:|Rank:' \
| grep -v 'No Module Installed'
echo
echo "== Does the platform claim ECC at all? =="
# Error Correction Type lives on the physical memory array (type 16), not on
# the individual devices. "None" here ends the conversation early.
dmidecode -t 16 | grep -E 'Error Correction Type|Maximum Capacity'
echo
echo "== Is a memory controller actually bound to the EDAC layer? =="
# This is the one that matters. SMBIOS can advertise ECC while no driver is
# bound, in which case nothing is counting and nothing will ever be reported.
if compgen -G '/sys/devices/system/edac/mc/mc*' >/dev/null; then
for mc in /sys/devices/system/edac/mc/mc*; do
printf '%s corrected=%s uncorrected=%s\n' "$(basename "$mc")" \
"$(cat "$mc/ce_count" 2>/dev/null || echo n/a)" \
"$(cat "$mc/ue_count" 2>/dev/null || echo n/a)"
done
else
echo "NO EDAC controllers registered -- corrected errors are invisible here"
fi
echo
echo "== Is anyone reading the counters? =="
systemctl is-active rasdaemon 2>/dev/null || echo "rasdaemon: not running"
command -v ras-mc-ctl >/dev/null 2>&1 && ras-mc-ctl --error-count || true
journalctl -k --no-pager | grep -iE 'edac|mce:|machine check' | tail -5 || true
echo
echo "== IOMMU: is peripheral DMA translated, or does the NIC own RAM? =="
if compgen -G '/sys/kernel/iommu_groups/*' >/dev/null; then
echo "iommu groups: $(find /sys/kernel/iommu_groups -maxdepth 1 -type d | wc -l)"
else
echo "NO IOMMU GROUPS -- any DMA-capable device sees physical memory"
fi
echo
echo "== Cross-tenant memory dedup (KSM). On a shared host you want 0. =="
cat /sys/kernel/mm/ksm/run 2>/dev/null || echo "KSM not built in"The two mitigations that actually change the answer
Everything above narrows the window. Two things change the shape of the problem, and both are commercial decisions as much as engineering ones.
1. Tenancy: put nobody else on the machine
Every attack in this post has the same precondition: the attacker's code and the victim's data are on the same physical machine. Remove co-residency and the entire category — rowhammer, DMA between tenants, cross-tenant cache and DRAM side channels, the transient-execution families — stops being about strangers and starts being about your own workloads attacking each other, which is a threat model most organisations are willing to accept.
This is why single-tenant hardware is the honest answer to a hardware-layer threat model, and why it is priced the way it is. You are not buying a better hypervisor. You are buying the removal of the precondition, and paying for the idle capacity that removal implies. If a customer's threat model genuinely includes a co-resident adversary with hardware-level ambitions, the correct response is a dedicated host and a contractual statement of that fact — not a longer paragraph about your VMM's seccomp filter. Anyone who answers "we use microVMs" to that question is answering a different question.
2. Dwell time: make the attacker's runway short
The second lever is the one an ephemeral platform gets close to for free. Hammering is not instantaneous. It needs a reconnaissance phase to reverse-engineer the address mapping on that specific machine, a templating phase to find reproducible weak cells, a grooming phase to get the target where you want it, and then sustained activation. That is a process with a duration, running in a guest that has to keep existing for the whole of it.
Which means the lifetime of an untrusted guest is a security parameter, not just a cost parameter. A platform whose sandboxes exist for the length of one request and are then destroyed is not a comfortable place to run a multi-stage physical attack. This only works if teardown is genuinely cheap, though — the reason short TTLs are palatable here is that a create is a snapshot restore rather than a boot: about 49 ms for the restore step, 179 ms at p50 and around 203 ms at p99 for the whole create path, against roughly 3 seconds for a cold boot with no snapshot yet. When re-creating a sandbox costs a fifth of a second, an aggressive TTL is a default rather than a trade-off.
from pandastack import Sandbox
# Dwell time is the variable you control. A hardware-layer attack needs a
# reconnaissance phase, a templating phase and sustained hammering -- all of
# it inside a guest that has to survive long enough to finish. Do not let it.
sbx = Sandbox.create(
template="code-interpreter",
ttl_seconds=60, # hard backstop, enforced host-side
metadata={"tenant": "acme", "trust": "untrusted"},
)
try:
# The per-call timeout is the circuit breaker; the TTL is the backstop for
# when your process dies before it gets to the finally block.
result = sbx.exec("python3 /workspace/job.py", timeout_seconds=45)
print(result.exit_code, result.stdout)
finally:
sbx.kill() # explicit teardown beats a timer
# For an interactive session you cannot bound up front, lease time in small
# increments while the user is actually present, rather than granting an hour
# on the first request. Every extension is a decision you can stop making.
live = Sandbox.get(session_id)
live.set_ttl(120)import { Sandbox } from "@pandastack/sdk";
// The same idea on the request path. One untrusted execution, one microVM,
// destroyed at the end of the handler. The security property is not that the
// VM is strong -- it is that it does not exist for very long.
export async function runUntrusted(source: string, tenant: string) {
const sbx = await Sandbox.create({
template: "code-interpreter",
ttlSeconds: 90, // backstop if this process dies
metadata: { tenant, trust: "untrusted" },
});
try {
return await sbx.exec(
"python3 -c " + JSON.stringify(source),
{ timeoutSeconds: 30 },
);
} finally {
// Teardown is the mitigation. Make it unconditional, and make it cheap
// enough that nobody is tempted to reuse a guest across tenants to save
// a boot -- a recycled sandbox is co-residency you built yourself.
await sbx.kill().catch(() => {});
}
}Shared, dedicated, air-gapped: what each buys at the hardware layer
Lined up against hardware-layer threats specifically — not against ordinary software compromise, where the ranking looks different and the shared host does much better than people expect.
- Co-residency with strangers — Shared multi-tenant host: present by design; it is the precondition every attack in this post needs. Dedicated host: removed for external adversaries; your own workloads still share DRAM with each other. Air-gapped/on-prem: removed, and you also control who is in the building.
- Rowhammer exposure — Shared multi-tenant host: theoretically in scope; mitigated in practice by ECC, vendor refresh mitigations, short guest lifetimes and abuse detection, none of which is a proof. Dedicated host: reduced to insider and supply-chain scenarios. Air-gapped/on-prem: same as dedicated, plus you choose the DIMMs and the refresh configuration yourself.
- DMA and peripheral attacks — Shared multi-tenant host: depends entirely on the operator's IOMMU posture and passthrough policy, which you should ask about explicitly. Dedicated host: same technical controls, but the only devices are yours. Air-gapped/on-prem: yours end to end, including the part where you have to actually do it.
- Cold boot and physical DRAM extraction — Shared multi-tenant host: delegated to the cloud provider's physical security; read their attestations rather than assuming. Dedicated host: still the provider's datacentre unless it is your rack. Air-gapped/on-prem: yours, which is an improvement only if your physical security is better than a hyperscaler's. Frequently it is not.
- Firmware and UEFI persistence — Shared multi-tenant host: provider-managed, invisible to you, and generally handled better than most enterprises handle it. Dedicated host: mostly provider-managed, with more room to demand attestation. Air-gapped/on-prem: entirely your programme — secure boot, measured boot, update discipline, management network isolation.
- Transient execution (Spectre family) — Shared multi-tenant host: mitigations plus microcode currency; residual cross-tenant risk is real but small and well studied. Dedicated host: cross-tenant risk removed; guest-to-host and guest-internal risk remains. Air-gapped/on-prem: identical to dedicated, since this is a CPU property, not a tenancy one.
- Everything you will actually be breached by — Shared multi-tenant host: leaked credentials, SSRF, supply chain, misconfigured storage. Dedicated host: exactly the same list, unchanged. Air-gapped/on-prem: the same list, plus a patching burden you now own and an operations team that is smaller than the provider's.
- Cost and operational drag — Shared multi-tenant host: lowest; you pay for what you run. Dedicated host: you pay for idle capacity, which is the actual product being sold. Air-gapped/on-prem: highest, and the ongoing cost is people rather than hardware.
Who should actually care
Most teams should read this, understand it, and change nothing about their architecture. If you are a SaaS running customer workloads on a reputable provider, redesigning around rowhammer is a misallocation. The marginal security engineer is worth vastly more pointed at your secret management, your dependency supply chain, your tenant-scoping in the data layer and your incident detection. Those are where compromises come from, and they are tractable in a quarter.
The teams that should care are the ones where a well-funded adversary would find co-residency worth engineering for. If your threat model includes a nation-state, or a competitor who would meaningfully profit from your process design kit, your unreleased model weights, your trading strategy or your pre-announcement financials, then "an adversary can pay to be scheduled onto the same physical machine as us" is a sentence you have to take seriously. The response is not a better hypervisor. It is dedicated hardware, written into the contract, with an operator who will tell you what they run, what firmware they are on, and who else is on the box.
For everyone in between, the useful posture is a short list you can actually hold: know your DRAM generation and whether ECC is live and monitored; keep microcode, firmware and host kernels current on a stated cadence; keep the IOMMU on and passthrough exceptional; never dedup memory across tenants; keep untrusted guests short-lived and rate-limited; and be able to say out loud which of your workloads share a physical machine with strangers. That is a finite amount of work, it protects you against far more than rowhammer, and it lets you answer the hard question in a security review with something better than a shrug.
And when someone asks whether a microVM protects them from hardware attacks, give them the sentence rather than the reassurance. The VM boundary is a software boundary on shared silicon. It is a good boundary — strictly better than a shared kernel, worth what it costs, and the right default for running code you did not write. It is also implemented at one layer, and the layers beneath it are a tenancy question. Anyone selling you otherwise is selling.
Frequently asked questions
Can rowhammer actually break out of a virtual machine?
Researchers have demonstrated rowhammer-based attacks in virtualised and cross-VM settings, so the answer is not "no". It is "yes, under conditions that are hard to arrange and easy to disrupt". The attacker needs to reverse-engineer the physical address mapping of that specific memory controller and DIMM population, template the machine to find cells that flip reproducibly, groom memory so that a high-value victim structure lands physically adjacent to a row they can hammer, and then sustain a high rate of DRAM activations long enough to induce a flip — all while the guest they are doing it from continues to exist. In virtualisation there is also an extra translation layer between guest-physical and host-physical addresses, which makes the grooming step materially harder than it is in a bare-metal privilege-escalation scenario. It remains a legitimate concern for high-value targets on shared hardware and a poor use of engineering attention for typical SaaS, where the realistic compromise paths are leaked credentials, SSRF and supply-chain issues.
Does ECC memory stop rowhammer?
It helps substantially and it is not a solution. Conventional server ECC is single-error-correcting and double-error-detecting per code word, so isolated single-bit flips are repaired transparently and two-bit patterns are detected and escalated. Three gaps remain. Fault patterns that put multiple flips inside a single code word fall outside what the code can correct, and research has shown attackers working deliberately toward that. The correction itself is observable through timing, which has been demonstrated as a feedback channel an attacker can use while templating a machine — the safety mechanism becomes the oracle. And most practically, ECC is only a security control if the counters are collected and alerted on; a host quietly correcting errors that nobody reads has bought reliability, not detection. Separately, DDR5 on-die ECC is not the same thing as system ECC — it exists to make dense chips manufacturable and should not be read as an anti-rowhammer feature.
Why do TRR and other vendor rowhammer mitigations keep getting bypassed?
Because they are samplers rather than proofs. Target row refresh works by tracking rows that are being activated aggressively and issuing extra refreshes to their neighbours before the disturbance accumulates. Tracking every row exactly would cost die area and power, so implementations track a bounded number of candidate aggressors using a heuristic. Anything bounded and heuristic can be evaded by an access pattern that exceeds what it tracks — distributing activations across more aggressor rows than the tracker has slots for is the general shape, and academic work has repeatedly demonstrated bypasses across vendors and generations. The correct way to hold this is that TRR raises the cost of the attack rather than closing the class, and that a vendor statement describing the problem as addressed should be read as "harder" rather than "impossible". Halving the refresh interval is the blunt alternative: it reduces flip rates rather than eliminating them, and it costs memory bandwidth and power on every access forever.
Does a short sandbox TTL really mitigate hardware attacks?
It raises the cost meaningfully and it is not immunity, so be precise about the claim. A hardware-layer attack has a reconnaissance phase to learn the address mapping, a templating phase to find reproducible weak cells, a grooming phase to position the target, and a sustained hammering phase — all of which must happen inside a guest that survives for the duration. Destroying that guest after seconds forces the attacker to restart from the beginning, and it bounds how much progress any single sandbox can make. What it does not do is stop someone creating sandboxes in a loop and accumulating knowledge across them, so short lifetimes belong alongside per-tenant rate limits, concurrency caps and abuse detection rather than instead of them. The reason aggressive TTLs are practical at all on a snapshot-restore platform is that re-creating a sandbox costs roughly 179 ms at p50 rather than a full boot, so short lifetimes stop being a performance trade-off. Note also that TTLs are enforced by a periodic sweep, so the real bound is the TTL plus up to one sweep interval.
When is a dedicated host worth the money for security reasons?
When your threat model includes an adversary who would find it worth engineering to be scheduled onto the same physical machine as you. Every attack in this category — rowhammer, cross-tenant DRAM and cache side channels, DMA between tenants, the transient-execution families — has co-residency as its precondition, and a dedicated host removes the precondition rather than mitigating the attack. That is a categorically different kind of defence from a better hypervisor, which is why it is priced the way it is: you are paying for the idle capacity that exclusivity implies. Concretely, it is worth it if a nation-state, or a competitor who would profit materially from your unreleased designs, model weights, trading strategy or pre-announcement financials, is a realistic adversary. For most SaaS it is not worth it, and the same budget spent on secret management, dependency provenance and detection buys far more actual security. If you do buy it, get the exclusivity written into the contract, and ask the operator what firmware and microcode revisions the fleet is on.
Keep reading
- Spectre, Meltdown, and what a microVM actually protects you from — The transient-execution layer this post deliberately skips over.
- Side-channel attacks in multi-tenant compute — The general shape: architectural boundaries, microarchitectural leaks.
- Hyper-threading and microVM isolation — The other tenancy decision that is made in hardware, not in software.
- KSM memory deduplication, explained — Why cross-tenant page sharing is a security decision, not a tuning knob.
- The Firecracker VMM security model — What the software boundary above all of this actually enforces.
- Controlling sandbox lifetime: TTLs and idle timeouts — The dwell-time lever, with the actual knobs and their semantics.
- Air-gapped and on-premise microVM deployment — The far end of the tenancy spectrum, and what it really costs.
- How to vet a code-execution vendor on security — The questions to ask an operator, including the tenancy one.
49ms p50 cold start. Fork, snapshot, and scale to zero.