Cache side-channel attacks read a victim's secrets by timing whether its data is in the cache, but attacks on the instruction cache are noisy because the hit-versus-miss timing gap is small; this paper (Iowa State and Google — Seonghun Son, Daniel Moghimi, Berk Gulmezoglu) shows that self-modifying-code detection on x86 — the machinery that flushes the pipeline when a program writes to code currently in the instruction cache — can be turned into a far louder signal, because a self-modifying write to a cache line triggers an expensive machine clear of hundreds of cycles only if that line is resident in the L1 instruction cache; SMaCk exploits this to build sharper, quieter versions of Prime+Probe and Flush+Reload that extract cryptographic keys and build Spectre-like covert channels across x86 platforms, and it proposes a performance-counter detector as a defense.
The argument in one glance
-
Problem
Instruction-cache attacks work in principle, but the timing signal is faint
Cache side-channel attacks let an adversary spy on a victim program by measuring timing. The idea is that when the victim runs, it pulls its code and data into shared caches, and the attacker can tell which cache lines the victim touched by timing how long its own accesses to those lines take — a fast access means the line was cached (the victim used it), a slow one means it was not. From that leak the attacker reconstructs the victim's secret-dependent behavior, such as which branch a cryptographic routine took, and recovers keys. This works well on the data cache, where the hit-versus-miss timing gap is clear, but on the L1 instruction cache — which holds the victim's executing code and would reveal exactly which code paths it ran — the timing difference between a hit and a miss is small and easily swamped by noise, so instruction-cache attacks are imprecise. The attacker's problem is that the signal they most want to read is one of the hardest to read cleanly.
-
What people did
Use classic Prime+Probe and Flush+Reload on the instruction cache
The standard tools are Prime+Probe (fill the cache with your own lines, let the victim run, then time re-accessing your lines to see which the victim evicted) and Flush+Reload (flush a shared line, let the victim run, then time reloading it to see if the victim brought it back). Applied to the L1 instruction cache across a shared core, these attacks in principle reveal the victim's code accesses.
-
Why it fell short
The hit-miss gap on the L1 instruction cache is too small to be reliable
On the L1 instruction cache the distinction the attacker relies on — was this line present or not — produces only a small timing difference, because a miss that goes to the nearby L2 or last-level cache is not much slower than a hit. Measured on real hardware, the gap is on the order of tens of cycles, which is easily lost in measurement noise and in evictions caused by other work on the core. So classic Prime+Probe and Flush+Reload on the instruction cache give noisy, low-resolution measurements, making precise key extraction and clean covert channels hard. What an attacker needs is a way to turn "is this line in the L1 instruction cache?" into a large, unmistakable timing event, rather than a faint one that must be teased out of noise — and that is exactly what the paper finds in an unlikely place.
-
This paper's idea
Weaponize self-modifying-code detection to make a loud L1i signal
SMaCk exploits self-modifying code (SMC): the x86 feature that lets a program write to its own instructions, and the hardware machinery that keeps that safe. When a program writes to a memory location whose instructions are currently in the pipeline, the processor must detect this conflict and take a machine clear — a full pipeline flush that discards in-flight work and refetches the modified code, costing hundreds of cycles. The crucial fact SMaCk uncovers is that this SMC-detection mechanism watches the L1 instruction cache specifically: a self-modifying write triggers the expensive machine clear only if the target line is resident in the L1i cache. So "is this line in the L1i cache?" becomes "does a self-modifying write to it cause a machine clear?", and the machine clear is a huge, clean timing event — up to around 350 cycles versus roughly 60 for an ordinary miss — far above the noise. SMaCk systematically finds which x86 instructions trigger SMC detection on Intel and AMD, builds new Prime+iProbe and Flush+iReload attacks on the L1i cache with a large timing margin, and uses them to create low-error covert channels between hyper-threads, recover cryptographic keys (including a single-trace attack on OpenSSL's Secure Remote Password protocol and an RSA key recovery), and mount Spectre-type attacks; it also proposes a hardware-performance-counter detector as a defense.
Why this problem is worth solving
Micro-architectural side channels are one of the most persistent threats to real systems, and the instruction cache is a uniquely revealing target. Modern processors share fast caches among the programs running on a core, and caches speed things up by keeping recently used code and data close. That sharing is the vulnerability: because a cache hit is faster than a miss, an attacker who runs on the same core as a victim can measure its own access times to infer which cache lines the victim used, without ever reading the victim’s memory directly. If the victim is a cryptographic routine whose control flow depends on secret key bits — taking one code path for a 1 bit and another for a 0 — then knowing which code lines it executed reveals the key. This is why the instruction cache is such a prize: it holds the code the victim is running, so leaking its contents directly exposes the victim’s secret-dependent code paths, which is often exactly what an attacker needs. Data-cache attacks are well developed, but they see data accesses, which are a step removed from control flow; instruction-cache attacks see the control flow itself. The obstacle has been that the instruction cache is hard to read cleanly, because the timing difference between a hit and a miss there is small. Making that read reliable would hand attackers a sharper, more direct window into secret-dependent execution, which is why understanding how it can be done — and how to detect it — matters for defending real cryptographic software.
The reason a bigger signal changes everything is that side-channel attacks live and die by signal-to-noise. A cache side channel is fundamentally a measurement: the attacker times an operation and decides, from that time, whether a cache line was present. The reliability of that decision depends entirely on how far apart the two cases are in time relative to the noise. If a hit takes, say, a few dozen cycles and a miss a few dozen more, the two distributions overlap once you add the jitter of a real system — other threads evicting lines, interrupts, frequency changes — so many measurements are ambiguous, and the attacker must average over many repetitions to recover each bit, which is slow, and sometimes impossible when the secret can only be observed once (a single-trace setting). If instead the two cases are hundreds of cycles apart, a single measurement decides the bit almost unambiguously, so the attack becomes faster, quieter (fewer repetitions to be noticed), and capable of single-trace key extraction and high-bandwidth covert channels. This is why attackers constantly seek ways to amplify the hit-miss gap. On the instruction cache the natural gap is small, so the search is for some hardware behavior tied to instruction-cache residency that produces a large, clean timing event. Self-modifying-code detection turns out to be exactly such a behavior, which is what makes SMaCk’s observation consequential rather than incremental — it converts a faint channel into a loud one.
What people did before, and where it broke
- Prime+Probe on the L1 instruction cache. Fill the instruction cache with attacker lines, let the victim run, and time re-accessing them to infer which the victim evicted. Where it broke. The hit-miss timing gap on the L1i is only tens of cycles, so the measurements are noisy and low-resolution, needing many repetitions.
- Flush+Reload on shared code. Flush a shared instruction line, let the victim run, and time reloading it to see if the victim executed it. Where it broke. Same small timing gap on the instruction cache, and it needs shared read-only code pages; the faint signal limits precision.
- Store-based self-modifying-code conflicts. Prior work noted that a store to cached code can cause an SMC conflict. Where it broke. It was limited to store instructions and not developed into a systematic, high-resolution attack primitive across instruction types and platforms.
- Amplify the signal by other means. Increase attack resolution or widen the speculative window to improve signal-to-noise. Where it broke. These help data-cache and speculative attacks but do not give the instruction cache a large, direct residency signal, so L1i attacks stayed noisy.
- The gap. No one had turned the x86 self-modifying-code detection mechanism — which fires a hundreds-of-cycles machine clear precisely when a written line is resident in the L1 instruction cache — into a general, high-resolution attack primitive for reading the instruction cache.
How SMaCk works
Turning self-modifying-code detection into a loud instruction-cache probe
-
-
new
-
new
-
new
-
new
-
new
The idea
Find a hardware event that fires exactly on instruction-cache residency, and make it loud. The core insight of SMaCk is to stop trying to read the faint natural hit-miss difference on the instruction cache and instead find a hardware behavior that produces a large, unambiguous signal tied to L1i residency. Self-modifying-code detection is that behavior: because the mechanism that catches a program rewriting its own in-flight instructions monitors the L1 instruction cache, a self-modifying write causes an expensive machine clear only when the target line is resident there. The attacker converts a subtle residency question into a hundreds-of-cycles event they can time trivially. Exploiting a subtle micro-architectural side effect to leak information that the hardware never intended to expose is the same lesson the work on eavesdropping user credentials via GPU side channels covered elsewhere on this site drives home, showing how much escapes through channels no designer meant to carry data. SMaCk’s contribution is identifying self-modifying-code detection as an especially powerful such channel for the instruction cache.
Amplify the channel enough to enable single-trace and speculative attacks. A larger signal is not just a nicer version of the same attack; it changes what is possible. Because an SMC conflict yields up to roughly 350 cycles versus about 60 for an ordinary miss, a single measurement can decide a bit with little averaging, which enables single-trace key extraction where the secret is observable only once, and it makes covert channels between hyper-threads low-error. SMaCk also uses the loud L1i channel to build Spectre-type attacks, reading out the results of transient execution through the instruction cache. Using cache-timing effects, including in speculative execution, to exfiltrate secrets across a trust boundary is the same phenomenon the work on protecting cryptographic code against Spectre and its variants covered elsewhere on this site defends against, where transient execution leaves cache traces an attacker can read. SMaCk is the offensive counterpart, sharpening the read side and showing that detectors keyed only to the data cache miss this instruction-cache channel.
Exploit the sharing that makes co-tenancy efficient. SMaCk’s attacks work because two hyper-threads on the same physical core share the L1 instruction cache, so an attacker co-located with a victim can probe it. This is the recurring cost of resource sharing: the same simultaneous multithreading that raises utilization also opens a channel between tenants that trust boundaries assume is closed. That danger — that merely sharing hardware with an adversary can leak your secrets — is the concern the work on co-location attacks against public cloud serverless platforms covered elsewhere on this site lays bare, where an attacker who lands on the same host can attack a neighbor. SMaCk shows a particularly sharp instance on a shared core, and, tellingly, ends by proposing a detector, because once a channel this clean is known, the practical response is to watch for its signature — the machine-clear and SMC-conflict events that hardware performance counters can see.
Following an attack through SMaCk
Reading a victim's secret code path via SMC conflicts
A trace, drawn from the paper, of how SMaCk turns self-modifying-code detection into a high-resolution instruction-cache attack.
Step 1 of 5
Attacker co-locates with the victim on one core
The attacker arranges to run as a hyper-thread on the same physical core as the victim, so they share the L1 instruction cache. The victim runs a cryptographic routine whose code path depends on secret key bits.
Shared L1i across SMT threads is the attack surface.
Why it works
- SMC detection is bound to L1 instruction-cache residency. An ordinary miss to L2 or the last-level cache is only tens of cycles slower than a hit → but a self-modifying write triggers a machine clear only when the line is in the L1i → so the event directly answers the residency question the attacker cares about.
- A machine clear is a huge, clean timing event. The natural L1i hit-miss gap is easily lost in noise → but a full pipeline flush costs up to hundreds of cycles → so a single measurement decides a bit almost unambiguously.
- A larger margin enables single-trace and covert channels. Faint signals require averaging many repetitions, which is slow and sometimes impossible → but the loud SMC signal decides bits in one shot → so single-trace key recovery and low-error inter-thread covert channels become feasible.
- Many instructions can provoke the conflict. Relying only on plain stores limits the attack → but SMaCk finds diverse x86 instructions (locks, prefetches, and more) that trigger SMC detection → so the primitive is flexible across situations and platforms.
- It slips past data-cache-focused defenses. Spectre detectors that watch the shared data cache miss this path → but SMaCk’s channel is in the instruction cache via SMC conflicts → so it demonstrates transient-execution leakage those detectors do not catch, motivating the proposed performance-counter detector.
What it costs (trade-offs and scope)
- This is an offensive study, presented with a defense. The paper’s purpose is to expose and characterize a vulnerability and then mitigate it; it proposes a dynamic detector using hardware performance counters to catch the tell-tale machine-clear and SMC events, and discusses software and hardware countermeasures. The value is defensive understanding, not a tool for misuse.
- It requires co-location on the same physical core. The attacks assume the attacker runs as a hyper-thread sharing the L1 instruction cache with the victim (simultaneous multithreading). Environments that disable hyper-threading or otherwise prevent co-residency remove this specific surface, at a performance cost.
- It is x86-specific and micro-architecture-dependent. SMaCk exploits the self-modifying-code detection behavior of x86 processors, and the exact instructions that trigger it and the timing magnitudes vary by micro-architecture (the paper studies Intel and AMD parts); the details do not transfer unchanged to other ISAs or to future designs that alter SMC handling.
- The detector has its own trade-offs. Detecting attacks via performance counters watching machine-clear and SMC events can raise false positives (legitimate self-modifying code exists) and imposes monitoring overhead; the paper proposes it as a mitigation, but a deployed detector must balance detection against those costs.
- The demonstrations are on specific targets. The key-recovery and covert-channel results are shown on particular software (OpenSSL’s SRP, an RSA implementation) and platforms; other software, hardened implementations, and configurations would need their own assessment, and constant-time code that avoids secret-dependent instruction paths is less exposed.
The evaluation
Reading the L1 instruction cache as a side channel
| Property | Classic Prime+Probe / Flush+Reload | Store-only SMC conflict | SMaCk |
|---|---|---|---|
| Signal used | |||
| Signal-to-noise on L1i | |||
| Triggering instructions | |||
| Single-trace / covert channel | |||
| Reported outcome |
Check yourself
Check your understanding
1. Why have attacks on the L1 instruction cache been imprecise?
2. What hardware behavior does SMaCk exploit to make a loud signal?
3. Why does the larger timing signal make the attacks more dangerous?
4. What is the scope and defensive framing of SMaCk?
Sources
The paper is “SMaCk: Efficient Instruction Cache Attacks via Self-Modifying Code Conflicts” by Seonghun Son, Daniel Moghimi, and Berk Gulmezoglu (Iowa State University and Google), published at ASPLOS 2025, DOI 10.1145/3676641.3716274. It is licensed CC BY.
This explainer is written from the paper’s full text (arXiv:2502.05429). The paper explores the security implications of self-modifying code (SMC) on x86 processors, showing that specific x86 instructions affecting instruction-cache lines lead to measurable timing discrepancies between cache hits and misses because the SMC-detection mechanism, which monitors the L1 instruction cache, triggers an expensive machine clear (pipeline flush) — up to around 350 cycles versus roughly 60 for an ordinary miss — only when a written line is resident in the L1i. SMaCk systematically identifies which x86 instructions trigger SMC detection on Intel and AMD, analyzes their effect on front-end and back-end units with performance counters, and crafts new Prime+iProbe and Flush+iReload attacks on the L1 instruction cache that create low-error covert channels between simultaneous-multithreading threads, more precisely recover cryptographic keys (including a single-trace attack on the OpenSSL Secure Remote Password protocol that extracts the server’s private key exponent bits, and an RSA key recovery), and mount Spectre-type attacks that show the inefficiency of shared-data-cache-based Spectre detection. Finally, it proposes a dynamic detection methodology using hardware performance counters to mitigate these threats, and discusses software and hardware countermeasures. This is published, defensively-oriented security research; the page explains it for understanding and mitigation. No numbers beyond the paper’s own have been introduced; the exact timing figures, instruction analysis, attack implementations, and detector evaluation should be confirmed in the paper.