public inbox for kvm@vger.kernel.org
 help / color / mirror / Atom feed
From: Adalbert Lazar <alazar@bitdefender.com>
To: kvm@vger.kernel.org
Cc: "Paolo Bonzini" <pbonzini@redhat.com>,
	"Radim Krčmář" <rkrcmar@redhat.com>,
	alazar@bitdefender.com, mdontu@bitdefender.com
Subject: [RFC PATCH 10/19] kvm: vmx: Hook in kvmi_page_fault()
Date: Fri, 16 Jun 2017 16:43:39 +0300	[thread overview]
Message-ID: <20170616134348.17725-11-alazar@bitdefender.com> (raw)
In-Reply-To: <20170616134348.17725-1-alazar@bitdefender.com>

From: Mihai Dontu <mdontu@bitdefender.com>

Notify the guest introspection tool when a #PF occurs due to a failed
permission check in the shadow page tables.

This call and the code involved in managing the shadow page tables
permissions are the essence of a security solution using guest
introspection facilities.

The shadow page tables are used to guarantee the purpose of code areas
inside the guest (code, rodata, stack, heap etc.) Each attempt at an
operation unfitting for a certain memory range (eg. execute code in
heap) triggers a #PF and gives the introspection tool the chance to
audit the code attempting the operation. The possible responses can be:

 * allow it
 * allow it via emulation
 * allow it via emulation and with custom input (see the 'Change
 emulation context' patch)
 * deny it by skipping the instruction

The #PF event is generated only for pages for which the guest
introspection tool has shown interest (ie. has previously touched it by
adjusting the permissions).

Page size is essential for performance (the smaller the better), that's
why huge pages should be split. At the time of writing this patch, they
are disabled with CONFIG_TRANSPARENT_HUGEPAGE=n.

Signed-off-by: Mihai Dontu <mdontu@bitdefender.com>
---
 arch/x86/include/asm/kvm_host.h |  4 ++--
 arch/x86/kvm/mmu.c              | 51 +++++++++++++++++++++++++++++++++++++++--
 arch/x86/kvm/vmx.c              | 24 ++++++++++++++++---
 3 files changed, 72 insertions(+), 7 deletions(-)

diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 40d1ee68474a..8d1d80bd2230 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1238,8 +1238,8 @@ void kvm_vcpu_deactivate_apicv(struct kvm_vcpu *vcpu);
 
 int kvm_emulate_hypercall(struct kvm_vcpu *vcpu);
 
-int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t gva, u64 error_code,
-		       void *insn, int insn_len);
+int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
+		       void *insn, int insn_len, unsigned long gva, bool pf);
 void kvm_mmu_invlpg(struct kvm_vcpu *vcpu, gva_t gva);
 void kvm_mmu_new_cr3(struct kvm_vcpu *vcpu);
 
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index 12e4c33ff879..3d2527626694 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -40,6 +40,8 @@
 #include <linux/uaccess.h>
 #include <linux/hash.h>
 #include <linux/kern_levels.h>
+#include <linux/kvmi.h>
+#include "../../../../virt/kvm/kvmi.h"
 
 #include <asm/page.h>
 #include <asm/cmpxchg.h>
@@ -4723,11 +4725,46 @@ static void make_mmu_pages_available(struct kvm_vcpu *vcpu)
 	kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list);
 }
 
+static enum emulation_result __kvm_mmu_page_fault(struct kvm_vcpu *vcpu,
+						  gpa_t gpa, unsigned long gva,
+						  bool *again)
+{
+	unsigned int opts = 0;
+	unsigned long eq = vcpu->arch.exit_qualification;
+	u64 spte = kvm_mmu_get_spte(vcpu->kvm, vcpu, gpa);
+	enum emulation_result er = EMULATE_FAIL;
+
+	if (spte == -ENOENT) {
+		/* The SPTE is not present */
+		*again = true;
+		return EMULATE_FAIL;
+	}
+
+	if (!kvmi_page_fault(vcpu, gpa, gva, eq, &opts))
+		return EMULATE_FAIL;
+
+	if (opts & KVMI_EVENT_NOEMU)
+		er = EMULATE_DONE;
+	else {
+		er = x86_emulate_instruction(vcpu, gpa, 0, NULL, 0);
+
+		vcpu->ctx_size = 0;
+		vcpu->ctx_pos = 0;
+
+		if (er != EMULATE_DONE)
+			kvm_err("%s: emulate failed (err: %d, gpa: %llX)\n",
+			     __func__, er, gpa);
+	}
+
+	return er;
+}
+
 int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
-		       void *insn, int insn_len)
+		       void *insn, int insn_len, unsigned long gva, bool pf)
 {
 	int r, emulation_type = EMULTYPE_RETRY;
 	enum emulation_result er;
+	bool again = false;
 	bool direct = vcpu->arch.mmu.direct_map || mmu_is_nested(vcpu);
 
 	if (unlikely(error_code & PFERR_RSVD_MASK)) {
@@ -4742,12 +4779,21 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
 			return r;
 	}
 
+	if (pf) {
+		er = __kvm_mmu_page_fault(vcpu, cr2, gva, &again);
+		if (er != EMULATE_FAIL)
+			goto check_er;
+	}
+
 	r = vcpu->arch.mmu.page_fault(vcpu, cr2, lower_32_bits(error_code),
 				      false);
 	if (r < 0)
 		return r;
-	if (!r)
+	if (!r) {
+		if (again)
+			__kvm_mmu_page_fault(vcpu, cr2, gva, &again);
 		return 1;
+	}
 
 	/*
 	 * Before emulating the instruction, check if the error code
@@ -4769,6 +4815,7 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
 emulate:
 	er = x86_emulate_instruction(vcpu, cr2, emulation_type, insn, insn_len);
 
+check_er:
 	switch (er) {
 	case EMULATE_DONE:
 		return 1;
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index 7a594cfcb2ea..f99fcc86f141 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -5653,7 +5653,8 @@ static int handle_exception(struct kvm_vcpu *vcpu)
 
 		if (kvm_event_needs_reinjection(vcpu))
 			kvm_mmu_unprotect_page_virt(vcpu, cr2);
-		return kvm_mmu_page_fault(vcpu, cr2, error_code, NULL, 0);
+		return kvm_mmu_page_fault(vcpu, cr2, error_code, NULL, 0, 0,
+					  false);
 	}
 
 	ex_no = intr_info & INTR_INFO_VECTOR_MASK;
@@ -6204,6 +6205,8 @@ static int handle_task_switch(struct kvm_vcpu *vcpu)
 
 static int handle_ept_violation(struct kvm_vcpu *vcpu)
 {
+	bool pf = false;
+	unsigned long gla = 0;
 	unsigned long exit_qualification;
 	gpa_t gpa;
 	u32 error_code;
@@ -6234,6 +6237,21 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
 	gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS);
 	trace_kvm_page_fault(gpa, exit_qualification);
 
+	if ((exit_qualification & EPT_VIOLATION_GVA_TRANSLATED)) {
+		pf  = true;
+		gla = vmcs_readl(GUEST_LINEAR_ADDRESS);
+
+		/*
+		 * It can happen for kvm_read_cr3() to return 0 event though
+		 * the page fault took place as a result of a guest page table
+		 * translation
+		 *
+		 * TODO: Fix kvm_read_cr3(). The problem is in is_paging()
+		 */
+		vcpu->arch.cr3 = vmcs_readl(GUEST_CR3);
+		__set_bit(VCPU_EXREG_CR3, (ulong *)&vcpu->arch.regs_avail);
+	}
+
 	/* Is it a read fault? */
 	error_code = (exit_qualification & EPT_VIOLATION_ACC_READ)
 		     ? PFERR_USER_MASK : 0;
@@ -6252,7 +6270,7 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu)
 	vcpu->arch.gpa_available = true;
 	vcpu->arch.exit_qualification = exit_qualification;
 
-	return kvm_mmu_page_fault(vcpu, gpa, error_code, NULL, 0);
+	return kvm_mmu_page_fault(vcpu, gpa, error_code, NULL, 0, gla, pf);
 }
 
 static int handle_ept_misconfig(struct kvm_vcpu *vcpu)
@@ -6273,7 +6291,7 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu)
 					      EMULATE_DONE;
 
 	if (unlikely(ret == RET_MMIO_PF_INVALID))
-		return kvm_mmu_page_fault(vcpu, gpa, 0, NULL, 0);
+		return kvm_mmu_page_fault(vcpu, gpa, 0, NULL, 0, 0, false);
 
 	if (unlikely(ret == RET_MMIO_PF_RETRY))
 		return 1;
-- 
2.12.2

  parent reply	other threads:[~2017-06-16 13:43 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-06-16 13:43 [RFC PATCH 00/19] Guest introspection Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 01/19] kvm: x86: mmu: Add kvm_mmu_get_spte() and kvm_mmu_set_spte() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 02/19] kvm: x86: Add kvm_arch_vcpu_set_regs() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 03/19] mm: Add vm_replace_page() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 04/19] kvm: Add kvm_enum() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 05/19] kvm: Add uuid member in struct kvm + support for KVM_CAP_VM_UUID Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 06/19] kvm: Add kvm_vm_shutdown() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 07/19] kvm: x86: Add kvm_arch_msr_intercept() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 08/19] kvm: Add the introspection subsystem Adalbert Lazar
2017-06-21 11:54   ` Paolo Bonzini
2017-06-21 12:36     ` Mihai Donțu
2017-06-21 12:57       ` Paolo Bonzini
2017-06-16 13:43 ` [RFC PATCH 09/19] kvm: Hook in kvmi on VM on/off events Adalbert Lazar
2017-06-16 13:43 ` Adalbert Lazar [this message]
2017-06-16 13:43 ` [RFC PATCH 11/19] kvm: x86: Hook in kvmi_breakpoint_event() Adalbert Lazar
2017-06-21 11:48   ` Paolo Bonzini
2017-06-21 12:37     ` Mihai Donțu
2017-06-16 13:43 ` [RFC PATCH 12/19] kvm: x86: Hook in kvmi_trap_event() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 13/19] kvm: x86: Hook in kvmi_cr_event() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 14/19] kvm: x86: Hook in kvmi_xsetbv_event() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 15/19] kvm: x86: Hook in kvmi_msr_event() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 16/19] kvm: x86: Change the emulation context Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 17/19] kvm: x86: Hook in kvmi_vmcall_event() Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 18/19] kvm: x86: Set the new spte flags before entering the guest Adalbert Lazar
2017-06-16 13:43 ` [RFC PATCH 19/19] kvm: x86: Handle KVM_REQ_INTROSPECTION Adalbert Lazar
2017-06-16 14:45 ` [RFC PATCH 00/19] Guest introspection Jan Kiszka
2017-06-16 15:18   ` Mihai Donțu
2017-06-16 15:34     ` Jan Kiszka
2017-06-16 15:59       ` Mihai Donțu
2017-06-19  9:39       ` Stefan Hajnoczi
2017-06-20 14:58         ` alazar
2017-06-20 15:03           ` Jan Kiszka
2017-06-21 11:04           ` Stefan Hajnoczi
2017-06-21 13:25             ` Paolo Bonzini
2017-06-27 16:12               ` Mihai Donțu
2017-06-27 16:23                 ` Paolo Bonzini
2017-06-16 17:05     ` Paolo Bonzini
2017-06-16 17:27       ` Jan Kiszka

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170616134348.17725-11-alazar@bitdefender.com \
    --to=alazar@bitdefender.com \
    --cc=kvm@vger.kernel.org \
    --cc=mdontu@bitdefender.com \
    --cc=pbonzini@redhat.com \
    --cc=rkrcmar@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox