forked from mirrors/linux
		
	KVM: inject #UD if instruction emulation fails and exit to userspace
Do not kill VM when instruction emulation fails. Inject #UD and report failure to userspace instead. Userspace may choose to reenter guest if vcpu is in userspace (cpl == 3) in which case guest OS will kill offending process and continue running. Signed-off-by: Gleb Natapov <gleb@redhat.com> Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
This commit is contained in:
		
							parent
							
								
									57bc24cfd6
								
							
						
					
					
						commit
						6d77dbfc88
					
				
					 5 changed files with 26 additions and 61 deletions
				
			
		| 
						 | 
					@ -576,7 +576,6 @@ enum emulation_result {
 | 
				
			||||||
#define EMULTYPE_SKIP		    (1 << 2)
 | 
					#define EMULTYPE_SKIP		    (1 << 2)
 | 
				
			||||||
int emulate_instruction(struct kvm_vcpu *vcpu,
 | 
					int emulate_instruction(struct kvm_vcpu *vcpu,
 | 
				
			||||||
			unsigned long cr2, u16 error_code, int emulation_type);
 | 
								unsigned long cr2, u16 error_code, int emulation_type);
 | 
				
			||||||
void kvm_report_emulation_failure(struct kvm_vcpu *cvpu, const char *context);
 | 
					 | 
				
			||||||
void realmode_lgdt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
 | 
					void realmode_lgdt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
 | 
				
			||||||
void realmode_lidt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
 | 
					void realmode_lidt(struct kvm_vcpu *vcpu, u16 size, unsigned long address);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2814,11 +2814,8 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u32 error_code)
 | 
				
			||||||
		return 1;
 | 
							return 1;
 | 
				
			||||||
	case EMULATE_DO_MMIO:
 | 
						case EMULATE_DO_MMIO:
 | 
				
			||||||
		++vcpu->stat.mmio_exits;
 | 
							++vcpu->stat.mmio_exits;
 | 
				
			||||||
		return 0;
 | 
							/* fall through */
 | 
				
			||||||
	case EMULATE_FAIL:
 | 
						case EMULATE_FAIL:
 | 
				
			||||||
		vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
 | 
					 | 
				
			||||||
		vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
 | 
					 | 
				
			||||||
		vcpu->run->internal.ndata = 0;
 | 
					 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		BUG();
 | 
							BUG();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1535,7 +1535,7 @@ static int io_interception(struct vcpu_svm *svm)
 | 
				
			||||||
	string = (io_info & SVM_IOIO_STR_MASK) != 0;
 | 
						string = (io_info & SVM_IOIO_STR_MASK) != 0;
 | 
				
			||||||
	in = (io_info & SVM_IOIO_TYPE_MASK) != 0;
 | 
						in = (io_info & SVM_IOIO_TYPE_MASK) != 0;
 | 
				
			||||||
	if (string || in)
 | 
						if (string || in)
 | 
				
			||||||
		return !(emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DO_MMIO);
 | 
							return emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	port = io_info >> 16;
 | 
						port = io_info >> 16;
 | 
				
			||||||
	size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT;
 | 
						size = (io_info & SVM_IOIO_SIZE_MASK) >> SVM_IOIO_SIZE_SHIFT;
 | 
				
			||||||
| 
						 | 
					@ -2386,16 +2386,12 @@ static int iret_interception(struct vcpu_svm *svm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int invlpg_interception(struct vcpu_svm *svm)
 | 
					static int invlpg_interception(struct vcpu_svm *svm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (emulate_instruction(&svm->vcpu, 0, 0, 0) != EMULATE_DONE)
 | 
						return emulate_instruction(&svm->vcpu, 0, 0, 0) == EMULATE_DONE;
 | 
				
			||||||
		pr_unimpl(&svm->vcpu, "%s: failed\n", __func__);
 | 
					 | 
				
			||||||
	return 1;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int emulate_on_interception(struct vcpu_svm *svm)
 | 
					static int emulate_on_interception(struct vcpu_svm *svm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (emulate_instruction(&svm->vcpu, 0, 0, 0) != EMULATE_DONE)
 | 
						return emulate_instruction(&svm->vcpu, 0, 0, 0) == EMULATE_DONE;
 | 
				
			||||||
		pr_unimpl(&svm->vcpu, "%s: failed\n", __func__);
 | 
					 | 
				
			||||||
	return 1;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int cr8_write_interception(struct vcpu_svm *svm)
 | 
					static int cr8_write_interception(struct vcpu_svm *svm)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3070,7 +3070,7 @@ static int handle_io(struct kvm_vcpu *vcpu)
 | 
				
			||||||
	++vcpu->stat.io_exits;
 | 
						++vcpu->stat.io_exits;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (string || in)
 | 
						if (string || in)
 | 
				
			||||||
		return !(emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DO_MMIO);
 | 
							return emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	port = exit_qualification >> 16;
 | 
						port = exit_qualification >> 16;
 | 
				
			||||||
	size = (exit_qualification & 7) + 1;
 | 
						size = (exit_qualification & 7) + 1;
 | 
				
			||||||
| 
						 | 
					@ -3327,22 +3327,7 @@ static int handle_wbinvd(struct kvm_vcpu *vcpu)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int handle_apic_access(struct kvm_vcpu *vcpu)
 | 
					static int handle_apic_access(struct kvm_vcpu *vcpu)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned long exit_qualification;
 | 
						return emulate_instruction(vcpu, 0, 0, 0) == EMULATE_DONE;
 | 
				
			||||||
	enum emulation_result er;
 | 
					 | 
				
			||||||
	unsigned long offset;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	exit_qualification = vmcs_readl(EXIT_QUALIFICATION);
 | 
					 | 
				
			||||||
	offset = exit_qualification & 0xffful;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	er = emulate_instruction(vcpu, 0, 0, 0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (er !=  EMULATE_DONE) {
 | 
					 | 
				
			||||||
		printk(KERN_ERR
 | 
					 | 
				
			||||||
		       "Fail to handle apic access vmexit! Offset is 0x%lx\n",
 | 
					 | 
				
			||||||
		       offset);
 | 
					 | 
				
			||||||
		return -ENOEXEC;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return 1;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static int handle_task_switch(struct kvm_vcpu *vcpu)
 | 
					static int handle_task_switch(struct kvm_vcpu *vcpu)
 | 
				
			||||||
| 
						 | 
					@ -3554,13 +3539,8 @@ static int handle_invalid_guest_state(struct kvm_vcpu *vcpu)
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (err != EMULATE_DONE) {
 | 
							if (err != EMULATE_DONE)
 | 
				
			||||||
			vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
 | 
								return 0;
 | 
				
			||||||
			vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
 | 
					 | 
				
			||||||
			vcpu->run->internal.ndata = 0;
 | 
					 | 
				
			||||||
			ret = 0;
 | 
					 | 
				
			||||||
			goto out;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (signal_pending(current))
 | 
							if (signal_pending(current))
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3639,24 +3639,6 @@ int emulator_set_dr(int dr, unsigned long value, struct kvm_vcpu *vcpu)
 | 
				
			||||||
	return __kvm_set_dr(vcpu, dr, value);
 | 
						return __kvm_set_dr(vcpu, dr, value);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void kvm_report_emulation_failure(struct kvm_vcpu *vcpu, const char *context)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	u8 opcodes[4];
 | 
					 | 
				
			||||||
	unsigned long rip = kvm_rip_read(vcpu);
 | 
					 | 
				
			||||||
	unsigned long rip_linear;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!printk_ratelimit())
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rip_linear = rip + get_segment_base(vcpu, VCPU_SREG_CS);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	kvm_read_guest_virt(rip_linear, (void *)opcodes, 4, vcpu, NULL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	printk(KERN_ERR "emulation failed (%s) rip %lx %02x %02x %02x %02x\n",
 | 
					 | 
				
			||||||
	       context, rip, opcodes[0], opcodes[1], opcodes[2], opcodes[3]);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
EXPORT_SYMBOL_GPL(kvm_report_emulation_failure);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static u64 mk_cr_64(u64 curr_cr, u32 new_val)
 | 
					static u64 mk_cr_64(u64 curr_cr, u32 new_val)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return (curr_cr & ~((1ULL << 32) - 1)) | new_val;
 | 
						return (curr_cr & ~((1ULL << 32) - 1)) | new_val;
 | 
				
			||||||
| 
						 | 
					@ -3863,6 +3845,19 @@ static void inject_emulated_exception(struct kvm_vcpu *vcpu)
 | 
				
			||||||
		kvm_queue_exception(vcpu, ctxt->exception);
 | 
							kvm_queue_exception(vcpu, ctxt->exception);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int handle_emulation_failure(struct kvm_vcpu *vcpu)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						++vcpu->stat.insn_emulation_fail;
 | 
				
			||||||
 | 
						trace_kvm_emulate_insn_failed(vcpu);
 | 
				
			||||||
 | 
						vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
 | 
				
			||||||
 | 
						vcpu->run->internal.suberror = KVM_INTERNAL_ERROR_EMULATION;
 | 
				
			||||||
 | 
						vcpu->run->internal.ndata = 0;
 | 
				
			||||||
 | 
						kvm_queue_exception(vcpu, UD_VECTOR);
 | 
				
			||||||
 | 
						return EMULATE_FAIL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int emulate_instruction(struct kvm_vcpu *vcpu,
 | 
					int emulate_instruction(struct kvm_vcpu *vcpu,
 | 
				
			||||||
			unsigned long cr2,
 | 
								unsigned long cr2,
 | 
				
			||||||
			u16 error_code,
 | 
								u16 error_code,
 | 
				
			||||||
| 
						 | 
					@ -3931,11 +3926,11 @@ int emulate_instruction(struct kvm_vcpu *vcpu,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		++vcpu->stat.insn_emulation;
 | 
							++vcpu->stat.insn_emulation;
 | 
				
			||||||
		if (r)  {
 | 
							if (r)  {
 | 
				
			||||||
			++vcpu->stat.insn_emulation_fail;
 | 
					 | 
				
			||||||
			trace_kvm_emulate_insn_failed(vcpu);
 | 
					 | 
				
			||||||
			if (kvm_mmu_unprotect_page_virt(vcpu, cr2))
 | 
								if (kvm_mmu_unprotect_page_virt(vcpu, cr2))
 | 
				
			||||||
				return EMULATE_DONE;
 | 
									return EMULATE_DONE;
 | 
				
			||||||
 | 
								if (emulation_type & EMULTYPE_SKIP)
 | 
				
			||||||
				return EMULATE_FAIL;
 | 
									return EMULATE_FAIL;
 | 
				
			||||||
 | 
								return handle_emulation_failure(vcpu);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3960,9 +3955,7 @@ int emulate_instruction(struct kvm_vcpu *vcpu,
 | 
				
			||||||
		if (kvm_mmu_unprotect_page_virt(vcpu, cr2))
 | 
							if (kvm_mmu_unprotect_page_virt(vcpu, cr2))
 | 
				
			||||||
			return EMULATE_DONE;
 | 
								return EMULATE_DONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		trace_kvm_emulate_insn_failed(vcpu);
 | 
							return handle_emulation_failure(vcpu);
 | 
				
			||||||
		kvm_report_emulation_failure(vcpu, "mmio");
 | 
					 | 
				
			||||||
		return EMULATE_FAIL;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	toggle_interruptibility(vcpu, vcpu->arch.emulate_ctxt.interruptibility);
 | 
						toggle_interruptibility(vcpu, vcpu->arch.emulate_ctxt.interruptibility);
 | 
				
			||||||
| 
						 | 
					@ -4798,7 +4791,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run)
 | 
				
			||||||
		vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
 | 
							vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
 | 
				
			||||||
		r = emulate_instruction(vcpu, 0, 0, EMULTYPE_NO_DECODE);
 | 
							r = emulate_instruction(vcpu, 0, 0, EMULTYPE_NO_DECODE);
 | 
				
			||||||
		srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
 | 
							srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
 | 
				
			||||||
		if (r == EMULATE_DO_MMIO) {
 | 
							if (r != EMULATE_DONE) {
 | 
				
			||||||
			r = 0;
 | 
								r = 0;
 | 
				
			||||||
			goto out;
 | 
								goto out;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue