Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75d61fbc authored by Takuya Yoshikawa's avatar Takuya Yoshikawa Committed by Marcelo Tosatti
Browse files

KVM: set_memory_region: Disallow changing read-only attribute later



As Xiao pointed out, there are a few problems with it:
 - kvm_arch_commit_memory_region() write protects the memory slot only
   for GET_DIRTY_LOG when modifying the flags.
 - FNAME(sync_page) uses the old spte value to set a new one without
   checking KVM_MEM_READONLY flag.

Since we flush all shadow pages when creating a new slot, the simplest
fix is to disallow such problematic flag changes: this is safe because
no one is doing such things.

Reviewed-by: default avatarGleb Natapov <gleb@redhat.com>
Signed-off-by: default avatarTakuya Yoshikawa <yoshikawa_takuya_b1@lab.ntt.co.jp>
Cc: Xiao Guangrong <xiaoguangrong@linux.vnet.ibm.com>
Cc: Alex Williamson <alex.williamson@redhat.com>
Signed-off-by: default avatarMarcelo Tosatti <mtosatti@redhat.com>
parent f64c0398
Loading
Loading
Loading
Loading
+6 −6
Original line number Diff line number Diff line
@@ -874,12 +874,12 @@ It is recommended that the lower 21 bits of guest_phys_addr and userspace_addr
be identical.  This allows large pages in the guest to be backed by large
pages in the host.

The flags field supports two flag, KVM_MEM_LOG_DIRTY_PAGES, which instructs
kvm to keep track of writes to memory within the slot.  See KVM_GET_DIRTY_LOG
ioctl.  The KVM_CAP_READONLY_MEM capability indicates the availability of the
KVM_MEM_READONLY flag.  When this flag is set for a memory region, KVM only
allows read accesses.  Writes will be posted to userspace as KVM_EXIT_MMIO
exits.
The flags field supports two flags: KVM_MEM_LOG_DIRTY_PAGES and
KVM_MEM_READONLY.  The former can be set to instruct KVM to keep track of
writes to memory within the slot.  See KVM_GET_DIRTY_LOG ioctl to know how to
use it.  The latter can be set, if KVM_CAP_READONLY_MEM capability allows it,
to make a new slot read-only.  In this case, writes to this memory will be
posted to userspace as KVM_EXIT_MMIO exits.

When the KVM_CAP_SYNC_MMU capability is available, changes in the backing of
the memory region are automatically reflected into the guest.  For example, an
+12 −23
Original line number Diff line number Diff line
@@ -754,7 +754,6 @@ int __kvm_set_memory_region(struct kvm *kvm,
	struct kvm_memory_slot *slot;
	struct kvm_memory_slot old, new;
	struct kvm_memslots *slots = NULL, *old_memslots;
	bool old_iommu_mapped;
	enum kvm_mr_change change;

	r = check_memory_region_flags(mem);
@@ -797,15 +796,14 @@ int __kvm_set_memory_region(struct kvm *kvm,
	new.npages = npages;
	new.flags = mem->flags;

	old_iommu_mapped = old.npages;

	r = -EINVAL;
	if (npages) {
		if (!old.npages)
			change = KVM_MR_CREATE;
		else { /* Modify an existing slot. */
			if ((mem->userspace_addr != old.userspace_addr) ||
			    (npages != old.npages))
			    (npages != old.npages) ||
			    ((new.flags ^ old.flags) & KVM_MEM_READONLY))
				goto out;

			if (base_gfn != old.base_gfn)
@@ -867,7 +865,6 @@ int __kvm_set_memory_region(struct kvm *kvm,

		/* slot was deleted or moved, clear iommu mapping */
		kvm_iommu_unmap_pages(kvm, &old);
		old_iommu_mapped = false;
		/* From this point no new shadow pages pointing to a deleted,
		 * or moved, memslot will be created.
		 *
@@ -898,26 +895,18 @@ int __kvm_set_memory_region(struct kvm *kvm,

	/*
	 * IOMMU mapping:  New slots need to be mapped.  Old slots need to be
	 * un-mapped and re-mapped if their base changes or if flags that the
	 * iommu cares about change (read-only).  Base change unmapping is
	 * handled above with slot deletion, so we only unmap incompatible
	 * flags here.  Anything else the iommu might care about for existing
	 * slots (size changes, userspace addr changes) is disallowed above,
	 * so any other attribute changes getting here can be skipped.
	 * un-mapped and re-mapped if their base changes.  Since base change
	 * unmapping is handled above with slot deletion, mapping alone is
	 * needed here.  Anything else the iommu might care about for existing
	 * slots (size changes, userspace addr changes and read-only flag
	 * changes) is disallowed above, so any other attribute changes getting
	 * here can be skipped.
	 */
	if (change != KVM_MR_DELETE) {
		if (old_iommu_mapped &&
		    ((new.flags ^ old.flags) & KVM_MEM_READONLY)) {
			kvm_iommu_unmap_pages(kvm, &old);
			old_iommu_mapped = false;
		}

		if (!old_iommu_mapped) {
	if ((change == KVM_MR_CREATE) || (change == KVM_MR_MOVE)) {
		r = kvm_iommu_map_pages(kvm, &new);
		if (r)
			goto out_slots;
	}
	}

	/* actual memory is freed via old in kvm_free_physmem_slot below */
	if (change == KVM_MR_DELETE) {