[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
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |