By the time x86 was invented in the mid-1970s, stacks had become a well-understood feature of computer architectures.
The 8086 accordingly had a stack pointer, SP, and a base pointer, BP, for addressing a procedure’s frame; the 80386 widened both to ESP and EBP, and x86-64 widened them again to RSP and RBP.1 Application binary interfaces (ABIs) define calling conventions built on those registers, and stack operations are their stock in trade. Every PUSH, POP, CALL, and RET instruction modifies the stack pointer, and before stack engines, every one of them had to manipulate the stack pointer. That extra work is small, but it is serial, because every stack reference then depends on the register that the instruction before it has just modified. A single PUSH EAX decoded to three µops on the Pentium III, and a series of pushes formed a dependence chain through one architectural register that the out-of-order machinery behind it could do nothing to shorten.
The stack engine breaks the chain by maintaining the difference between the stack pointer requested by the program and the version last computed by the out-of-order core. Intel’s description of the Pentium M frames the relationship as an integer addition, with the architectural pointer equal to the out-of-order copy plus a delta maintained by the front end. The hardware patches the in-flight delta into the address of each stack-referencing µop as it is issued, so that dependencies on the pointer register are removed rather than resolved2. The savings are larger than one might expect, because the elided µops were on the critical path: about 5% of all µops, by Intel’s estimate.
The idea of computing the addresses of stack references in the front end, an operation then described as non-speculative that remains so, was presented at ISCA in 20003. It shipped in the Pentium M in 2003, and AMD’s first implementation was the K10, which Agner Fog describes as very similar to Intel’s. Intel’s small cores took longer to implement the feature, with partial support in Bonnell (c. 2008) updated the stack pointer at the address generation unit but had no engine at all, and a full implementation in Goldmont (c. 2016).
The structure does not hold architectural state. There is no register a program can read to discover the delta, no instruction that manipulates it, and no architectural state that records it; yet the correctness of every stack access depends on it being correct. That places the stack engine in the first family of covert caches, the ones that must be correct, and the mechanism that keeps it correct is hardware rather than software: when an instruction uses the stack pointer in a way the engine does not track, the hardware inserts a synchronization µop that folds the accumulated delta back into the register.
When the Hardware Misbehaves
The implementation cost comes in the form of the synchronization µop, and what triggers one differs by core. Intel’s early implementations tracked PUSH, POP, CALL, and RET, and synchronized whenever any other instruction read or wrote the stack pointer while the delta was non-zero, so that ADD RSP, 8 and MOV EAX, [RSP+16] both cost an extra µop. Golden Cove and Zen 5 track ADD and SUB against the stack pointer as well, which removes the most common trigger. AMD’s Zen 4 guide lists what is free: pushes and pops that do not reference the stack pointer as data, near calls, near returns including the form that takes an immediate, and stack-relative loads and stores all are tracked; everything else incurs an additional op and resets the tracking4.
The delta is held in a narrow register, so a long enough run of pushes can overflow it and force a synchronization. The depth at which that happens varies widely by core:
Sandy Bridge and Kaby Lake: 32 pushes
Gracemont: 56
Zen 2: 96
Zen 1, Zen 3, and Zen 4: about 128
Golden Cove: 540
Zen 5: 8,192
None of these figures is published by either vendor, and all of them are measurements.
The errata make the case that this structure belongs in the must-be-correct family, because when it fails, it produces an incorrect program. AMD’s erratum 721, against Family 10h and Family 12h, records that after a long series of pushes and near calls, or of pops and near returns, in 64-bit mode, “the stack pointer value jumps by a value of approximately 1024.”5 The erratum offers no fix, only a workaround that sets a bit in a configuration register, and it was found not by AMD but by Matthew Dillon while working on DragonFly BSD. AMD’s own text never says the words stack engine; the attribution comes from researchers reverse-engineering the structure thirteen years later.
Those researchers, at ETH Zurich, published the first systematic reverse engineering of the stack engine in 2025, covering Zen 1 through Zen 5 and Intel cores from Sandy Bridge to Alder Lake6. Their result is that the delta is a side channel. Because an unsupported instruction costs an extra µop only when the delta is non-zero, and the delta depends on how many pushes have happened, an attacker who is able to time a synchronization can learn the depth of the victim’s stack, and therefore which path the victim took through a function. They classify cJSON records by type with complete accuracy, and run a cross-thread covert channel at 12.5 kilobits per second. They are careful to say what the structure does not do: it introduces no new speculation primitives, so this is a timing channel rather than a transient execution attack.
A second result, published the following year, is more serious because it breaks correctness rather than confidentiality. A bit in an AMD model-specific register enables and disables the stack engine, and although the bit is documented as being per-core, it is not correctly synchronized between the two logical cores of a physical one. A malicious hypervisor can therefore flip it from the sibling thread of a victim running under SEV-SNP, at which point the memory updates from a PUSH commit while the architectural update of the stack pointer is withheld, shifting the guest’s stack pointer by as much as 640 bytes. The authors recover RSA-CRT keys, bypass an OpenSSH authentication check, and obtain root on Zen 1 through Zen 57.
How to Detect It
No modern core counts stack synchronizations. Searching the event lists that ship with Linux for Skylake, Ice Lake, Sapphire Rapids, and Zen 2 through Zen 5 finds no event whose name or description mentions the stack pointer, synchronization, or folding.
Three earlier Intel cores did count them, which makes this one of the few covert caches whose observability has regressed:
Pentium M:
EMON_SYNCH_UOPSCore 2:
ESP.SYNCHandESP.ADDITIONSNehalem and Westmere:
UOPS_DECODED.ESP_SYNCandUOPS_DECODED.ESP_FOLDING
Intel’s optimization manual still prints advice for the Core 2 pair, recommending a ratio of ESP.SYNCH to ESP.ADDITIONS of 0.2 or lower, on cores that have not been sold for fifteen years and using events that no current part implements8.
What remains is a microbenchmark. Issue a known number of PUSH instructions, execute an instruction the engine does not track, and compare the µop count against what the instructions alone should produce; the difference is the synchronization. The 2025 paper gives listings for this, and also reports that Golden Cove and Zen 1 fail to count the extra µop, which it attributes to a performance counter bug.
Working Around It
Keep the stack pointer out of the middle of stack sequences. Intel’s optimization manual has given a coding rule to this effect since the Core era, advising against explicit references to the stack pointer inside a run of pushes, pops, calls, and returns. Where such a reference is unavoidable, Agner Fog’s suggestion is to express it as a stack operation instead, replacing ADD ESP, 4 with POP ECX, and to pass parameters in registers where the calling convention allows, which is easier on 64-bit Linux than it was on the 32-bit conventions the advice was written for.
Treat stack depth as secret-dependent data if the code handles secrets. The remedy the 2025 authors recommend is the one that already applies to branch predictors and data caches, which is to make control flow independent of secrets, and where that is impractical, to balance the paths so that both synchronize the same number of times. AMD’s advisory for the side channel is informational and makes the same recommendation.
Switching the engine off is possible, and costs about four percent. The same model-specific register bit that StackWarp abuses can be set deliberately, and the 2025 measurements put the cost at 3.98 percent on Zen 4 and 3.94 percent on Zen 5 across SPEC CPU2017, with perlbench and leela near ten percent. On Zen 5 a narrower control disables only the tracking of ADD and SUB against the stack pointer, at 1.65 percent. For the hypervisor attack the remedies are microcode updates and the SMT protections that SEV-SNP already provides; shadow stacks help only against the variant that redirects control flow.
Further Reading
Niederer, Rüegge, Hajiabadi, and Razavi, Stack Engine Attacks — The first systematic reverse engineering of the structure, across nine microarchitectures, and the side channel that follows from it.
AMD, Revision Guide for Family 10h Processors — Erratum 721 in its original form, with its workaround and its refusal to name the structure responsible.
The widening is visible in the 80386 manual's register diagram, which draws SP inside ESP and BP inside EBP, and in the SDM's references to "instructions using RSP/RBP as base registers."
The mechanism is described in Intel's Technology Journal account of the Pentium M, May 2003, which gives the relationship between the architectural stack pointer, the out-of-order copy, and the front-end delta, and states that "dependencies on ESP are removed." The five percent figure and the recovery mechanism that restores the delta after mispredictions and faults are from the same article. Later cores moved the logic: Intel's optimization manual places it in the decoders on Core, Agner Fog puts it before the µop cache on Sandy Bridge and after the µop queue on Zen, and AMD's guides place it in the decode unit on Family 17h and in the integer rename unit on Zen 4. The 2025 measurements find it at dispatch, and independent of the µop cache.
Michael Bekerman, Adi Yoaz, Freddy Gabbay, Stephan Jourdan, Maxim Kalaev, and Ronny Ronen, "Early Load Address Resolution via Register Tracking," ISCA 2000. The paper describes tracking the stack pointer in the front end so that addresses can be computed there, and is cited as the antecedent by Intel's own Pentium M article.
Software Optimization Guide for the AMD Zen4 Microarchitecture, publication 57647, section 2.9.7. The Intel trigger examples are from Agner Fog's microarchitecture manual, which works through them core by core from the Pentium M onward. The overflow depths listed below are all measurements from the 2025 paper, in its Table 3 and Figure 4; neither vendor publishes the width of the delta register, and the two published inferences about its width, eight bits on the Pentium M and twelve on Golden Cove, do not obviously reconcile with the push counts.
Revision Guide for AMD Family 10h Processors, publication 41322, revision 3.92, March 2012, erratum 721, "Processor May Incorrectly Update Stack Pointer," which is marked as having no fix planned. AMD's confirmation to Matthew Dillon adds that Family 12h is affected and that Bulldozer is not. No comparable Intel erratum has been found; a search of the Skylake specification update for stack pointer, push, and pop turns up only unrelated items concerning MOV and POP to the stack segment register.
Fabian Niederer, Lukas Rüegge, Ali Hajiabadi, and Kaveh Razavi, "Stack Engine Attacks," MICRO 2025. AMD's response is bulletin AMD-SB-7045, which is marked informational and recommends constant-time code; Intel points to its existing constant-time guidance and has issued no advisory.
Ruiyi Zhang, Tristan Hornetz, Daniel Weber, Fabian Thomas, and Michael Schwarz, "StackWarp: Breaking AMD SEV-SNP Integrity via Deterministic Stack-Pointer Manipulation through the CPU's Stack Engine," CISPA Helmholtz Center for Information Security, USENIX Security 2026, tracked as CVE-2025-29943 and AMD bulletin AMD-SB-3027 of January 2026, which rates it Low under CVSS 3.1 and Medium under CVSS 4.0, and reports it fixed by microcode or platform firmware.
The Pentium M, Core 2, and Nehalem event names are from the historical event lists in the Linux kernel's 0 and from libpfm4. That the Pentium M event counts stack synchronizations specifically, rather than synchronization µops of every kind, is not established by any document found. The surviving Core 2 advice is in the Intel 64 and IA-32 Architectures Optimization Reference Manual, Volume 1, section 22.8.1.3.