Xen Test Framework
XSA-7

Advisory: XSA-7

Background

Intel and AMD CPUs implement the SYSRET instruction differently. Relevant to XSA-7 is the order of changing privilege and the canonical check on %rip.

In x86_64 with 4-level paging, 0x0000800000000000 is a non-canonical address but a legal state for %rip, which can occur by executing an instruction which ends on the lower canonical boundary. This state should yield #GP[0] for a bad instruction fetch, reporting the non-canonical regs->rip.

Exceptions taken in CPL0 push onto the current stack (IST vectors can change stack, but #GP is never typically IST), while exceptions at lower privilege perform a normal privilege change including stack switch back to CPL0. At the SYSCALL entry and SYSRET exit points, %rsp is the user stack, not the kernel stack.

For the SYSRET instruction, at the time of writing (July 2025) AMD don't explicitly identify where the %rip canonical check is performed, but have stated that it is after switching to CPL3. Therefore, AMD CPUs are not vulnerable.

The Intel SDM pseudocode for SYSRET does show the %rip canonical check being performed early and while in CPL0, and experimentation confirms that Intel CPUs are vulnerable. The recommended fix is to fall back to using IRET in this case. IRET takes #GP[0] for a non-canonical %rip in CPL0, but does so on a kernel stack, so can handle the fault safely.

P.S. Unrelated to XSA-7, but if an attacker can clear MSR_EFER.SCE, the resulting #UD from SYSRET can be used in the same way.

PoC

Crashing a vulnerable Xen is easy. Like XSA-260, causing Xen to use a non-canonical stack will escalate to #DF.

However, on a mitigated Xen the resulting #GP needs handing back to the guest. If the guest stack is bad, pushing the exception frame will fail and the guest will be crashed, preventing a clean status report.

Instead, use a good %rsp in the same frame that holds the SYSCALL for the attack run. If Xen has SMAP, there will be a clean #DF (64bit PV guests are always CPL3, so use _PAGE_USER mappings).

If SMAP isn't active, Xen's #GP handler will run on the provided stack, including the top-of-stack block. To force a crash, poison the rest of the page with 0xcc, notably covering the current and __per_cpu_offset top-of-stack fields, causing recursive #GP faults. Eventually, Xen runs off the bottom of the stack, finds an unmapped page, and escalates to a clean #DF, which does notice that it's on the wrong stack.

A mitigated Xen will not run on the provided stack, but will deliver the #GP back to us on it. Almost 4k is plenty of space to handle the fault and recover.

See also
tests/xsa-7/main.c