|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v3 1/5] x86/32on64: use generic instruction decoding for call gate emulation
>>> On 06.12.16 at 12:56, <andrew.cooper3@xxxxxxxxxx> wrote:
> On 06/12/16 11:12, Jan Beulich wrote:
>> +static int gate_op_read(
>> + enum x86_segment seg,
>> + unsigned long offset,
>> + void *p_data,
>> + unsigned int bytes,
>> + struct x86_emulate_ctxt *ctxt)
>> +{
>> + const struct gate_op_ctxt *goc =
>> + container_of(ctxt, struct gate_op_ctxt, ctxt);
>> + unsigned int rc = bytes, sel = 0;
>> + unsigned long addr = offset, limit = 0;
>> +
>> + switch ( seg )
>> + {
>> + case x86_seg_cs:
>> + addr += goc->cs.base;
>> + limit = goc->cs.limit;
>> + break;
>> + case x86_seg_ds:
>> + sel = read_sreg(ds);
>> + break;
>> + case x86_seg_es:
>> + sel = read_sreg(es);
>> + break;
>> + case x86_seg_fs:
>> + sel = read_sreg(fs);
>> + break;
>> + case x86_seg_gs:
>> + sel = read_sreg(gs);
>> + break;
>> + case x86_seg_ss:
>> + sel = ctxt->regs->ss;
>> + break;
>> + default:
>> + return X86EMUL_UNHANDLEABLE;
>> + }
>> + if ( sel )
>> + {
>> + unsigned int ar;
>> +
>> + ASSERT(!goc->insn_fetch);
>> + if ( !read_descriptor(sel, current, &addr, &limit, &ar, 0) ||
>> + !(ar & _SEGMENT_S) ||
>> + !(ar & _SEGMENT_P) ||
>> + ((ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR)) )
>> + return X86EMUL_UNHANDLEABLE;
>> + addr += offset;
>> + }
>> + else if ( seg != x86_seg_cs )
>> + return X86EMUL_UNHANDLEABLE;
>> +
>> + if ( limit < bytes - 1 || offset > limit - bytes + 1 )
>
> Is this correct for the zero-length instruction fetches which the
> emulator emits? It would be better to make it safe now, than to
> introduce a latent bug in the future.
The left side of the || unconditionally fails such fetches, and that's
precisely what we want here (as tight a condition as possible)
considering that this gets used only for decoding, not for executing
instructions.
>> + return X86EMUL_UNHANDLEABLE;
>> +
>> + if ( is_pv_32bit_vcpu(current) )
>> + addr = (uint32_t)addr;
>
> This should be based on %cs attributes. What about a 64bit PV guest in
> a compatability segment?
No - I now realize the conditional is pointless. We only do gate op
emulation for 32-bit guests.
>> +
>> + if ( !__addr_ok(addr) ||
>> + (rc = __copy_from_user(p_data, (void *)addr, bytes)) )
>> + {
>> + x86_emul_pagefault(goc->insn_fetch && cpu_has_nx
>> + ? PFEC_insn_fetch : 0,
>> + addr + bytes - rc, ctxt);
>
> Independently to the point below, the correct predicate for setting
> PFEC_insn_fetch is ((EFER & EFER_NX) || (CR4 & SMEP))
Remember that here we're dealing with PV guests only - those don't
see any SMEP, and always run with NX enabled as long as we do
ourselves.
> However, this is subtly wrong, but I can't think of a sensible way of
> fixing it (i.e. without doing independent pagewalks).
>
> __copy_from_user() does a data fetch, not an instruction fetch.
>
> With the advent of PKU, processors support pages which can't be read,
> but can be executed. Then again, our chances of ever supporting PKU for
> PV guests is slim, so maybe this point isn't very important.
>
> As we actually do a data read, the error code should remain 0. This
> (getting a data fetch failure for something which should have been an
> instruction fetch) will be less confusing for the guest than claiming an
> instruction fetch failure when we actually did a data fetch, as at least
> the error code will be correct for the access rights in the translation.
With the above I think this should remain as is.
>> @@ -3350,179 +3431,59 @@ static void emulate_gate_op(struct cpu_u
>> return;
>> }
>>
>> - op_bytes = op_default = ar & _SEGMENT_DB ? 4 : 2;
>> - ad_default = ad_bytes = op_default;
>> - opnd_sel = opnd_off = 0;
>> - jump = -1;
>> - for ( eip = regs->eip; eip - regs->_eip < 10; )
>> + ctxt.ctxt.addr_size = ar & _SEGMENT_DB ? 32 : 16;
>> + /* Leave zero in ctxt.ctxt.sp_size, as it's not needed for decoding. */
>> + state = x86_decode_insn(&ctxt.ctxt, gate_op_read);
>> + ctxt.insn_fetch = false;
>> + if ( IS_ERR_OR_NULL(state) )
>> + {
>> + if ( PTR_ERR(state) != -X86EMUL_EXCEPTION )
>> + do_guest_trap(TRAP_gp_fault, regs);
>
> Here, and...
>
>> - if ( (opnd_sel != regs->cs &&
>> - !read_descriptor(opnd_sel, v, &base, &limit, &ar, 0)) ||
>> - !(ar & _SEGMENT_S) ||
>> - !(ar & _SEGMENT_P) ||
>> - ((ar & _SEGMENT_CODE) && !(ar & _SEGMENT_WR)) )
>> - {
>> - do_guest_trap(TRAP_gp_fault, regs);
>> - return;
>> - }
>> + if ( rc == X86EMUL_EXCEPTION )
>
> .. and here must unconditionally inject a fault, or the guest will
> livelock by repeatedly taking #GP faults.
The first one needs an else (to deliver the exception indicated by
X86EMUL_EXCEPTION) - this was an oversight during rebase over
your recent changes. And the second one is similar, just that it's
not a missing else (i.e. it's not entirely unconditional to deliver an
exception here; other failure cases are being handled right after
this).
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |