x86/pv: Don't assume that INT $imm8 instructions are two bytes long

For INT $N instructions (besides $0x80 for which there is a dedicated fast
path), handling is mostly fault-based because of DPL0 gates in the IDT.  This
means that when the guest kernel allows the instruction too, Xen must
increment %rip to the end of the instruction before passing a trap to the
guest kernel.

When an INT $N instruction has a prefix, it's longer than two bytes, and Xen
will deliver the "trap" with %rip pointing into the middle of the instruction.

Introduce a new pv_emulate_sw_interrupt() which uses x86_insn_length() to
determine the instruction length, rather than assuming two.

This is a change in behaviour for PV guests, but the prior behaviour cannot
reasonably be said to be intentional.

This change does not affect the INT $0x80 fastpath.  Prefixed INT $N
instructions occur almost exclusively in test code or exploits, and INT $0x80
appears to be the only user-usable interrupt gate in contemporary PV guests.

Signed-off-by: Andrew Cooper <andrew.cooper3@citrix.com>
Reviewed-by: Jan Beulich <jbeulich@suse.com>
master commit: a934a600c4
master date: 2026-03-03 15:15:53 +0000
This commit is contained in:
Andrew Cooper authored and Jan Beulich committed 2026-03-10 14:36:41 +01:00
1 parent 93614ffa48
commit 805e2b8ada
3 files changed
+52 -2

No files matched your search

+2
View File
@@ -17,6 +17,7 @@
int pv_raise_nmi(struct vcpu *v);
int pv_emulate_privileged_op(struct cpu_user_regs *regs);
void pv_emulate_sw_interrupt(struct cpu_user_regs *regs);
void pv_emulate_gate_op(struct cpu_user_regs *regs);
bool pv_emulate_invalid_op(struct cpu_user_regs *regs);
@@ -33,6 +34,7 @@ static inline bool pv_trap_callback_registered(const struct vcpu *v,
static inline int pv_raise_nmi(struct vcpu *v) { return -EOPNOTSUPP; }
static inline int pv_emulate_privileged_op(struct cpu_user_regs *regs) { return 0; }
static inline void pv_emulate_sw_interrupt(struct cpu_user_regs *regs) {}
static inline void pv_emulate_gate_op(struct cpu_user_regs *regs) {}
static inline bool pv_emulate_invalid_op(struct cpu_user_regs *regs) { return true; }
+49
View File
@@ -8,6 +8,7 @@
*/
#include <xen/domain_page.h>
#include <xen/err.h>
#include <xen/event.h>
#include <xen/guest_access.h>
#include <xen/hypercall.h>
@@ -1384,6 +1385,54 @@ int pv_emulate_privileged_op(struct cpu_user_regs *regs)
return 0;
}
/*
* Hardware already decoded the INT $N instruction and determined that there
* was a permission issue (i.e. the DPL violation intended to trigger #GP).
* Xen has already determined that the guest kernel has permitted this
* software interrupt.
*
* All that is needed is the instruction length, to turn the fault into a
* trap. All errors are turned back into the original #GP, as that's the
* action that really happened.
*/
void pv_emulate_sw_interrupt(struct cpu_user_regs *regs)
{
struct vcpu *curr = current;
struct domain *currd = curr->domain;
struct priv_op_ctxt ctxt = {
.ctxt.regs = regs,
.ctxt.lma = !is_pv_32bit_domain(currd),
};
struct x86_emulate_state *state;
uint8_t vector = regs->error_code >> 3;
unsigned int len, ar;
if ( !pv_emul_read_descriptor(regs->cs, curr, &ctxt.cs.base,
&ctxt.cs.limit, &ar, 1) ||
!(ar & _SEGMENT_S) ||
!(ar & _SEGMENT_P) ||
!(ar & _SEGMENT_CODE) )
goto error;
state = x86_decode_insn(&ctxt.ctxt, insn_fetch);
if ( IS_ERR_OR_NULL(state) )
goto error;
len = x86_insn_length(state, &ctxt.ctxt);
x86_emulate_free_state(state);
/* Note: Checked slightly late to simplify 'state' handling. */
if ( ctxt.ctxt.opcode != 0xcd /* INT $imm8 */ )
goto error;
regs->rip += len;
pv_inject_sw_interrupt(vector);
return;
error:
pv_inject_hw_exception(X86_EXC_GP, regs->error_code);
}
/*
* Local variables:
* mode: C
+1 -2
View File
@@ -1365,8 +1365,7 @@ void do_general_protection(struct cpu_user_regs *regs)
if ( permit_softint(TI_GET_DPL(ti), v, regs) )
{
regs->rip += 2;
pv_inject_sw_interrupt(vector);
pv_emulate_sw_interrupt(regs);
return;
}
}
Reference in new issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.