Xenbleed: Xen Guest to Dom0 Escape CodeAnt AI Security Research Team disclosed Xenbleed, a guest-to-dom0 escape in the Xen hypervisor that researcher Hemanshu Baviskar demonstrated on XenServer 9.0.0 running Xen 4.21.2-3, allowing kernel-level code in a guest to read and write dom0 memory and execute instructions in the dom0 Linux kernel. The flaw traces to a January 29, 2015 grant-copy performance commit that skipped a permission check, leaving Xen 4.6 and later affected for 4,296 days until the Xen Project published fixes as XSA-523 on November 3, 2026, after a two-week embargo; CodeAnt AI rated the two CVEs 9.3 Critical (CVSS v3.1) and 9.4 Critical (v4.0). In January 2015, a patch made guest networking on Xen up to 25 percent faster. It also taught the hypervisor to skip a permission check, but only when a guest asked in one specific order. That door stayed open for 4,296 days. Kernel code in any guest on Xen 4.6 or later could use it to read and write dom0, the privileged VM that runs the host. The CodeAnt AI Security Research Team independently found this flaw, which we call Xenbleed. We built a working guest-to-dom0 escape on XenServer 9 and reported it to Xen, which shipped the fix on November 3, 2026, as XSA-523. Below is the full chain, and why the same bug is probably in your code too. How CodeAnt AI found it, and what happened next Hemanshu Baviskar, a security researcher at CodeAnt AI, independently found the flaw in Xen's grant-copy code and turned it into a working exploit chain on XenServer 9.0.0 running Xen 4.21.2-3, a current release. He proved every step before we wrote a word: a guest reads dom0 memory, writes dom0 memory, then runs instructions inside dom0's kernel. On October 13, the CodeAnt AI Security Research Team sent the full package to the Xen Project security team and to Cloud Software Group, which makes XenServer: root cause, step-by-step reproduction, proof-of-concept source, demonstrated impact and a proposed fix. Our report hit a spam filter on the way in. Xen's security team dug it out anyway and replied the next day: the issue was already known to them, a fix was being prepared, and they offered us independent discovery credit. We accepted, offered to help test the fix, and kept the details private until users could patch. | Date UTC | What happened | |---|---| | Oct 13, 2026 | We report a guest-to-dom0 escape with dom0 kernel code execution to the Xen Project security team and Cloud Software Group, with root cause, reproduction steps, PoC source and a proposed fix. | | Oct 13, 2026 | Cloud Software Group’s security response team confirms receipt and starts its analysis. | | Oct 14, 2026 | Xen’s security team confirms the issue is known, says a fix is in progress and offers independent discovery credit. | | Oct 14, 2026 | We confirm credit as the CodeAnt AI Security Research Team and offer to test the fix. | | Nov 3, 2026 | Xen publishes XSA-523 https://xenbits.xen.org/xsa/advisory-523.html after a two-week embargo. Both patches carry a Reported-by line for CodeAnt AI Security Research. | | Nov 4, 2026 | We publish this write-up and our CVSS assessment for both CVEs: 9.3 Critical v3.1 and 9.4 Critical v4.0 . | TL;DR - Bug: Xen reused security-sensitive state across grant-copy batch entries without fully validating how that state had been authorized. - Access: The attacker starts with kernel-level control inside a Xen guest. - Impact: Unauthorized read/write of other domains, including dom0. On XenServer 9, we demonstrated controlled instruction execution in the dom0 Linux kernel. - Status: Xen 4.6 onward is affected. Xen published fixes in XSA-523 https://xenbits.xen.org/xsa/advisory-523.html on November 3, 2026. A guest with kernel-level control could turn Xen’s normal memory-copy interface into read and write access to XenServer’s management domain, dom0. The authorization check worked for an isolated request but could be skipped after Xen reused state from an earlier entry in the same batch. The flaw dates to a grant-copy performance change committed on January 29, 2015 https://github.com/xen-project/xen/commit/d28f42f2703e483116bafd2b0b76a32af67d83ad . It remained in Xen for more than eleven years. We independently found the same flaw in 2026, reproduced the boundary crossing on XenServer 9, and used the memory primitive on an exact kernel image to execute a short, reversible instruction sequence in the dom0 Linux kernel. Xen in one diagram Xen is a type-1 hypervisor https://handbook.xenproject.org/users/introduction.html . It runs directly on the hardware and divides the machine into isolated virtual machines called domains. A conventional Xen host starts a privileged control domain called dom0 and can run one or more ordinary workload domains called domU . Dom0 is a Linux VM with extra authority. On XenServer it runs management services, creates and destroys guests, and provides much of their storage and network I/O. A domU does not sit “inside” dom0; both run beside each other on Xen. The privilege difference is what makes dom0 memory a meaningful boundary. Guests ask Xen to perform privileged operations through hypercalls . Grant tables are the memory-sharing system behind many Xen I/O paths: a domain creates a small permission record, called a grant, that tells Xen which peer may access one of its pages and whether the peer may read or write it. The demonstrated boundary crossing is domU → Xen grant-copy code → dom0 memory. The proof reached the dom0 Linux kernel, not Xen’s own code pages. One copy, two address modes GNTTABOP copy asks Xen to copy bytes between a source page and a destination page. The caller may send many operations in one hypercall. Each source and destination carries a domain ID, a page offset, a value, and a flag that decides how Xen interprets that value. A foreign domain is any domain other than the caller. A guest frame number identifies a page in the named domain’s address space; this post calls the flag-clear form direct-frame mode . | Mode | Meaning | Authorization rule | |---|---|---| | gref flag set | The value is a grant reference created by the page owner. | Required when accessing a foreign domain. | | gref flag clear | The value is a guest frame number resolved in the named domain. | Allowed only for DOMID SELF , meaning the caller’s own memory. | The public ABI says that foreign-domain operands must use grant references. The vulnerable implementation contained the same rule in a comment and a guard: xen/common/grant table.c:2926 Vulnerable source at commit e4da1829 https://github.com/xen-project/xen/blob/e4da1829737fb575133d1f79d9ad987000891649/xen/common/grant table.c L2926-L2934 . The rule is correct; its position is not. Batches often touch adjacent ranges in the same page, so Xen caches locked domains and page mappings between entries. That optimization avoids repeating the expensive setup for every small copy. The cache becomes security-sensitive because it holds objects that were reached through an authorization decision. The cache remembered too little XSA-523 https://xenbits.xen.org/xsa/advisory-523.html covers two defects introduced into this cache. Both came from the same design mistake: Xen decided that cached state was reusable without checking every fact that made the state valid. | CVE | Incomplete identity | Consequence | |---|---|---| | CVE-2026-102703 | A cached page buffer was compared by its numeric value without first requiring the old and new address modes to match. | A grant reference could be mistaken for a frame number. | | CVE-2026-102704 | The foreign direct-frame check ran only when Xen locked a domain, while later entries could reuse that lock. | A same-domain-ID entry could switch from grant mode to direct-frame mode and skip the check. | Our reproduced chain exercises CVE-2026-102704 . The domain-cache decision compared the source and destination domain IDs, but did not compare the grant-reference flags: xen/common/grant table.c:3127 The vulnerable cache key contained the two domain IDs, but not the bits that selected grant-reference or direct-frame mode. View the source. https://github.com/xen-project/xen/blob/e4da1829737fb575133d1f79d9ad987000891649/xen/common/grant table.c L3127-L3136 An entry that failed grant lookup released its page buffers but kept the locked domains for the next entry. When the next operation kept the same domain IDs, Xen skipped gnttab copy lock domain , including its DOMID SELF check. The direct-frame branch then resolved the selected frame inside the cached foreign domain. Xen already enforced the correct rule when the cache was empty. The batch cache created another route to the operation without repeating that check. CVE-2026-102703 affected a second layer of the cache. gnttab copy buf valid needed to require b- have grant == has gref before accepting a cached numeric match. The official patch https://xenbits.xen.org/xsa/xsa523-1.patch adds that mode check. Our invalid-grant primer chain exercises CVE-2026-102704; we did not independently exercise CVE-2026-102703. Two entries, one skipped check The read primitive fits in one two-entry GNTTABOP copy batch. The attacker deliberately makes the first grant lookup fail after Xen caches the domains. That discards the page state while preserving the domain cache. The second entry keeps the domain IDs and changes the source operand from a grant reference to a direct frame. 1. Entry 0: Name dom0 as a grant-reference source, use the invalid reference ~0U , and copy toward a scratch page in DOMID SELF . 2. Cached state: Xen locks dom0 and the caller’s domain. Grant lookup fails with GNTST bad gntref -3 . Page state is released; the domain locks remain cached. 3. Entry 1: Keep both domain IDs, clear the source grant flag, and put a selected dom0 guest frame number in the same operand field. 4. Result: The domain cache matches, so the lock-time permission check does not run. Xen resolves the frame in dom0 and copies its bytes into the guest. Status: 0 . The negative control makes the bypass visible. A standalone foreign direct-frame copy starts with an empty cache and returns GNTST permission denied -8 . In our proof, the same direct-frame operation succeeded when it followed the invalid-grant primer in one batch. Reversing source and destination turns the read into a write. Hypercall preemption can discard the cache between entries, so the targeted probes retry. No timing race is required; our fresh XenServer run succeeded on the first batch. From dom0 memory to dom0 instructions A marker page with no grant We first tested the primitive against a private marker page allocated by a fixture in dom0. The fixture created no grant and disclosed neither its address nor its frame number. The guest scanned candidate dom0 frames, found XEN COPY SECRET A , and changed it to XEN COPY SECRET B . Dom0 independently reported the new value. | Test | Observed evidence | Result | |---|---|---| | Standalone read | status=-8 | DENIED | | Two-entry read | first status=-3 second status=0 ; buffer contained XEN COPY SECRET A | READ | | Two-entry write | first status=-3 second status=0 ; dom0 reported XEN COPY SECRET B | WRITE | | Guest types | A PVH guest and a XenServer-managed HVM guest, covering two Xen x86 guest modes, reached the same hypercall path | REPRODUCED | A reversible kernel-control proof For code execution, we constrained the proof to the exact XenServer dom0 kernel image we had hashed and ran Xen with one pCPU. SMP reliability was not measured. From the guest, the read primitive located the page containing xen evtchn do upcall , dom0’s Xen event-channel interrupt handler, using a 28-byte signature, an expected five-byte entry sequence, and a 13-byte NOP region. The guest wrote a 13-byte stub into that NOP region. The stub saved registers, called the kernel’s existing dump stack , restored registers, and returned. A five-byte patch redirected the event-handler entry to the stub. We sent a XenStore event, an ordinary host/guest notification, observed the injected call in dom0’s kernel trace, and restored both modified regions with read-back verification. Closing notes Xenbleed began with a grant-copy performance optimization committed in January 2015. Xen already had the correct rule: a guest may use direct frame numbers only for its own memory. The optimization created a cached path where that rule was not checked again. The flaw remained in Xen for more than eleven years. The XSA-523 patches repair both incomplete cache checks. Xen lists no known mitigation, so operators running untrusted or tenant-controlled guests should deploy a hypervisor or downstream update that explicitly includes both fixes.