diff --git a/xen/arch/x86/hvm/emulate.c b/xen/arch/x86/hvm/emulate.c index ccf3aa2700..d0a043bfd3 100644 --- a/xen/arch/x86/hvm/emulate.c +++ b/xen/arch/x86/hvm/emulate.c @@ -567,10 +567,16 @@ static int hvmemul_virtual_to_linear( if ( *reps != 1 ) return X86EMUL_UNHANDLEABLE; - /* This is a singleton operation: fail it with an exception. */ - x86_emul_hw_exception((seg == x86_seg_ss) - ? TRAP_stack_error - : TRAP_gp_fault, 0, &hvmemul_ctxt->ctxt); + /* + * Leave exception injection to the caller for non-user segments: We + * neither know the exact error code to be used, nor can we easily + * determine the kind of exception (#GP or #TS) in that case. + */ + if ( is_x86_user_segment(seg) ) + x86_emul_hw_exception((seg == x86_seg_ss) + ? TRAP_stack_error + : TRAP_gp_fault, 0, &hvmemul_ctxt->ctxt); + return X86EMUL_EXCEPTION; } diff --git a/xen/arch/x86/hvm/hvm.c b/xen/arch/x86/hvm/hvm.c index 27896ec654..504425a476 100644 --- a/xen/arch/x86/hvm/hvm.c +++ b/xen/arch/x86/hvm/hvm.c @@ -2497,24 +2497,28 @@ bool_t hvm_virtual_to_linear_addr( if ( !reg->attr.fields.p ) goto out; - switch ( access_type ) + /* Read/write restrictions only exist for user segments. */ + if ( reg->attr.fields.s ) { - case hvm_access_read: - if ( (reg->attr.fields.type & 0xa) == 0x8 ) - goto out; /* execute-only code segment */ - break; - case hvm_access_write: - if ( (reg->attr.fields.type & 0xa) != 0x2 ) - goto out; /* not a writable data segment */ - break; - default: - break; + switch ( access_type ) + { + case hvm_access_read: + if ( (reg->attr.fields.type & 0xa) == 0x8 ) + goto out; /* execute-only code segment */ + break; + case hvm_access_write: + if ( (reg->attr.fields.type & 0xa) != 0x2 ) + goto out; /* not a writable data segment */ + break; + default: + break; + } } last_byte = (uint32_t)offset + bytes - !!bytes; /* Is this a grows-down data segment? Special limit check if so. */ - if ( (reg->attr.fields.type & 0xc) == 0x4 ) + if ( reg->attr.fields.s && (reg->attr.fields.type & 0xc) == 0x4 ) { /* Is upper limit 0xFFFF or 0xFFFFFFFF? */ if ( !reg->attr.fields.db ) @@ -2530,10 +2534,18 @@ bool_t hvm_virtual_to_linear_addr( else { /* - * LONG MODE: FS and GS add segment base. Addresses must be canonical. + * User segments are always treated as present. System segment may + * not be, and also incur limit checks. */ + if ( is_x86_system_segment(seg) && + (!reg->attr.fields.p || (offset + bytes - !!bytes) > reg->limit) ) + goto out; - if ( (seg == x86_seg_fs) || (seg == x86_seg_gs) ) + /* + * LONG MODE: FS, GS and system segments: add segment base. All + * addresses must be canonical. + */ + if ( seg >= x86_seg_fs ) addr += reg->base; last_byte = addr + bytes - !!bytes; diff --git a/xen/arch/x86/mm/shadow/common.c b/xen/arch/x86/mm/shadow/common.c index c59f022c43..39be564e5d 100644 --- a/xen/arch/x86/mm/shadow/common.c +++ b/xen/arch/x86/mm/shadow/common.c @@ -162,9 +162,15 @@ static int hvm_translate_linear_addr( if ( !okay ) { - x86_emul_hw_exception( - (seg == x86_seg_ss) ? TRAP_stack_error : TRAP_gp_fault, - 0, &sh_ctxt->ctxt); + /* + * Leave exception injection to the caller for non-user segments: We + * neither know the exact error code to be used, nor can we easily + * determine the kind of exception (#GP or #TS) in that case. + */ + if ( is_x86_user_segment(seg) ) + x86_emul_hw_exception( + (seg == x86_seg_ss) ? TRAP_stack_error : TRAP_gp_fault, + 0, &sh_ctxt->ctxt); return X86EMUL_EXCEPTION; } diff --git a/xen/arch/x86/x86_emulate/x86_emulate.h b/xen/arch/x86/x86_emulate/x86_emulate.h index 915c1fee93..164fc24d56 100644 --- a/xen/arch/x86/x86_emulate/x86_emulate.h +++ b/xen/arch/x86/x86_emulate/x86_emulate.h @@ -31,7 +31,11 @@ struct x86_emulate_ctxt; -/* Comprehensive enumeration of x86 segment registers. */ +/* + * Comprehensive enumeration of x86 segment registers. Various bits of code + * rely on this order (general purpose before system, tr at the beginning of + * system). + */ enum x86_segment { /* General purpose. Matches the SReg3 encoding in opcode/ModRM bytes. */ x86_seg_es, @@ -40,21 +44,25 @@ enum x86_segment { x86_seg_ds, x86_seg_fs, x86_seg_gs, - /* System. */ + /* System: Valid to use for implicit table references. */ x86_seg_tr, x86_seg_ldtr, x86_seg_gdtr, x86_seg_idtr, - /* - * Dummy: used to emulate direct processor accesses to management - * structures (TSS, GDT, LDT, IDT, etc.) which use linear addressing - * (no segment component) and bypass usual segment- and page-level - * protection checks. - */ + /* No Segment: For accesses which are already linear. */ x86_seg_none }; -#define is_x86_user_segment(seg) ((unsigned)(seg) <= x86_seg_gs) +static inline bool is_x86_user_segment(enum x86_segment seg) +{ + unsigned int idx = seg; + + return idx <= x86_seg_gs; +} +static inline bool is_x86_system_segment(enum x86_segment seg) +{ + return seg >= x86_seg_tr && seg < x86_seg_none; +} /* Classification of the types of software generated interrupts/exceptions. */ enum x86_swint_type {