Commit edba23e5 authored by Gleb Natapov's avatar Gleb Natapov Committed by Avi Kivity

KVM: Return EFAULT from kvm ioctl when guest accesses bad area

Currently if guest access address that belongs to memory slot but is not
backed up by page or page is read only KVM treats it like MMIO access.
Remove that capability. It was never part of the interface and should
not be relied upon.
Signed-off-by: default avatarGleb Natapov <gleb@redhat.com>
Signed-off-by: default avatarAvi Kivity <avi@redhat.com>
parent fa7bff8f
...@@ -2078,7 +2078,9 @@ static int kvm_handle_bad_page(struct kvm *kvm, gfn_t gfn, pfn_t pfn) ...@@ -2078,7 +2078,9 @@ static int kvm_handle_bad_page(struct kvm *kvm, gfn_t gfn, pfn_t pfn)
if (is_hwpoison_pfn(pfn)) { if (is_hwpoison_pfn(pfn)) {
kvm_send_hwpoison_signal(kvm, gfn); kvm_send_hwpoison_signal(kvm, gfn);
return 0; return 0;
} } else if (is_fault_pfn(pfn))
return -EFAULT;
return 1; return 1;
} }
......
...@@ -269,6 +269,7 @@ extern pfn_t bad_pfn; ...@@ -269,6 +269,7 @@ extern pfn_t bad_pfn;
int is_error_page(struct page *page); int is_error_page(struct page *page);
int is_error_pfn(pfn_t pfn); int is_error_pfn(pfn_t pfn);
int is_hwpoison_pfn(pfn_t pfn); int is_hwpoison_pfn(pfn_t pfn);
int is_fault_pfn(pfn_t pfn);
int kvm_is_error_hva(unsigned long addr); int kvm_is_error_hva(unsigned long addr);
int kvm_set_memory_region(struct kvm *kvm, int kvm_set_memory_region(struct kvm *kvm,
struct kvm_userspace_memory_region *mem, struct kvm_userspace_memory_region *mem,
......
...@@ -96,6 +96,9 @@ static bool largepages_enabled = true; ...@@ -96,6 +96,9 @@ static bool largepages_enabled = true;
static struct page *hwpoison_page; static struct page *hwpoison_page;
static pfn_t hwpoison_pfn; static pfn_t hwpoison_pfn;
static struct page *fault_page;
static pfn_t fault_pfn;
inline int kvm_is_mmio_pfn(pfn_t pfn) inline int kvm_is_mmio_pfn(pfn_t pfn)
{ {
if (pfn_valid(pfn)) { if (pfn_valid(pfn)) {
...@@ -815,13 +818,13 @@ EXPORT_SYMBOL_GPL(kvm_disable_largepages); ...@@ -815,13 +818,13 @@ EXPORT_SYMBOL_GPL(kvm_disable_largepages);
int is_error_page(struct page *page) int is_error_page(struct page *page)
{ {
return page == bad_page || page == hwpoison_page; return page == bad_page || page == hwpoison_page || page == fault_page;
} }
EXPORT_SYMBOL_GPL(is_error_page); EXPORT_SYMBOL_GPL(is_error_page);
int is_error_pfn(pfn_t pfn) int is_error_pfn(pfn_t pfn)
{ {
return pfn == bad_pfn || pfn == hwpoison_pfn; return pfn == bad_pfn || pfn == hwpoison_pfn || pfn == fault_pfn;
} }
EXPORT_SYMBOL_GPL(is_error_pfn); EXPORT_SYMBOL_GPL(is_error_pfn);
...@@ -831,6 +834,12 @@ int is_hwpoison_pfn(pfn_t pfn) ...@@ -831,6 +834,12 @@ int is_hwpoison_pfn(pfn_t pfn)
} }
EXPORT_SYMBOL_GPL(is_hwpoison_pfn); EXPORT_SYMBOL_GPL(is_hwpoison_pfn);
int is_fault_pfn(pfn_t pfn)
{
return pfn == fault_pfn;
}
EXPORT_SYMBOL_GPL(is_fault_pfn);
static inline unsigned long bad_hva(void) static inline unsigned long bad_hva(void)
{ {
return PAGE_OFFSET; return PAGE_OFFSET;
...@@ -959,8 +968,8 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr) ...@@ -959,8 +968,8 @@ static pfn_t hva_to_pfn(struct kvm *kvm, unsigned long addr)
if (vma == NULL || addr < vma->vm_start || if (vma == NULL || addr < vma->vm_start ||
!(vma->vm_flags & VM_PFNMAP)) { !(vma->vm_flags & VM_PFNMAP)) {
up_read(&current->mm->mmap_sem); up_read(&current->mm->mmap_sem);
get_page(bad_page); get_page(fault_page);
return page_to_pfn(bad_page); return page_to_pfn(fault_page);
} }
pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; pfn = ((addr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff;
...@@ -2226,6 +2235,15 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, ...@@ -2226,6 +2235,15 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align,
hwpoison_pfn = page_to_pfn(hwpoison_page); hwpoison_pfn = page_to_pfn(hwpoison_page);
fault_page = alloc_page(GFP_KERNEL | __GFP_ZERO);
if (fault_page == NULL) {
r = -ENOMEM;
goto out_free_0;
}
fault_pfn = page_to_pfn(fault_page);
if (!zalloc_cpumask_var(&cpus_hardware_enabled, GFP_KERNEL)) { if (!zalloc_cpumask_var(&cpus_hardware_enabled, GFP_KERNEL)) {
r = -ENOMEM; r = -ENOMEM;
goto out_free_0; goto out_free_0;
...@@ -2298,6 +2316,8 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align, ...@@ -2298,6 +2316,8 @@ int kvm_init(void *opaque, unsigned vcpu_size, unsigned vcpu_align,
out_free_0a: out_free_0a:
free_cpumask_var(cpus_hardware_enabled); free_cpumask_var(cpus_hardware_enabled);
out_free_0: out_free_0:
if (fault_page)
__free_page(fault_page);
if (hwpoison_page) if (hwpoison_page)
__free_page(hwpoison_page); __free_page(hwpoison_page);
__free_page(bad_page); __free_page(bad_page);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment