Commit 2031f287 authored by Sean Christopherson's avatar Sean Christopherson Committed by Paolo Bonzini
Browse files

KVM: Add helpers to wrap vcpu->srcu_idx and yell if it's abused



Add wrappers to acquire/release KVM's SRCU lock when stashing the index
in vcpu->src_idx, along with rudimentary detection of illegal usage,
e.g. re-acquiring SRCU and thus overwriting vcpu->src_idx.  Because the
SRCU index is (currently) either 0 or 1, illegal nesting bugs can go
unnoticed for quite some time and only cause problems when the nested
lock happens to get a different index.

Wrap the WARNs in PROVE_RCU=y, and make them ONCE, otherwise KVM will
likely yell so loudly that it will bring the kernel to its knees.

Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
Tested-by: default avatarFabiano Rosas <farosas@linux.ibm.com>
Message-Id: <20220415004343.2203171-4-seanjc@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent fdd6f6ac
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -168,9 +168,10 @@ int kvmppc_mmu_walk_radix_tree(struct kvm_vcpu *vcpu, gva_t eaddr,
			return -EINVAL;
		/* Read the entry from guest memory */
		addr = base + (index * sizeof(rpte));
		vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);

		kvm_vcpu_srcu_read_lock(vcpu);
		ret = kvm_read_guest(kvm, addr, &rpte, sizeof(rpte));
		srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
		kvm_vcpu_srcu_read_unlock(vcpu);
		if (ret) {
			if (pte_ret_p)
				*pte_ret_p = addr;
@@ -246,9 +247,9 @@ int kvmppc_mmu_radix_translate_table(struct kvm_vcpu *vcpu, gva_t eaddr,

	/* Read the table to find the root of the radix tree */
	ptbl = (table & PRTB_MASK) + (table_index * sizeof(entry));
	vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
	kvm_vcpu_srcu_read_lock(vcpu);
	ret = kvm_read_guest(kvm, ptbl, &entry, sizeof(entry));
	srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
	kvm_vcpu_srcu_read_unlock(vcpu);
	if (ret)
		return ret;

+8 −8
Original line number Diff line number Diff line
@@ -306,10 +306,10 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
	/* copy parameters in */
	hv_ptr = kvmppc_get_gpr(vcpu, 4);
	regs_ptr = kvmppc_get_gpr(vcpu, 5);
	vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
	kvm_vcpu_srcu_read_lock(vcpu);
	err = kvmhv_read_guest_state_and_regs(vcpu, &l2_hv, &l2_regs,
					      hv_ptr, regs_ptr);
	srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
	kvm_vcpu_srcu_read_unlock(vcpu);
	if (err)
		return H_PARAMETER;

@@ -410,10 +410,10 @@ long kvmhv_enter_nested_guest(struct kvm_vcpu *vcpu)
		byteswap_hv_regs(&l2_hv);
		byteswap_pt_regs(&l2_regs);
	}
	vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
	kvm_vcpu_srcu_read_lock(vcpu);
	err = kvmhv_write_guest_state_and_regs(vcpu, &l2_hv, &l2_regs,
					       hv_ptr, regs_ptr);
	srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
	kvm_vcpu_srcu_read_unlock(vcpu);
	if (err)
		return H_AUTHORITY;

@@ -600,16 +600,16 @@ long kvmhv_copy_tofrom_guest_nested(struct kvm_vcpu *vcpu)
			goto not_found;

		/* Write what was loaded into our buffer back to the L1 guest */
		vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
		kvm_vcpu_srcu_read_lock(vcpu);
		rc = kvm_vcpu_write_guest(vcpu, gp_to, buf, n);
		srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
		kvm_vcpu_srcu_read_unlock(vcpu);
		if (rc)
			goto not_found;
	} else {
		/* Load the data to be stored from the L1 guest into our buf */
		vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
		kvm_vcpu_srcu_read_lock(vcpu);
		rc = kvm_vcpu_read_guest(vcpu, gp_from, buf, n);
		srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
		kvm_vcpu_srcu_read_unlock(vcpu);
		if (rc)
			goto not_found;

+2 −2
Original line number Diff line number Diff line
@@ -229,9 +229,9 @@ int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
	 */
	args_phys = kvmppc_get_gpr(vcpu, 4) & KVM_PAM;

	vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
	kvm_vcpu_srcu_read_lock(vcpu);
	rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
	srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
	kvm_vcpu_srcu_read_unlock(vcpu);
	if (rc)
		goto fail;

+2 −2
Original line number Diff line number Diff line
@@ -425,9 +425,9 @@ int kvmppc_ld(struct kvm_vcpu *vcpu, ulong *eaddr, int size, void *ptr,
		return EMULATE_DONE;
	}

	vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
	kvm_vcpu_srcu_read_lock(vcpu);
	rc = kvm_read_guest(vcpu->kvm, pte.raddr, ptr, size);
	srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
	kvm_vcpu_srcu_read_unlock(vcpu);
	if (rc)
		return EMULATE_DO_MMIO;

+8 −8
Original line number Diff line number Diff line
@@ -727,13 +727,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
	/* Mark this VCPU ran at least once */
	vcpu->arch.ran_atleast_once = true;

	vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
	kvm_vcpu_srcu_read_lock(vcpu);

	/* Process MMIO value returned from user-space */
	if (run->exit_reason == KVM_EXIT_MMIO) {
		ret = kvm_riscv_vcpu_mmio_return(vcpu, vcpu->run);
		if (ret) {
			srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
			kvm_vcpu_srcu_read_unlock(vcpu);
			return ret;
		}
	}
@@ -742,13 +742,13 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
	if (run->exit_reason == KVM_EXIT_RISCV_SBI) {
		ret = kvm_riscv_vcpu_sbi_return(vcpu, vcpu->run);
		if (ret) {
			srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
			kvm_vcpu_srcu_read_unlock(vcpu);
			return ret;
		}
	}

	if (run->immediate_exit) {
		srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
		kvm_vcpu_srcu_read_unlock(vcpu);
		return -EINTR;
	}

@@ -787,7 +787,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
		 */
		vcpu->mode = IN_GUEST_MODE;

		srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
		kvm_vcpu_srcu_read_unlock(vcpu);
		smp_mb__after_srcu_read_unlock();

		/*
@@ -805,7 +805,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
			vcpu->mode = OUTSIDE_GUEST_MODE;
			local_irq_enable();
			preempt_enable();
			vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
			kvm_vcpu_srcu_read_lock(vcpu);
			continue;
		}

@@ -849,7 +849,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)

		preempt_enable();

		vcpu->srcu_idx = srcu_read_lock(&vcpu->kvm->srcu);
		kvm_vcpu_srcu_read_lock(vcpu);

		ret = kvm_riscv_vcpu_exit(vcpu, run, &trap);
	}
@@ -858,7 +858,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)

	vcpu_put(vcpu);

	srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx);
	kvm_vcpu_srcu_read_unlock(vcpu);

	return ret;
}
Loading