Vulnerability Research
KVM Treated a 24-Byte Scratch Buffer Like a Full GHCB Page — Reconstructing CVE-2026-53360
CVE-2026-53360 let an SEV-SNP guest turn an invented 24-byte PSC scratch length into an external KVM heap object, then traverse it under the 253-entry limit for a full GHCB page, producing KASAN-confirmed out-of-bounds and use-after-free failures; we trace the object through ten repairs and turn the findings into version, reboot, scheduling, evidence, and regression checks for confidential-computing fleets.

In this article
1 The 24-byte object held two entries; old KVM enforced a 253-entry ceiling
The Linux CNA's KASAN results reduce CVE-2026-53360 to a reproducible object-boundary mismatch. An eight-byte PSC header leaves room for only two eight-byte entries in a 24-byte host object, yet old KVM validated the end index against the 253-entry capacity of a complete GHCB Shared Buffer. Array index two begins at offset 24, so its first eight-byte access is already outside the object. Public testing produced 73 reports: 62 slab out-of-bounds findings, seven slab use-after-free findings, and four general use-after-free findings.
These results are public test evidence, not a confirmed cloud incident, and they do not establish exploitation in the wild or reliable host code execution. They still fix the full causal chain: a field with no PSC-length meaning creates a short host object; a page-wide capacity then governs the object-local loop; userspace completion, synchronization, and release carry the same boundary error across several lifecycle stages. The report follows that object from VMGEXIT through cleanup, then turns the repair into running-version, SNP-placement, evidence-preservation, and regression decisions.
The code and response can be stated early. The flaw is in arch/x86/kvm/svm/sev.c: setup_vmgexit_scratch() once let external GHCB v2 scratch become a host object, while snp_begin_psc() failed to derive the current object's PSC capacity from ghcb_sa_len. The Linux CNA maps affected upstream releases from 6.10 and publishes fixed floors of 6.12.93, 6.18.35, 7.0.12, and 7.1. Deploy a vendor kernel with equivalent repair, reboot L0, and return a node to SNP placement only after verifying the running build. During the window, remove unknown or unrepaired nodes from SNP scheduling, migration, and HA destinations and pause untrusted SNP workloads where necessary; guest updates and network rules do not replace the host repair.
Picture a short service tray. Its first compartment holds an eight-byte header; the second and third hold one eight-byte entry apiece. Twenty-four bytes are now exhausted. On the wall hangs a capacity notice for the entire shared work surface: at most 253 entries. Old KVM did not measure the tray before consulting that notice. If the guest's end index remained below 253, iteration continued. Entry index two—the third entry—already began outside the allocation.
That sounds like an ordinary array overflow until we ask where “24” came from. The Page State Change protocol did not carry a total scratch length. GHCB version 2 instead required the PSC structure to reside in the Shared Buffer of the same GHCB page, allowing the host to derive the remaining size from a validated pointer. Old dispatch code nevertheless supplied exit_info_2 to setup_vmgexit_scratch() as though it were a PSC length. A size absent from the protocol became a real kvzalloc().
The next semantic substitution followed immediately. snp_begin_psc() knew that a complete GHCB work area could hold at most 253 eight-byte entries. It did not derive how many entries fit in the object returned for this exit. One field was promoted to an invented length; a page-wide allowance was then granted to the resulting short object. Neither choice looked like a dramatic unsafe copy in isolation. Connected in one data path, they moved the loop beyond its allocation.
The Linux CNA is explicit about the public consequences. A malicious SNP guest can corrupt the host kernel heap and obtain information about host-heap layout. The presence of both slab out-of-bounds and use-after-free findings shows that the error need not end with the first bad entry read. The object is handed toward userspace, completed, synchronized, and eventually released; allocator layout and timing can decide where the defect becomes visible.
The record is equally valuable for what it does not establish. Public material does not document exploitation in the wild, disclose a reliable host-code-execution chain, or show that an attacker can freely choose a host write address and value. Completion updates the cur_page field of a PSC entry; the public analysis describes constrained values of 1 or 512. Whether an out-of-object word looks valid, what object sits next to it, and when reuse occurs all affect the result. Host-heap corruption is serious without claiming reliable host takeover that the evidence does not support.
The reachable chain does not begin with an ordinary unencrypted VM. The vulnerable validation path requires an SEV-SNP guest, control of GHCB fields and the relevant VMGEXIT, and VMM/userspace support for the KVM_HC_MAP_GPA_RANGE exit used by PSC. An AMD processor advertising SEV or a loaded kvm_amd module is only the first inventory filter. It does not prove that this path is reachable on a particular node.
1.1 Confidential computing seals the private room; it does not measure the shared tray
AMD Secure Encrypted Virtualization first encrypted guest memory under distinct keys. SEV-ES extended protection to register state. SEV-SNP added integrity against remapping, measurement, and stronger attestation properties. Those mechanisms answer whether a host or neighboring guest should be able to inspect or substitute protected private state. A protected guest still needs the host to perform I/O, change page state, and emulate privileged operations, so the two sides retain a deliberately visible communication surface.
The Guest-Hypervisor Communication Block, or GHCB, is that service hatch. It occupies one shared, decrypted page. The guest places an exit reason, validity information, control fields, and references to auxiliary payloads into the page. KVM maps it, checks the negotiated protocol and required fields, then dispatches work inside the kernel or to VMM userspace. Shared means visible to both sides. It does not mean the guest-authored bytes have become trustworthy.
An attestation report is closer to a seal on the private room than a ruler for the tray passed through the hatch. It can help establish which measured guest launched and which private-memory guarantees apply. It does not verify every address, index, or size interpretation used by a host parser. The more privileged the receiver, the more carefully every guest-controlled value must be tied to the concrete object that receiver accepted.
CVE-2026-53360 therefore is not a break of memory encryption. The guest does not force the host to decrypt a private page or forge SNP's cryptographic evidence. The defect lies in the GHCB control plane that both parties intentionally share. The host parser violated the version 2 location rule, then traversed a copied host-heap object using a capacity that belonged to a different layout. Encryption continued doing its job while C code left its allocation.
That distinction changes an investigation. Engineers should not begin with a search for cryptographic anomalies. They should identify the booted host kernel, its GHCB v2 implementation, the SNP guests scheduled during the event, and the surviving crash stack. Successful guest attestation cannot mark the node safe: attestation answers who the guest is, not whether KVM safely parses a structure that guest submits through shared memory.
It also explains why the threat model permits a malicious confidential guest. Giving a tenant control over its guest kernel is an ordinary property of a VM product; confidential computing emphasizes that the host should not inspect the tenant's private state. A platform cannot assume that an attested guest will remain benevolent, or that a compromised guest kernel will form only friendly GHCB requests. The hypervisor must remain correct against the worst legal protocol endpoint.
An application process inside the VM does not normally gain arbitrary PSC construction merely by opening a socket. The attacker needs guest-kernel control, equivalent privileged code, or another capability that can produce the relevant GHCB and VMGEXIT state. That prerequisite narrows entry, but not the host failure radius. Memory corruption occurs in L0 KVM; a panic can interrupt unrelated VMs and management components sharing the physical node.
From a systems perspective, GHCB is a particularly consequential shared-memory RPC. A sender authors a structure, a privileged receiver validates and acts upon it, the sender may continue changing shared bytes, protocol versions alter field semantics, and completion can cross the kernel/userspace boundary. Virtio descriptors, device rings, firmware mailboxes, and shared command queues exhibit the same family of design pressure. The engineering lessons at the end of this report travel well beyond SEV.

1.2 Fix the evidence first, then reconstruct the causal chain
The report uses the raw Linux CNA JSON as the primary anchor for affected versions and public impact, AMD GHCB 56421 Rev. 2.04 as the protocol anchor, and the vulnerable parent plus ten adjacent upstream commits for the data path. Current Linux headers establish the exact layouts of struct ghcb, psc_hdr, and psc_entry. Ubuntu, Red Hat, and SUSE pages provide product-package entry points; they do not override the upstream implementation facts.
That source hierarchy prevents several plausible mistakes. A CVE summary explains what happened and where upstream fixes landed; it rarely exposes every parameter transition. The specification says what version 2 required. Only the source chain shows how a value crossed a function boundary. Reading the summary alone invites the false statement that the guest honestly supplied a PSC length. Reading only the four-line main fix obscures why capacity and shared-field snapshots still needed work.
Vendor status needs a date. Enterprise kernels backport changes onto version strings that may look older than upstream fixed floors; custom builds may look newer while omitting a relevant change. This report records the official upstream floors and a July 17, 2026 view of vendor pages. Final acceptance still has to connect the product advisory, exact package build, equivalent source behavior, and the boot ID of the kernel actually running.
2 One page is 4,096 bytes; only 2,032 form the tray surface
The service hatch is one GHCB page—4,096 bytes on the x86 systems in scope. From a distance it looks like one shared board. The source layout divides it into three regions with different jobs. The first half contains the GHCB save area and fixed controls. At offset 2,048 begins a 2,032-byte Shared Buffer. The final sixteen bytes hold reserved tail space, the protocol version, and the usage field.
struct ghcb is statically constrained to equal PAGE_SIZE. This is not aesthetic alignment. Guest and host must agree on exact field locations in order to exchange state while register contents remain protected. Fields such as sw_exit_code, sw_exit_info_1, sw_exit_info_2, and sw_scratch live in the save-area portion. Variable payloads use shared_buffer[2032].
“Inside the page” is not precise enough. A scratch pointer must lie in the permitted Shared Buffer, not in the save area or the metadata tail. Even when the pointer legally begins halfway through the Shared Buffer, its available object extends only from that point to the end of the work area. It does not inherit all 2,032 bytes merely because both addresses share a page.
Commit 5867d7e202e0 later made that distinction explicit. Setup no longer receives an ambiguous total length. The consumer states a min_len that must be present. For an in-page address, KVM calculates the distance from the accepted scratch start to the end of the Shared Buffer and stores that effective span in ghcb_sa_len. A later start yields a smaller object.
The rule is familiar from any C object with a flexible tail. A type tells the compiler the fixed header size; it cannot reveal how many trailing elements this allocation contains. Nor does a pointer into the middle of a larger object retain the full length of its container. The reliable size comes from a validated start and a known end, in the same address domain, after overflow-safe checks.
2.1 Walking from the save area to the end of the Shared Buffer
The Linux GHCB structure begins with state needed for protected exits and padding that places the variable work area at 0x800, decimal 2,048. The Shared Buffer then occupies 2,032 bytes, reaching 0xff0. The final 0x10 bytes contain reserved space, the two-byte protocol version, and the four-byte usage value. Those fixed offsets let independently implemented guest and host code exchange the same structure.
The hexadecimal offsets are not indicators that operators need to memorize, but they explain why 2,032 is not an approximation. Calling the work area 2,048 bytes would change the theoretical PSC capacity from 253 entries to 255. Calling the whole 4,096-byte page a payload buffer would be worse. A recurring source-review error is to substitute a container's size for the usable extent of one member.
sw_scratch contains a guest-physical address, not a host pointer that has already passed validation. After mapping the current GHCB, KVM determines whether that address lies in this page's Shared Buffer and converts it into a position in the mapping. The old helper also retained a route for scratch data elsewhere in guest memory, copying it through a temporary host object. Some version 1 operations required that compatibility; version 2 PSC did not permit it.
Checking the start alone is insufficient. Any minimum header access must establish that start + min_len remains within the allowed region and does not wrap. Variable-array capacity must then use the remaining accepted span. The repaired helper separates “is the minimum structure readable?” from “how many bytes remain in this object?” That is clearer than asking callers for one len that simultaneously pretends to be a requirement and an externally supplied fact.
The version value lives near the page tail yet changes how the entire page is interpreted. Once the guest and KVM negotiate a protocol version, the same sw_scratch address can be legal under one version and invalid under another. Code must select the version's rule before validating the address. A protocol version is part of the parsing contract, not descriptive UI metadata.
Downstream vendor branches may rename members or reshape the helper while preserving the ABI. Source acceptance should therefore ask behavioral questions. Does version 2 and newer reject PSC scratch outside the Shared Buffer? Is an in-buffer effective length computed from the accepted pointer to the work-area end? Is the minimum header guaranteed before the first dereference? Equivalent behavior matters more than an identical patch hunk.

2.2 The 253-entry figure belongs to the full work surface, not every tray
PSC stands for Page State Change, not Process State Change. It allows an SEV-SNP guest to request state transitions for guest-physical pages, including movement between private and shared uses. The guest places a psc_hdr at the beginning of scratch space and follows it with psc_entry records. Host and userspace process records in batches and write progress back into the shared structure.
struct psc_hdr is exactly eight bytes: two 16-bit indices, cur_entry and end_entry, plus a 32-bit reserved field. Each packed psc_entry is also eight bytes, holding current-page progress, a 40-bit GFN, an operation, and reserved bits. The protocol structure contains no C pointer and no total-length field. The number of entries has to be derived from the bytes in the accepted backing object.
If the header starts at the first byte of the 2,032-byte Shared Buffer, the arithmetic is (2032 - 8) / 8 = 253. That is an entry count, so the largest valid zero-based end index is 252. Kernel validation rejects an end_entry that is greater than or equal to the calculated count. Moving the scratch start eight bytes later reduces available capacity by one entry.
The same calculation for a 24-byte object yields (24 - 8) / 8 = 2. Indices zero and one lie inside; index two does not. The vulnerable implementation never inserted 24 into that equation. It compared end_entry only with the fixed VMGEXIT_PSC_MAX_COUNT. An end_entry of ten passed that page-level check while requesting indices zero through ten—eleven entries from an object holding two.
A neighboring constant, VMGEXIT_PSC_MAX_ENTRY 64, is easy to misread. It concerns a guest-side stack batch, not the total capacity of the GHCB page. VMGEXIT_PSC_MAX_COUNT 253 represents the full Shared Buffer layout. Neither value replaces the capacity of the object in this exit. Similar names are not enough; each constant must be read at its use site.
cur_entry and end_entry also represent progress. KVM may not finish the array in one step. Userspace completion can return, update the current point, and continue processing. That gives the object a lifetime across several phases and makes guest modification of the shared page relevant. Correct capacity solves the spatial problem; stable reads are still needed to solve the temporal one.
That is why the repair did not merely lower 253. Replacing it with 64 would still be unsafe for a two-entry allocation, while globally reducing capacity to two would break legitimate batching for a full page. A correct limit has to vary with the current object. The architecture-wide maximum can remain as a ceiling, but the allocation-specific maximum must be the effective gate.
The AMD specification connects both halves of that rule. The PSC structure for protocol version 2 and newer belongs in the GHCB Shared Buffer, and the hypervisor is expected to keep indices within the buffer's limits. Enforcing only location still leaves a pointer near the end with less capacity. Enforcing only count while retaining external objects violates the version contract. The ten-commit sequence is useful because it reconnects both requirements.
At this point in the data path, the 24-byte external tray should never exist. A version 2 PSC belongs inside the page, where KVM can derive its extent. The next chapter explains how history preserved a route around that natural size: a new protocol version entered KVM, an old helper still asked callers for a length, and the PSC caller did not actually possess one.
3 The protocol supplied no length, so KVM invented one
The history returns us to 2024. Commit 4af663c2f64a allowed KVM to configure the GHCB protocol version per guest and raised the negotiable maximum to version 2. The newer KVM_SEV_INIT2 route could select v2 for a protected guest, while the legacy initialization interface retained v1 behavior. The Linux CNA begins the affected upstream range at 6.10 because this is where version 2 semantics entered the existing SEV VMGEXIT machinery.
Introducing version 2 did not itself write the out-of-bounds loop. The existing scratch helper supported operations that could refer to guest memory outside the GHCB page: a caller supplied an address and length, the helper allocated a host buffer, copied bytes in, and synchronized them back after processing. That was meaningful compatibility behavior where an older protocol allowed external scratch space. The PSC contract changed, but the call interface still looked as though every consumer possessed a meaningful len.
PSC support originated in commit 9b54e248d264. When an SNP guest supplied the corresponding sw_exit_code, KVM validated mandatory GHCB fields and entered the SVM_VMGEXIT_PSC case. The old code then called setup_vmgexit_scratch(vcpu, true, control->exit_info_2). The final argument was syntactically tidy and semantically unsupported.
AMD's PSC definition did not say that exit_info_2 carried the total payload size. The explanation for later commit 2be54670bdc0 characterized the old length behavior as something the implementation had made up. The repair stopped pretending that the guest supplied a total. The caller expressed only what the consumer required before its first access: at least sizeof(struct psc_hdr).
3.1 From VMGEXIT dispatch to the wrong meaning for exit_info_2
A protected exit starts when the guest executes VMGEXIT. KVM obtains and maps the current GHCB, reads its protocol version, usage, and validity map, then chooses a handler from sw_exit_code. PSC validation also requires that the vCPU belongs to an SNP guest and that fields such as sw_scratch are marked valid. An SEV-ES guest does not automatically reach this vulnerability merely because it also uses a GHCB.
Passing that validation means “this request is eligible for PSC parsing.” It says nothing about total array length. At this point, the vulnerable dispatcher fed control->exit_info_2 to setup as the length. Both are integer values, so the compiler cannot see the semantic mismatch. Nor could the old function signature distinguish a length guaranteed by the protocol from a minimum number of bytes needed by a local consumer.
If the guest selected a value of 24, setup_vmgexit_scratch() used 24 when calculating an end address and deciding between the in-GHCB and external paths. With sw_scratch outside the page, old code permitted the second route and eventually called kvzalloc() with that value. A number with no PSC length identity now had a concrete allocator consequence.
Protocol evolution made the error subtle. External scratch support was real for neighboring historical operations, so it was easy to move from “the helper can do this” to “the new version permits this.” When a new version narrows the legal address set, compatibility code must be explicitly confined to the old branch. Merely increasing a maximum-version constant imports old capabilities into a new contract.
The main fix, db3f2195d293, put the version rule into setup. When the negotiated version is at least 2 and scratch is not in the current GHCB Shared Buffer, the function fails immediately. Four inserted lines place the specification in front of allocation. An outside address can no longer use the old compatibility path to become a version 2 host-heap object.
Why enforce this in the helper instead of only in the PSC switch case? Setup is the point that knows both the negotiated version and whether the address resolved inside the GHCB. It is also the point where the dangerous capability—external allocation and copy—would occur. Guarding the capability covers future callers, while individual consumers still declare the minimum structure they need.
This gate closed the principal route that created a 24-byte external PSC allocation. It did not yet make every consumer follow one safe object model. An in-page pointer can begin midway through the work area, and shared header fields can change after validation. Upstream maintainers treated the four-line CVE patch as the beginning of an interface audit, not its final line.
3.2 The external compatibility route turns a wrong number into a host object
In the parent of db3f2195d293, setup_vmgexit_scratch() first read the saved sw_scratch guest-physical address and calculated an end using the caller's length. If the required span fell inside the mapped GHCB Shared Buffer, the helper made sev_es.ghcb_sa point directly into the page. No new allocation was necessary.
For an outside address, the old helper allowed up to sixteen pages, called kvzalloc(len, GFP_KERNEL_ACCOUNT), and copied guest memory with kvm_read_guest(). It stored the new object in ghcb_sa, recorded ghcb_sa_len, and set flags describing whether the buffer needed synchronization and eventual release. This was a complete input-object lifecycle, not merely a pointer conversion.
kvzalloc() chooses backing memory according to size and allocator policy. The CNA's 24-byte case can land in a 32-byte accounted kmalloc cache such as the reported kmalloc-cg-32. Allocator alignment or a larger slab slot does not change the C object's legal length. Bytes beyond 24 are not PSC capacity just because the allocator reserves a wider physical slot internally.
During cleanup, sev_es_unmap_ghcb() examines the saved flags. If an external scratch object was modified, it writes back exactly ghcb_sa_len bytes to guest memory. If setup allocated the object, cleanup calls kvfree(), then clears the pointer. Asynchronous PSC handling can resume after a userspace exit before cleanup, giving a bad index more than one phase in which to influence behavior.
The in-page and outside routes return the same-looking ghcb_sa, but their extents come from different places. The GHCB layout defines the end of an in-page object. A caller-supplied length defined the old external allocation. If a consumer receives only a bare pointer and ignores ghcb_sa_len, those two objects collapse into the vague idea that “somewhere there is a PSC.”
Commit ebe4b2dc9cfb later made sev_es.ghcb_sa the authoritative PSC object instead of passing a second explicit buffer parameter through the consumer chain. This was more than signature cleanup. The setup result—address, effective length, synchronization state, and ownership—remained one coherent identity. The consumer had less opportunity to bypass or diverge from the validated object.
Commit 5867d7e202e0 changed the helper's input semantics from nominal length to minimum length. For an in-GHCB address, ghcb_sa_len became the distance from scratch to the end of the Shared Buffer. For an external object still allowed by an older protocol, allocation covered the consumer's minimum. Callers no longer pretended to know a total that the PSC protocol never supplied.

The tray is now a host-heap object with a clear start, a legal 24-byte extent, synchronization duties, and a release point. If the next consumer used ghcb_sa_len, the out-of-bounds chain would stop here. Old snp_begin_psc() instead consulted the header indices and a global constant. It received the tray but never picked up the calipers beside it.
The chain also explains why an allocator-wide maximum cannot solve the problem. This allocation was small and easily passed the sixteen-page ceiling. The defect was not that the guest requested too much memory. The consumer accessed beyond the amount requested. Resource-abuse limits and object-access bounds answer different questions.
Userspace completion also means that returning from one kernel helper is not the end of the object's work. KVM emits a batch, VMM userspace completes it, kernel code resumes, and only then does final cleanup occur. Ownership, effective length, and guest-writable state must remain coherent across that asynchronous boundary; they cannot survive as an informal assumption on one call stack.
The UAF classes in the public record reinforce the point. Out-of-range values can meet continuation or cleanup in different allocator states, making the same root cause visible at different lifecycle moments. We do not need to invent an exact heap-reuse narrative for every report to know that one fixed check at the first read would be an incomplete repair.
4 The gate sees ten below 253; entry two has already stepped into air
snp_begin_psc() is where the short tray actually crosses its edge. It first confirms that VMM userspace supports the required hypercall exit, interprets the start of ghcb_sa as a PSC header, and obtains the current and end indices. The old implementation rejected an end_entry reaching 253. It never calculated how many entries fit inside the ghcb_sa_len accepted for this exit.
Assume cur_entry = 0 and end_entry = 10. Ten is less than 253, so the old gate opens. Indices zero and one are processed from bytes genuinely present in the 24-byte allocation. When the index becomes two, address arithmetic reaches offset 24—the first byte beyond the object. Every subsequent step treats another eight-byte location owned by something else as a psc_entry.
The consumer does not unconditionally overwrite every eight-byte word. It parses a GFN, operation, reserved fields, and progress, checks whether a request is acceptable, attempts to merge adjacent ranges, and then may expose a KVM_HC_MAP_GPA_RANGE batch to userspace. What the neighboring slab bytes happen to look like decides whether processing fails early, returns one response, or reaches completion.
That response difference creates the information oracle. An out-of-object word that decodes as empty or invalid produces one visible outcome; a nonzero word that passes some validation can produce another. Repeated VMGEXIT requests can distinguish properties of neighboring host memory, which is why the CNA includes host-heap layout disclosure. This is a parser-constrained oracle, not a direct primitive that returns arbitrary host bytes.
If an out-of-object entry is considered valid and completed, __snp_complete_one_psc() updates its cur_page. Public analysis describes values constrained by page granularity—1 or 512—and the write targets a packed bitfield within the interpreted eight-byte entry. That can still corrupt a neighboring kernel object. It should not be restated as a freely chosen address and value.
4.1 Walking from header indices into the neighboring slab
The PSC header uses zero-based indices, and end_entry identifies the last item, not a byte length. A seemingly modest value of ten therefore requests eleven records. A check that compares only raw values can hide three later transformations: add one for the count, multiply by the element width, and add the header offset. The capacity repair compares indices to an entry count derived from bytes, keeping the units explicit.
The repaired calculation first relies on setup to prove that ghcb_sa_len covers the header. It then computes (ghcb_sa_len - sizeof(struct psc_hdr)) / sizeof(struct psc_entry). Twenty-four produces two; 2,032 produces 253; a pointer partway into the Shared Buffer produces the corresponding smaller result. If the end index is greater than or equal to that capacity, processing stops before the first array access.
The order of operations matters. Performing unsigned len - 8 when the object is shorter than the header can underflow into a huge value. Commit 2be54670bdc0 made setup guarantee the minimum header before capacity arithmetic. Spatial safety here is not one isolated if statement. It is an ordered set of preconditions in which each calculation depends on the previous result.
The old 253 ceiling remains useful as the architectural maximum and a bound on work per request. The repair does not discard the protocol limit; it makes the concrete object limit narrower whenever necessary. The effective permission is the smaller of the architecture-wide layout and this allocation's capacity. For a correctly positioned full-page PSC, the two values naturally coincide.
The loop can also aggregate adjacent GFNs with the same operation to reduce userspace round trips. Aggregation means KVM may read several entries before handing one range out, and state may persist across the kernel/userspace exchange. An allocation-specific bound must apply before prefetch, grouping, or completion. Waiting until a batch returns is already too late.
In the 24-byte case, the first bad access aligns exactly with the object end, so KASAN can report an eight-byte slab out-of-bounds operation at a useful stack. On a production kernel without red zones, the CPU does not fault merely because C object ownership ended. Execution may continue until neighboring metadata is interpreted or modified, surfacing later in a different function.
Absence of a production KASAN report is therefore not evidence of safety. KASAN is the laboratory instrument that paints object borders red. Operational triage must use running-version evidence and equivalent-fix review, while treating allocator corruption, general-protection faults, KVM SVM warnings, and unexplained host resets as correlating signals. Waiting for the exact test stack misses less direct manifestations.
Nor does every kmalloc-cg-32 complaint identify this CVE. The cache serves many kernel allocations, and corruption may first become visible when a victim object is used later. Meaningful attribution requires a vulnerable KVM build, an active and reachable SNP guest, relevant SEV/PSC frames or an eight-byte boundary pattern, and a timeline connecting the vCPU to the node.

cur_page update, not an arbitrary write.4.2 What the 73 KASAN reports prove—and what they do not
The 62 slab out-of-bounds reports map most directly to array traversal beyond the allocation. KASAN tracks object-validity shadow state; when an eight-byte read or write reaches a red zone, it records the access stack, allocation information, cache, and offset. Those findings turn a static possibility into observed memory-safety violations along the PSC path.
The seven slab UAF and four general UAF findings show that the defect can intersect object release, asynchronous continuation, or neighboring-object lifetimes. The public record does not give a complete reproducible allocator sequence for every report, and this report does not invent one. The defensible conclusion is that one root cause appears as both spatial and lifetime violations, putting host integrity and availability in scope.
Layout disclosure comes from visible parsing outcomes, not from KVM returning an eight-byte host word verbatim. The guest distinguishes whether a neighboring word behaves as zero, invalid, or sufficiently structured to progress. Repeated observations can assist inference about allocator state. That can weaken uncertainty for further exploitation, but it is not evidence that the guest can call read(host_address) across arbitrary memory.
A constrained update can still damage kernel objects. Flags, reference counts, lengths, and pointer fragments can be sensitive to a few bits. At the same time, a fixed or semi-fixed value does not prove that an attacker can reliably select a useful victim. Risk language needs both clauses: public evidence supports host-heap integrity loss; public evidence does not demonstrate a general, reliable arbitrary-code-execution technique.
Host denial of service is the most immediate operational outcome. An untrusted SNP guest that repeatedly drives L0 into allocator detection, a page fault, or delayed victim-object corruption can restart the whole physical node. Co-resident VMs lose service with it. An HA system that automatically reschedules the same guest to another vulnerable host may carry the failure through the placement pool.
Lack of in-the-wild evidence is not a reason to wait. Hypervisor exploitation leaves little conventional network telemetry, and the guest owns the shared bytes that form the request. Platform teams should decide from reachability and source state; a file hash or HTTP signature is unnecessary for remediation. Incident teams should ensure pstore, kdump, remote console, and scheduling history survive a host crash.
The reverse is also true: one unexplained reboot is not proof of exploitation. Production kernels may lack KASAN, and delayed corruption can fault far from KVM. A rigorous case note may say “consistent with the vulnerable path; direct attribution remains unconfirmed,” then preserve the dump, verify the booted build, enumerate SNP guests, and decide whether security isolation is warranted.
For a security owner, the actionable classification is host-kernel memory safety, not a guest application vulnerability. Virtualization platform, Linux kernel, and scheduler teams all participate in remediation. Asking tenants to update software inside their VMs does not alter L0's arch/x86/kvm/svm/sev.c, and installing a new host package does not replace the kernel already running until reboot.
5 Ten commits rebuild the workbench one instrument at a time
The principal fix added only four lines, but it was not the entire repair. Maintainers continued through neighboring users of the GHCB scratch helper. Which callers could pass zero? Which operations had tighter version 2 sizes? Should the helper receive a total length or a minimum requirement? Which pointer was authoritative for PSC? How should entry capacity be derived? Could the guest replace shared fields after the host checked them?
The result was a sequence of ten commits. The first maps to the CVE title; the next nine complete contracts exposed around the same interface. Read as a group, they form one chain: protocol version decides where scratch may reside; minimum length says what can be read first; the accepted address yields effective extent; an authoritative object reaches the consumer; that extent yields array capacity; and a stable observation keeps validation attached to use.
This sequence also explains why an enterprise backport may look larger than the CVE patch. A vendor can bundle the main repair with adjacent hardening in one kernel update or stage them according to branch risk. Acceptance should not stop after finding an equivalent of the four db3f2195d293 lines. At minimum, reviewers should establish that allocation-specific PSC capacity and stable shared-field handling are present or made unnecessary by a stronger design.
5.1 From the location gate to the effective-length ruler
| Order | Commit | Rule restored | Incorrect inheritance removed |
|---|---|---|---|
| 1 | db3f2195d293 | GHCB v2+ scratch must remain in the Shared Buffer | Legacy external scratch enters the new protocol |
| 2 | 1aa8a6dc7dac | Ignore zero-length MMIO | An absent payload reaches scratch setup |
| 3 | dcf1b2d4b056 | Limit v2+ MMIO to eight bytes | A neighboring consumer borrows excess scratch |
| 4 | 3988bd2723de | Ignore zero-length or zero-count Port I/O | Zero-sized requests cross the common helper |
| 5 | 2be54670bdc0 | PSC's minimum is sizeof(psc_hdr) | exit_info_2 becomes an invented total length |
| 6 | 5867d7e202e0 | In-page length runs from scratch to the work-area end | A middle pointer inherits all 2,032 bytes |
| 7 | f185e05dce6f | Warn when a caller supplies a zero minimum | A future consumer bypasses the minimum-object contract |
| 8 | ebe4b2dc9cfb | sev_es.ghcb_sa is authoritative | Prepared and consumed objects diverge |
| 9 | 121d88de56bc | Derive entry capacity from ghcb_sa_len | A short object borrows the page-wide 253 count |
| 10 | c8cc238093ca | Use READ_ONCE() for indices and entries | The guest swaps values between check and use |
The first commit places version semantics back at the address entrance. A v2+ request with an outside pointer fails before allocation or copy. Older behavior remains available only where the earlier specification allows it. This conditional tightening avoids deleting an old ABI indiscriminately while preventing compatibility from becoming a default escape hatch for a newer contract.
Commits two through four clean up neighboring MMIO and Port I/O callers. A zero length or count means that no payload needs preparation, so ignoring the operation is clearer than passing zero into a common helper. Version 2 MMIO has an eight-byte ceiling that belongs at the caller. The fewer semantic edge values a shared helper must interpret, the easier its state is to reason about.
The fifth change is the narrative pivot: PSC has no total length, so the caller can state only that an eight-byte header must exist. This converts an alleged remote fact into a local consumer requirement. Changing exit_info_2 can no longer dictate the size of a host allocation on behalf of PSC.
The sixth change derives the true in-page remainder. Old code validated a caller-sized span and saved that same span as object length. Repaired code stores the bytes from the current scratch pointer to the end of the Shared Buffer. The consumer can legally use all available entries after its minimum header, but it cannot run beyond the page or pretend a midpoint is the beginning.
Once minimum need and effective extent are separate, the API becomes composable. MMIO can request a fixed small structure. PSC can request only its header and then parse a flexible array from the remaining span. Future consumers must state what their first dereference requires. The seventh commit's warning behaves like an alarm at the gate when a new caller attempts to declare no minimum at all.

5.2 There is one authoritative object, so its capacity has one source
The eighth commit, ebe4b2dc9cfb, can look like deletion of a PSC function argument. Its actual purpose is to settle who speaks for the object. Setup has already stored the validated address, effective length, synchronization behavior, and ownership in svm->sev_es. Passing another explicit buffer lets future refactoring combine a checked context with an unchecked pointer. Reading ghcb_sa directly preserves one identity.
The ninth commit finally places the calipers on the tray. 121d88de56bc computes (ghcb_sa_len - sizeof(struct psc_hdr)) / sizeof(struct psc_entry) and requires end_entry to fall strictly below that count. A 24-byte object produces two entries, a full start-of-buffer layout produces 253, and an in-page pointer later in the region produces a correspondingly smaller number.
That check precedes every array access and works alongside index-order invariants such as current not exceeding end. Proving only that the end index is within capacity does not make an inverted current/end range coherent. Proving that two indices are ordered does not stop both from lying outside the object. Structured-input validation is an intersection of properties, not one broad “validated” flag.
The tenth commit repairs time. GHCB remains shared memory, so a guest can modify its header or entries while the host handles the exit. If the host loads end_entry once for the bound and again in the loop, the second value can be larger. If it validates an entry and then reloads guest-writable fields, it may act on content different from what passed the check.
READ_ONCE() is not a global lock and does not freeze the request indefinitely. It causes a definite load whose result code can carry through the corresponding decision. A private copy followed by full validation can be stronger for a more complex structure. In this implementation, stable loads of indices and entries remove repeated-read windows that let one request acquire two meanings.
Space and time must both be constrained. A perfect capacity formula still fails if the guest changes end_entry from one to ten after the check. A perfect snapshot still uses the wrong range if capacity remains fixed at 253. The repaired chain places both properties on the same authoritative object.
The sequence can be summarized as four gates. First, does the address belong to the location this protocol version permits? Second, is the minimum header present? Third, how many entries fit in the accepted remainder? Fourth, will validation and use operate on one observation of guest-writable fields? Each gate consumes the result of the previous one; none can be replaced by a context-free constant.
Those gates are more useful than ten hashes when reviewing a vendor branch. A vendor may squash commits, rename helpers, or replace READ_ONCE() with a private-copy design. Equivalent behavior is acceptable. If only the location rule is visible, reviewers should still ask what prevents a late in-page pointer from receiving the full page capacity. A security argument should state its conditions, not merely show a search hit.

Once the final snapshot clamp closes, the 24-byte external tray can no longer arise in version 2 PSC: an outside address fails first, and any in-page object receives capacity only from its remaining bytes. The analysis now moves from source to the running environment. The next task is not to search mechanically for 253 on every AMD system, but to identify which running hosts actually permit an untrusted SNP guest to reach this workbench.
6 Not every AMD host is waiting at this gate
An asset database may contain hundreds of AMD nodes. Public reachability requires several conditions to meet. The booted host kernel must contain the vulnerable KVM SVM/SEV behavior without an equivalent backport. The platform must enable and schedule SEV-SNP guests. A tenant or attacker must control a guest kernel or an equivalent GHCB-producing capability. VMM userspace must support the mapping exit needed by PSC. Removing one condition breaks this public path.
This is not an excuse to downgrade the defect. It is a way to spend a limited maintenance window on the combinations with the highest consequence. Multi-tenant confidential-computing pools come first because tenant control of the guest is part of the product and the host carries other tenants. An isolated lab with a fixed SNP image may be sequenced later, but it still needs repair: compromise of that guest kernel creates the same protocol endpoint.
A CPU advertising SEV-SNP does not prove that firmware, RMP initialization, the host command line, the KVM module, and the VMM all enable it. The inverse is also dangerous: a CMDB that says “no SNP” can be stale after firmware, image, or flavor changes. Hardware capability, current runtime state, and schedulable product capability need independent evidence.
6.1 Inventory from the booted kernel through every migration destination
The first host table should record the complete running package build, boot time, and boot ID. A package manager saying that a fixed kernel is installed does not mean the node has rebooted into it. A new vmlinuz on disk does not replace the sev.c executing in memory. Acceptance evidence must identify the running image and map it to a vendor advisory or an equivalent source backport.
The second group describes platform capability: CPU generation, firmware, SEV/SNP platform state, kvm_amd parameters, kernel command line, QEMU and libvirt builds, and whether the VMM enables the required KVM exit. None of this requires reading tenant-private memory. It establishes whether the host has opened the control path that matters.
The third group describes each VM. Preserve the domain XML, QMP state, or orchestrator specification that identifies an SEV-SNP guest object, launch policy, and machine type. Include active VMs, stopped templates, autoscaling images, and disaster-recovery copies. A host with no SNP guest at this instant may become exposed on the next placement decision.
The fourth group is trust. Which tenants upload custom kernels? Which appliances are maintained by a third party? Which management VMs can load kernel modules or access low-level virtualization? Which company-owned workloads expose high-risk applications? “Internal” is not a permanent trust guarantee. Once an attacker reaches kernel control inside the guest, KVM faces a malicious protocol endpoint.
The fifth group is every possible landing point: live-migration allowlists, HA failover groups, maintenance evacuation targets, autoscaling pools, cold-migration templates, and reserve capacity. A protected guest can move from a repaired host to an unrepaired one. During a rollout, a scheduler can also send it back to a node that has drained and installed a package but not rebooted.
Node state should distinguish at least: repaired and rebooted; installed but not rebooted; version unknown; unrepaired but removed from SNP placement; and unrepaired while still eligible. Calling every transitional node “maintenance” leaves scheduler and security teams with different interpretations. A machine can safely run ordinary workloads while remaining ineligible for untrusted SNP guests.
Temporary mitigation should break a condition in the path. Stop new SNP placements on unverified nodes, migrate or stop the highest-risk protected guests, suspend creation of affected confidential flavors, and narrow migration destinations. Whether a live node can safely disable an underlying capability depends on platform design. Prefer explicit placement controls over assuming that writing one module parameter revokes state from running guests.
Network isolation contributes little once the attacker controls a guest kernel. It can reduce the chance of an application compromise that provides that prerequisite, but it does not repair KVM's parser. Delaying the host patch while adding firewall rules merely returns the threat model to one that already assumes guest control.
The final inventory should answer one sentence for a given point in time: which controllable SNP guest could land on which host running which kernel? If CMDB, VM inventory, and scheduling policy cannot be joined, this CVE has also revealed an asset-governance gap. The relationship should become a durable query after remediation, not a one-use spreadsheet.
6.2 Four upstream fixed lines are a starting point; product packages are the destination
The Linux CNA maps affected upstream releases from 6.10 and publishes four fixed floors: 6.12.93 for 6.12.y, 6.18.35 for 6.18.y, 7.0.12 for 7.0.y, and 7.1 in mainline. The stable objects are bf9ba093fbb8, c9b4198fbc6e, and b328ede59ac3; the mainline object is db3f2195d293.
| Upstream line | First officially fixed release | Mapped fix object | Operational meaning |
|---|---|---|---|
| 6.12.y | 6.12.93 | bf9ba093fbb83c0c9a3dedd50efec29424eca2fc | Below this point, require vendor backport evidence |
| 6.18.y | 6.18.35 | c9b4198fbc6ed99a9da4bee9f74bb730f926c9ae | Installation still requires a verified reboot |
| 7.0.y | 7.0.12 | b328ede59ac34e7998e1eee5e5f0cc26c2a91846 | Migration targets need equivalent or stronger repair |
| Mainline | 7.1 | db3f2195d29344a3cf1e9dd9ab7f21ced7308cf7 | Use as the behavioral and source-review anchor |
A fixed floor is not a string comparator. Ubuntu, RHEL, SLES, and other vendors backport changes onto ABI and package versions that may look older than upstream. A custom or appliance kernel may look newer while omitting one change. Strong evidence combines the official product status and exact package with source, changelog, or vendor confirmation of equivalent behavior.
In the July 17, 2026 review, Ubuntu classified the CVE as Medium and showed different states by kernel package. Ubuntu 26.04 generic and several cloud lines remained vulnerable or work in progress; some Ubuntu 24.04 6.17 and 7.0 HWE lines were also being handled. Older generic baselines for 24.04, 22.04, and 20.04 were listed as not affected. Those entries will change as packages ship.
An older generic line can be not affected because its baseline never incorporated the vulnerable GHCB v2 design, not because old kernels are universally safer. HWE and cloud kernels under the same distribution release can use newer bases and receive a different status. Inventory needs the package flavor, not merely “Ubuntu 24.04.”
At review time, Red Hat's security-data entry classified the issue as Important, supplied CVSS v3 7.0 and CWE-787, and separated package states by product family. SUSE provides a permanent CVE entry as well. Dynamic product tables can change after publication, so this report links to the live sources instead of freezing a soon-stale SKU matrix.
The L0 host must reboot into the fixed build. Whether a live-patch product safely covers this stateful KVM path requires explicit vendor support; it cannot be assumed. Record the boot ID, package, and time at which the node regains eligibility. Otherwise an “installed” host can accept a protected guest while still executing vulnerable memory.
Updating the guest kernel does not repair L0's arch/x86/kvm/svm/sev.c. Guest updates can reduce the likelihood that an attacker gains guest-kernel control, and can repair bugs in the guest's own GHCB code, but the malicious-guest model does not depend on voluntary cooperation. Host-platform remediation remains independent.
A firmware update is not a substitute for this Linux source repair unless a vendor explicitly states that it removes reachability, for example by disabling the affected capability. Firmware, QEMU, and kernel updates may share one maintenance window, but the acceptance record should identify which control closes the CVE so a future rollback does not silently restore it.
7 When a host anomaly appears, join crash evidence to placement history
If an exposed node develops an unexplained reboot, panic, or kernel-heap anomaly, the first action is not to replay a malformed PSC against production or erase every disk. It is to preserve evidence before another restart, automatic evacuation, or rebuild changes the scene: pstore, kdump, remote serial output, BMC events, host journal, QEMU and libvirt logs, orchestration decisions, and migration records.
Responders identify the boot ID and exact kernel that were running at the crash, then decide whether the build predates a vendor fix. They enumerate SNP guests, vCPU placement, and migrations in the surrounding time window. If no reachable SNP guest existed, or the executing branch already had all four equivalent protections, this CVE becomes less plausible. If code state and reachability align, the memory-safety evidence warrants security investigation.
An ideal laboratory KASAN trace may include setup_vmgexit_scratch, snp_begin_psc, __snp_complete_one_psc, or sev_handle_vmgexit, together with an eight-byte access beyond a small object. Production builds usually lack KASAN. The eventual fault may appear in an allocator, a victim object, or cleanup. Investigation follows causal proximity; one literal stack is not required.
Raw VMGEXIT volume is a poor indicator. Protected guests use GHCB routinely, and workload changes can legitimately raise exit counts. Without exit type, return behavior, vCPU identity, and a workload baseline, “many exits” creates more noise than evidence. Running code, the PSC path, a compatible memory-corruption shape, and guest placement are stronger together.
7.1 One crash dump cannot convict; five evidence classes can correct one another
The first class is code evidence. Preserve the executing package, symbols, configuration, and vendor build. Determine whether the binary enforces v2 location, minimum header, effective remainder, allocation-specific capacity, and stable shared reads. If version-string inference is wrong, every later forensic conclusion rests on a false premise.
The second class is memory-safety evidence. KASAN can identify the access width and offset, object cache, allocation stack, and release stack. A production dump can still reveal a fault address, slab metadata, poisoning patterns, and neighboring object types. A 24-byte request with an eight-byte step resembles the public example, but the path does not require every run to preserve the same allocator appearance.
The third class is virtualization evidence. Establish whether the faulting vCPU belonged to an SNP guest, whether VMM userspace supported the required hypercall exit, and whether nearby records show SEV/PSC handling or KVM SVM warnings. When ordinary and SNP guests share a node, the first ordinary VM to report an outage is not necessarily the source of the host failure.
The fourth class is identity and control. Who administered the guest? Could it boot a custom kernel? Did it show earlier compromise evidence? Where did its image originate, and were management credentials abused? Confidential computing intentionally limits host visibility into private guest state. Collection must respect tenant, legal, and incident-response agreements; missing guest telemetry proves neither innocence nor malice.
The fifth class is scheduling history. A guest may have migrated in minutes before the crash or produced a similar anomaly on a prior node. HA may move it to another vulnerable host immediately after the reboot. Preserve placement decisions, migration identifiers, source and destination nodes, and the vCPU run interval. Those records turn an isolated panic into a pool-wide timeline.
All five classes may not be complete. A rigorous report can separate confirmed, strongly consistent, unconfirmed, and excluded facts: the host definitely ran an affected build; an eight-byte out-of-range trace is strongly consistent; malicious guest intent is unconfirmed; a disk-hardware event is excluded. Leaving an unknown visible is more useful than filling it with the CVE title.
If the dump shows neighboring kernel objects were corrupted, preserve the node and rebuild it or apply a stronger integrity response. A simple reboot and return to service is insufficient. Heap corruption may affect state that did not immediately fault. A reboot clears volatile memory but does not answer whether an attacker reached another persistence mechanism. Evidence and system value determine the depth of containment.
If the only finding is version exposure and no anomaly exists, remediation can remain a preventive vulnerability change. There is no need to manufacture a suspected-intrusion label. Conversely, a host memory-safety event consistent with this path can justify escalation even without a conventional IOC. Vulnerability management and incident response share facts but can reach distinct case states.

7.2 Keep protected guests in the verified pool while L0 rolls forward
Containment begins with placement. Remove version-unknown, installed-but-not-rebooted, and known-unrepaired nodes from SNP destinations. Block new creation, migration, HA, and autoscaling onto them. If capacity is tight, reserve the verified pool first for high-risk untrusted tenants and handle controlled workloads through the business continuity plan. Do not let an emergency scheduler silently defeat the security partition.
- Freeze unsafe destinations. Remove unknown and unrepaired nodes from SNP scheduling, migration, HA, and autoscaling targets.
- Preserve anomalous evidence. Retain dumps, pstore, BMC, VMM, and placement history with the relevant guest and vCPU identities.
- Install and boot the fixed L0. Use the vendor-qualified package, reboot, and verify the running build and boot ID.
- Review the four source properties. Confirm v2 location, minimum header, effective capacity, and stable snapshots or stronger equivalents.
- Exercise normal SNP service. Test attestation, PSC, I/O, pause/resume, and controlled migration without a destructive production reproducer.
- Reopen by evidence. Restore placement and automatic failover only when active nodes, destinations, and reserve capacity meet the same standard.
A rollback plan must obey the same security premise. If the new kernel causes a functional regression, do not simply boot the vulnerable build while leaving SNP placement open. Temporarily suspend the confidential flavor, keep guests on other repaired nodes, or use another vendor-qualified fixed build. An availability rollback should not silently restore the vulnerability condition.
Cluster capacity determines whether this plan can finish. Count migratable headroom, huge-page and NUMA constraints, attestation-service dependencies, and downtime budgets before the first drain. Rollouts often fail at the last mile when insufficient capacity forces an unverified host back into service. Scheduling resources are part of the security change, not a separate afterthought.
For legacy nodes that cannot be patched immediately, the strongest temporary measure is to stop hosting untrusted SNP guests and remove the nodes from every automatic destination. Additional guest monitoring, network restrictions, or tenant promises do not change the host's ability to misparse a hostile GHCB. Mitigation must break the technical path; policy text is not a technical control.
After the rollout, regenerate the guest-to-destination relationship from scratch. Disaster-recovery nodes, powered-off reserve machines, and new autoscaling images are common omissions. They carry no workload on an ordinary day but become the only landing point during a failure. Closure covers the future-reachable pool, not merely the current online list.

When the last reserve node passes, the team does not need to claim it has proven exploitation, and it does not need a traditional IOC before reducing risk. It can record two precise conclusions: the production path is now closed by executing repairs, while whether an existing host anomaly resulted from malicious exploitation remains classified according to available evidence.
Tenant communication should keep the same precision. For a preventive change, describe the host-kernel upgrade and migration window for protected VMs. If evidence supports a memory-safety event, use contractual incident procedures to state the affected interval, the evidence, and remaining unknowns. Saying merely that “SEV was broken” creates expectations unsupported by the technical record.
The final change record should include the vendor advisory, package or build identity, boot ID, four-property source assessment, functional results, node return time, and every migration destination. Those artifacts become a regression baseline for the next kernel rebase or appliance rebuild, preventing the repair from disappearing inside a custom patch stack.
If the fleet expands from a golden image, update the image and node-provisioning pipeline as part of closure. Repairing current hosts alone allows the next autoscaled node to reintroduce vulnerable code. Image version, signature, and an admission rule should stop a build below the qualified fixed level from joining the SNP pool.
Multi-region and edge deployments need one global eligibility view. During regional pressure, disaster recovery can select destinations that do not normally participate in the primary pool. Security qualification should follow image and node evidence; a manually maintained regional list can become stale before the next failover.
8 When the hatch reopens, 253 no longer hides the tray in front of it
When repaired KVM receives the same PSC conditions, the path changes earlier. A GHCB v2 sw_scratch outside the page is rejected before allocation. An in-page address must cover the minimum header, and setup measures from the actual pointer to the Shared Buffer end. PSC derives entry capacity from the saved ghcb_sa_len and processes stable observations of indices and entries.
Twenty-four bytes is no longer a “protocol length” that a guest can submit at will. A legitimate version 2 PSC should not create that external host object at all. In any compatible or test context where a short object remains possible, the capacity equation still yields only two entries. The 253 figure remains correct, but only for a complete work area beginning at its proper start.
A good repair chain has this quality: it does not sprinkle a mysterious constant before every dangerous index. Each stage inherits a fact proved by the prior stage. Version selects the location rule. Accepted location establishes object extent. Object extent yields array capacity. A snapshot carries validated values into use. This causal ordering survives future refactoring better than the number of lines in one patch.
8.1 A regression matrix proves what is accepted and what is refused
The first case is a version 2 pointer outside the GHCB Shared Buffer. On an isolated SNP test host, supply the prohibited location and expect a protocol failure before external allocation or guest copy. The test should observe the rejection point, not merely declare success because the machine did not crash. It directly covers the principal CVE fix.
The second case places scratch at the start of the Shared Buffer with a complete header and a small valid range. The third increases entries to the full largest valid layout, then tries the first index beyond calculated capacity. Together they expose off-by-one mistakes and confusion between entry count and the last zero-based index.
The fourth case moves scratch progressively toward the work-area end. An end_entry legal at the beginning must eventually fail at a later start. Test one-entry movement, exactly header-only space, and one byte less than a header. This catches an incomplete backport that still records all 2,032 bytes for a pointer in the middle.
The fifth case covers zero values and neighboring consumers. Zero-length MMIO, zero-length or zero-count Port I/O, and oversized v2 MMIO should follow the new caller semantics. The common helper should never receive a consumer that declares no minimum structure. This gives the adjacent hardening commits observable acceptance criteria.
The sixth case changes shared fields at controlled synchronization points. It does not race a production host into corruption. A test guest modifies cur_entry, end_entry, and entry contents while the harness observes whether KVM uses one stable value for a checked processing step. READ_ONCE() or a stronger private-copy design should explain the result.
The seventh case covers userspace completion. A legitimate PSC is split into adjacent ranges; VMM userspace returns success, partial progress, and error conditions; KVM updates progress and cleans up. Every continuation must use the same authoritative scratch identity and effective extent. Protecting only the first batch while a later continuation returns to a bare pointer would be incomplete.
The eighth case exercises the product: SNP guest launch and attestation, private/shared transitions, I/O, stress, pause/resume, live migration, and shutdown. A kernel that rejects every negative case but cannot run a supported protected guest is not a deliverable repair. Functional evidence belongs beside the kernel and VMM build in the change record.
| Regression target | Accept | Reject or block | Invariant proved |
|---|---|---|---|
| Location | v2 data in the Shared Buffer | v2 external scratch | Protocol version selects legal address space |
| Minimum size | Complete psc_hdr | Fewer than eight bytes | The header exists before first dereference |
| Capacity | Last index below actual entry count | First index at or beyond capacity | The array obeys this object |
| In-page offset | Batch shrinks with remaining space | Midpoint borrows all 2,032 bytes | Length runs from accepted start to end |
| Shared time | One stable batch snapshot | Post-check mutation expands work | The checked value is the used value |
| Operations | Rebooted and verified nodes | Installed-only or unknown nodes enter pool | Running evidence controls placement |
Run these cases on an isolated test system. There is no reason to recreate host memory corruption on production. An instrumented guest, KASAN or KFENCE staging kernel, VMM harness, and tracepoints can establish where malformed input is refused. Production needs normal functional testing and non-destructive build/configuration evidence.
When vendor source is unavailable, behavioral tests still provide useful evidence but should not be overstated. Record their coverage, return behavior, kernel package, and support case. Ask the vendor to confirm continuation and shared-field semantics where a black-box test cannot. An audit conclusion can state its confidence without forcing a choice between “fully proven” and “nothing known.”
8.2 Six engineering rules left by one short tray
First: a protocol version is a parsing rule, not a display field. Negotiating GHCB v2 changed where scratch could reside. Legacy capability had to remain confined to legacy branches. Every protocol upgrade should enumerate semantics that were added, removed, or narrowed—not merely raise max_version.
Second: minimum requirement and actual extent are different values. A caller can say that it needs an eight-byte header. The accepted mapping or allocation determines how many bytes exist. Combining remote assertion, consumer need, and local object fact in one len allows an integer to change identity silently as it crosses functions.
Third: an array limit belongs to a concrete object. The figure 253 describes the GHCB Shared Buffer layout, not every psc_entry *. The same warning applies to maximum packet sizes, ring descriptor counts, and file-format record ceilings. Establish the current backing object before translating a protocol maximum into a memory access.
Fourth: one source must authoritatively identify an object. If address, length, synchronization, and release responsibility travel through unrelated parameters, refactoring can create a combination that was never validated. Binding those facts in one context and requiring consumers to use it reduces “checked A, used B” failures.
Fifth: shared memory is untrusted in space and unstable in time. The remote side can change a field after it passes a range check. A single load, private copy, lock, or protocol state machine must keep validated data attached to use. A spatially correct formula applied to a moving index is correct only for an instant.
Sixth: remediation ends in runtime and scheduling, not in a source repository. An upstream commit enters a vendor package; the package reaches the node; the node reboots; normal SNP service passes; every migration and reserve destination becomes qualified. Any intermediate state deserves an ineligible placement label.
The pattern is not confined to confidential computing. Virtio descriptors, vhost rings, GPU command buffers, firmware mailboxes, network packets with variable tails, and io_uring shared queues can all suffer from a layout-wide maximum applied to a short object or from values replaced between check and use. The specific CVE function names will age; the design pressure will recur.
The repair neither enlarges the 24-byte object nor lowers the complete layout's 253-entry ceiling. It restores every number's subject: the page is 4,096 bytes; the Shared Buffer is 2,032; the header and each entry are eight; the full layout holds at most 253 entries; a 24-byte object holds two. A loop can travel only as far as the object in front of it.
If a future refactor passes the PSC buffer as a bare pointer again, the regression matrix should fail immediately. If another protocol version permits a new scratch location, design review must identify a new extent source. If an autoscaling image falls behind, admission must reject the node. A strong vulnerability review does not merely remember one function name; it makes the same category of mistake harder to reintroduce.
CVE-2026-53360 is not a mysterious cryptographic defeat. It exposes a plain systems truth: a receiver cannot let a remote endpoint invent the length of a local object, and the largest room cannot vouch for the tray currently on the desk. Keep that truth continuous through address, capacity, time, and operations, and the service hatch can remain open without trusting the guest.
Research record
9Evidence, objects, and sources
The material below preserves the identifiers and references used in this report.
9.1Research objects
Products, actors, techniques, affected objects, and control points discussed in the report.
Linux KVM SEV-SNP GHCB scratch host-heap out-of-bounds access
Affected KVM AMD SEV VMGEXIT implementation
Prepared an external GHCB v2 scratch object from an invented length
Consumed a short PSC object under a page-wide entry ceiling
Mainline GHCB v2+ in-buffer scratch requirement
PSC capacity derived from effective scratch length
Stable reads of guest-writable PSC indices and entries
Variable-payload work area within the GHCB page
PSC capacity must come from the accepted object
9.2Event chronology
- GHCB v2 became configurable per guest
Commit 4af663c2f64a allowed KVM to configure GHCB protocol version 2; the Linux CNA maps affected upstream releases from 6.10.
- The mainline repair was completed
db3f2195d293 required version 2 and newer scratch data to reside in the GHCB Shared Buffer; adjacent commits then completed length, capacity, and snapshot semantics.
- The Linux CNA record was published
CVE-2026-53360 documented host-heap corruption, layout disclosure, 73 KASAN reports, and four official fixed release lines.
- SOSEC completed the source reconstruction
SOSEC reconciled the CNA, specification, vulnerable parent, ten-commit repair, stable branches, and vendor status.
9.3Sources and material
- CVE.org: official CVE-2026-53360 recordhttps://www.cve.org/CVERecord?id=CVE-2026-53360
- CVEProject: raw Linux CNA JSONhttps://raw.githubusercontent.com/CVEProject/cvelistV5/main/cves/2026/53xxx/CVE-2026-53360.json
- AMD: GHCB specification 56421 Rev. 2.04https://docs.amd.com/api/khub/documents/oJly8EPzLO1Bt7ncrkCytw/content
- Linux documentation: KVM AMD memory encryptionhttps://docs.kernel.org/virt/kvm/x86/amd-memory-encryption.html
- Linux source: GHCB page and 2,032-byte Shared Buffer layouthttps://codebrowser.dev/linux/linux/arch/x86/include/asm/svm.h.html
- Linux source: PSC header, entry, and count constantshttps://codebrowser.dev/linux/linux/arch/x86/include/asm/sev-common.h.html
- Linux source: current KVM SEV VMGEXIT implementationhttps://codebrowser.dev/linux/linux/arch/x86/kvm/svm/sev.c.html
- Linux: per-guest GHCB protocol version 4af663c2f64ahttps://git.kernel.org/stable/c/4af663c2f64a8d252e690c60cf8b8abf22dc2951
- Linux: Page State Change VMGEXIT support 9b54e248d264https://git.kernel.org/stable/c/9b54e248d2644be71cb394eb85f31ad99e023a05
- Linux: GHCB v2+ in-buffer scratch fix db3f2195d293https://git.kernel.org/stable/c/db3f2195d29344a3cf1e9dd9ab7f21ced7308cf7
- Linux: zero-length MMIO hardening 1aa8a6dc7dachttps://git.kernel.org/stable/c/1aa8a6dc7dac
- Linux: GHCB v2+ MMIO length ceiling dcf1b2d4b056https://git.kernel.org/stable/c/dcf1b2d4b056
- Linux: zero-length or zero-count Port I/O hardening 3988bd2723dehttps://git.kernel.org/stable/c/3988bd2723de
- Linux: PSC minimum-header correction 2be54670bdc0https://git.kernel.org/stable/c/2be54670bdc0
- Linux: effective in-GHCB scratch length 5867d7e202e0https://git.kernel.org/stable/c/5867d7e202e0
- Linux: zero minimum-length caller warning f185e05dce6fhttps://git.kernel.org/stable/c/f185e05dce6f
- Linux: authoritative scratch object ebe4b2dc9cfbhttps://git.kernel.org/stable/c/ebe4b2dc9cfb
- Linux: allocation-specific PSC capacity 121d88de56bchttps://git.kernel.org/stable/c/121d88de56bc5c0ba0ce2f6381af67f948a7e7c1
- Linux: PSC shared-field snapshot hardening c8cc238093cahttps://git.kernel.org/stable/c/c8cc238093ca6c99267032f6cfe78f59389f3157
- Linux 6.12.y stable fix bf9ba093fbb8https://git.kernel.org/stable/c/bf9ba093fbb83c0c9a3dedd50efec29424eca2fc
- Linux 6.18.y stable fix c9b4198fbc6ehttps://git.kernel.org/stable/c/c9b4198fbc6ed99a9da4bee9f74bb730f926c9ae
- Linux 7.0.y stable fix b328ede59ac3https://git.kernel.org/stable/c/b328ede59ac34e7998e1eee5e5f0cc26c2a91846
- Ubuntu: CVE-2026-53360 package statushttps://ubuntu.com/security/CVE-2026-53360
- Red Hat: CVE-2026-53360 product-status entry pointhttps://access.redhat.com/security/cve/CVE-2026-53360
- SUSE: CVE-2026-53360 product-status entry pointhttps://www.suse.com/security/cve/CVE-2026-53360.html