[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v2 13/19] x86/shadow: Avoid raising faults behind the emulators back



On 28/11/16 14:49, Tim Deegan wrote:
> Hi,
>
> At 11:13 +0000 on 28 Nov (1480331610), Andrew Cooper wrote:
>> Use x86_emul_{hw_exception,pagefault}() rather than
>> {pv,hvm}_inject_page_fault() and hvm_inject_hw_exception() to cause raised
>> faults to be known to the emulator.  This requires altering the callers of
>> x86_emulate() to properly re-inject the event.
>>
>> While fixing this, fix the singlestep behaviour.  Previously, an otherwise
>> successful emulation would fail if singlestepping was active, as the emulator
>> couldn't raise #DB.  This is unreasonable from the point of view of the 
>> guest.
>>
>> We therefore tolerate #PF/#GP/SS and #DB being raised by the emulator, but
>> reject anything else as unexpected.
>>
>> Signed-off-by: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
>> +    if ( r == X86EMUL_EXCEPTION && emul_ctxt.ctxt.event_pending )
>> +    {
>> +        /*
>> +         * This emulation covers writes to shadow pagetables.  We tolerate 
>> #PF
>> +         * (from hitting adjacent pages), #GP/#SS (from segmentation 
>> errors),
>> +         * and #DB (from singlestepping).  Anything else is an emulation 
>> bug,
>> +         * or a guest playing with the instruction stream under Xen's feet.
>> +         */
>> +        if ( emul_ctxt.ctxt.event.type == X86_EVENTTYPE_HW_EXCEPTION &&
>> +             (emul_ctxt.ctxt.event.vector < 32) &&
>> +             ((1u << emul_ctxt.ctxt.event.vector) &
>> +              ((1u << TRAP_debug) | (1u << TRAP_stack_error) |
>> +               (1u << TRAP_gp_fault) | (1u << TRAP_page_fault))) )
>> +        {
>> +            if ( is_hvm_vcpu(v) )
>> +                hvm_inject_event(&emul_ctxt.ctxt.event);
>> +            else
>> +                pv_inject_event(&emul_ctxt.ctxt.event);
>> +        }
>> +        else
>> +        {
>> +            if ( is_hvm_vcpu(v) )
>> +                hvm_inject_hw_exception(TRAP_gp_fault, 0);
>> +            else
>> +                pv_inject_hw_exception(TRAP_gp_fault, 0);
>> +        }
> I don't think it's OK to lob #GP into a HVM guest here -- we can hit
> this path even if the guest isn't behaving strangely.

What circumstances are you thinking of?

Unless the guest is playing with the instruction stream,  event_pending
will only be set by the set in the paths altered by this patch, which is
the four whitelisted vectors.

> I think it would be better to run this check before the X86EMUL_UNHANDLEABLE 
> one
> and convert injections that we choose not to handle into
> X86EMUL_UNHANDLEABLE.
>
> Which I guess brings us back full circle to the behaviour we had
> before, and perhaps the right change to the earlier patch is to
> start down this road, with an explanatory comment.
>
> e.g. start with something like this, immediately after the first call
> to x86_emulate():
>
>     /*
>      * Events raised within the emulator itself used to return
>      * X86EMUL_UNHANDLEABLE because we didn't supply injection
>      * callbacks.  Now the emulator supplies those to us via
>      * ctxt.event_pending instead.  Preserve the old behaviour
>      * for now.
>      */
>      if (emul_ctxt.ctxt.event_pending)
>          r = X86EMUL_UNHANDLEABLE;
>
> And in this patch, replace it with the hunk you have above, but
> setting r = X86EMUL_UNHANDLEABLE instead of injecting #GP.
>
> Does that make sense?

It does make sense, but that goes against the comment of not unshadowing
on exception.

A lot of this revolves around how likely we are to hit the #GP[0] case. 
I assert that we shouldn't be able to hit it unless the guest is playing
games, or we have a bug in emulation.

If we don't go throwing a #GP back, we should at least leave something
obvious in the log.

>
> Also, I'm a little confused after all this as to whether the emulator
> can still return with X86EMUL_OKAY and the event_pending set.

I have now determined this not to be the case.  Apologies for my
misinformation in v1.

All exception and software interrupt cases end up returning X86_EXCEPTION.

    case 0xcd: /* int imm8 */
        swint_type = x86_swint_int;
    swint:
        rc = inject_swint(swint_type, (uint8_t)src.val,
                          _regs.eip - ctxt->regs->eip,
                          ctxt, ops) ? : X86EMUL_EXCEPTION;
        goto done;


This is why I introduced the slightly relaxed check:

if ( emul_ctxt.ctxt.event_pending )
    ASSERT(r == X86EMUL_EXCEPTION);

in the earlier patch.

> If it
> can do that then we need to inject the event come what may, because
> any other side-effects will have been committted already. 

Because of the shadow pagetables use of x86_swint_emulate_none, all
software interrupts have fault semantics, so %eip isn't moved forward;
this is left to hardware on the re-inject path.  There are no other
pieces of state change for any software interrupts.

With some re-evaluation of hindsight, I plan to make all trap injection
have fault semantics out of x86_emulate(), leaving the possible
adjustment of %eip to the lower level vendor functions, as x86_event has
an insn_len field.  In reality, its only the svm path which needs to
adjust %eip, and only in certain circumstances.

The odd-case-out is singlestep, which completes writeback then raises an
exception.

>
>> @@ -3475,6 +3503,37 @@ static int sh_page_fault(struct vcpu *v,
>>              {
>>                  perfc_incr(shadow_em_ex_fail);
>>                  TRACE_SHADOW_PATH_FLAG(TRCE_SFLAG_EMULATION_LAST_FAILED);
>> +
>> +                if ( r == X86EMUL_EXCEPTION && emul_ctxt.ctxt.event_pending 
>> )
>> +                {
>> +                    /*
>> +                     * This emulation covers writes to shadow pagetables.  
>> We
>> +                     * tolerate #PF (from hitting adjacent pages), #GP/#SS
>> +                     * (from segmentation errors), and #DB (from
>> +                     * singlestepping).  Anything else is an emulation bug, 
>> or
>> +                     * a guest playing with the instruction stream under 
>> Xen's
>> +                     * feet.
>> +                     */
>> +                    if ( emul_ctxt.ctxt.event.type == 
>> X86_EVENTTYPE_HW_EXCEPTION &&
>> +                         (emul_ctxt.ctxt.event.vector < 32) &&
>> +                         ((1u << emul_ctxt.ctxt.event.vector) &
>> +                          ((1u << TRAP_debug) | (1u << TRAP_stack_error) |
>> +                           (1u << TRAP_gp_fault) | (1u << 
>> TRAP_page_fault))) )
>> +                    {
>> +                        if ( is_hvm_vcpu(v) )
>> +                            hvm_inject_event(&emul_ctxt.ctxt.event);
>> +                        else
>> +                            pv_inject_event(&emul_ctxt.ctxt.event);
>> +                    }
>> +                    else
>> +                    {
>> +                        if ( is_hvm_vcpu(v) )
>> +                            hvm_inject_hw_exception(TRAP_gp_fault, 0);
>> +                        else
>> +                            pv_inject_hw_exception(TRAP_gp_fault, 0);
>> +                    }
>> +                }
>> +
> This looks like code duplication, but rather than trying to merge the
> two cases, I think we can drop this one entirely.  This emulation is
> optimistically trying to find the second half of a PAE PTE write -
> it's OK just to stop emulating if we hit anything this exciting.
> So we can lose the whole hunk.

At the very least we should retain the singlestep #DB injection, as it
still has trap semantics.

~Andrew

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel

 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.