From nobody Thu Apr 2 21:58:33 2026 Received: from out-173.mta0.migadu.com (out-173.mta0.migadu.com [91.218.175.173]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id E7BAA35EDAF for ; Thu, 12 Feb 2026 23:08:25 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=91.218.175.173 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770937713; cv=none; b=FrQciD1i9W5W3P5mXWjYJYzeWCXigojLLzBiA0ArZYx0DkPvDblmMtS5HhUkzn/Is3ozn5ui6wVji4eFiobTqAz1bqeTepCKiIDOBR6l37W/GdASPm6yWQIpUk2HGvoxBGNitO5wInMcWO7TRwjzNX69SL5FFFDoGcc7+glALjQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1770937713; c=relaxed/simple; bh=bAOaJdOA04AkQxS17TJBTKFOnGSDb1vT1i6dJxSl2nQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=NMEBYQGfThvSV4qHSZASSg/P30qKHR0ZCEMDOZYevcU/hZzcNBqT5G5xbPKk46Npwi7RU47ufvH5/vTGCXREyiiJ/9gUHGpwJKJKcC1+vI3gytqwCJEW9Vw/PkyvvYchP9n+EMfOVu1bvahj48izh23e2s71TdTGJp38OirNfkc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev; spf=pass smtp.mailfrom=linux.dev; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b=m2ge7CAD; arc=none smtp.client-ip=91.218.175.173 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=linux.dev Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=linux.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linux.dev header.i=@linux.dev header.b="m2ge7CAD" X-Report-Abuse: Please report any abuse attempt to abuse@migadu.com and include these headers. DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linux.dev; s=key1; t=1770937704; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=Xv3/xFFAZoY8Sit0MX/CbnOjm9pEWm9G+ntyH+it1HQ=; b=m2ge7CADwE8J8TFiGy/AGC7xyp/ExrzJvwKT0sGyjjpZsBBI0nzZWmA8eHVxadU0xR8R1J lSsYeG/39iO2W4/O0QtJwfGwJqjtoSnSN1YwTysAxheQtBKNfWic7PSa8eyiJJ3p4wWTG1 GJJWWYso5N7wRV3x9Jb5YqzQhI2462M= From: Yosry Ahmed To: Sean Christopherson Cc: Paolo Bonzini , kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Yosry Ahmed Subject: [RFC PATCH 5/5] DO NOT MERGE: KVM: selftests: Reproduce nested RIP restore bug Date: Thu, 12 Feb 2026 23:07:51 +0000 Message-ID: <20260212230751.1871720-6-yosry.ahmed@linux.dev> In-Reply-To: <20260212230751.1871720-1-yosry.ahmed@linux.dev> References: <20260212230751.1871720-1-yosry.ahmed@linux.dev> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Migadu-Flow: FLOW_OUT Content-Type: text/plain; charset="utf-8" Update svm_nested_soft_inject_test such that L1 syncs to userspace before running L2. The test then enables single-stepping and steps through guest code until VMRUN is execute, and saves/restores the VM immediately after (before L2 runs). This reproduces a bug in save/restore where L2's RIP is not used correctly to construct the vmcb02 at the destination. Signed-off-by: Yosry Ahmed --- .../testing/selftests/kvm/lib/x86/processor.c | 3 + .../kvm/x86/svm_nested_soft_inject_test.c | 74 +++++++++++++++---- 2 files changed, 61 insertions(+), 16 deletions(-) diff --git a/tools/testing/selftests/kvm/lib/x86/processor.c b/tools/testin= g/selftests/kvm/lib/x86/processor.c index fab18e9be66c..3e8d516ec8d3 100644 --- a/tools/testing/selftests/kvm/lib/x86/processor.c +++ b/tools/testing/selftests/kvm/lib/x86/processor.c @@ -1291,6 +1291,9 @@ void vcpu_load_state(struct kvm_vcpu *vcpu, struct kv= m_x86_state *state) =20 if (state->nested.size) vcpu_nested_state_set(vcpu, &state->nested); + + /* Switch between this and the call above */ + // vcpu_regs_set(vcpu, &state->regs); } =20 void kvm_x86_state_cleanup(struct kvm_x86_state *state) diff --git a/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c = b/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c index 4bd1655f9e6d..dfefd8eed392 100644 --- a/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c +++ b/tools/testing/selftests/kvm/x86/svm_nested_soft_inject_test.c @@ -101,6 +101,7 @@ static void l1_guest_code(struct svm_test_data *svm, ui= nt64_t is_nmi, uint64_t i vmcb->control.next_rip =3D vmcb->save.rip; } =20 + GUEST_SYNC(true); run_guest(vmcb, svm->vmcb_gpa); __GUEST_ASSERT(vmcb->control.exit_code =3D=3D SVM_EXIT_VMMCALL, "Expected VMMCAL #VMEXIT, got '0x%lx', info1 =3D '0x%lx, info2 = =3D '0x%lx'", @@ -131,6 +132,7 @@ static void l1_guest_code(struct svm_test_data *svm, ui= nt64_t is_nmi, uint64_t i /* The return address pushed on stack, skip over UD2 */ vmcb->control.next_rip =3D vmcb->save.rip + 2; =20 + GUEST_SYNC(true); run_guest(vmcb, svm->vmcb_gpa); __GUEST_ASSERT(vmcb->control.exit_code =3D=3D SVM_EXIT_HLT, "Expected HLT #VMEXIT, got '0x%lx', info1 =3D '0x%lx, info2 =3D '= 0x%lx'", @@ -140,6 +142,24 @@ static void l1_guest_code(struct svm_test_data *svm, u= int64_t is_nmi, uint64_t i GUEST_DONE(); } =20 +static struct kvm_vcpu *save_and_restore_vm(struct kvm_vm *vm, struct kvm_= vcpu *vcpu) +{ + struct kvm_x86_state *state =3D vcpu_save_state(vcpu); + + kvm_vm_release(vm); + vcpu =3D vm_recreate_with_one_vcpu(vm); + vcpu_load_state(vcpu, state); + kvm_x86_state_cleanup(state); + return vcpu; +} + +static bool is_nested_run_pending(struct kvm_vcpu *vcpu) +{ + struct kvm_x86_state *state =3D vcpu_save_state(vcpu); + + return state->nested.size && (state->nested.flags & KVM_STATE_NESTED_RUN_= PENDING); +} + static void run_test(bool is_nmi) { struct kvm_vcpu *vcpu; @@ -173,22 +193,44 @@ static void run_test(bool is_nmi) memset(&debug, 0, sizeof(debug)); vcpu_guest_debug_set(vcpu, &debug); =20 - struct ucall uc; - - alarm(2); - vcpu_run(vcpu); - alarm(0); - TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); - - switch (get_ucall(vcpu, &uc)) { - case UCALL_ABORT: - REPORT_GUEST_ASSERT(uc); - break; - /* NOT REACHED */ - case UCALL_DONE: - goto done; - default: - TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd); + for (;;) { + struct kvm_guest_debug debug; + struct ucall uc; + + alarm(2); + vcpu_run(vcpu); + alarm(0); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_SYNC: + /* + * L1 syncs before calling run_guest(), single-step over + * all instructions until VMRUN, and save+restore right + * after it (before L2 actually runs). + */ + debug.control =3D KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP; + vcpu_guest_debug_set(vcpu, &debug); + + do { + vcpu_run(vcpu); + TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_DEBUG); + } while (!is_nested_run_pending(vcpu)); + + memset(&debug, 0, sizeof(debug)); + vcpu_guest_debug_set(vcpu, &debug); + vcpu =3D save_and_restore_vm(vm, vcpu); + break; + + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + break; + /* NOT REACHED */ + case UCALL_DONE: + goto done; + default: + TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd); + } } done: kvm_vm_free(vm); --=20 2.53.0.273.g2a3d683680-goog