1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 2f08c3bdfSopenharmony_ci/* 3f08c3bdfSopenharmony_ci * Copyright (C) 2023 SUSE LLC 4f08c3bdfSopenharmony_ci * Author: Nicolai Stange <nstange@suse.de> 5f08c3bdfSopenharmony_ci * LTP port: Martin Doucha <mdoucha@suse.cz> 6f08c3bdfSopenharmony_ci */ 7f08c3bdfSopenharmony_ci 8f08c3bdfSopenharmony_ci/*\ 9f08c3bdfSopenharmony_ci * CVE 2021-3656 10f08c3bdfSopenharmony_ci * 11f08c3bdfSopenharmony_ci * Check that KVM correctly intercepts VMSAVE and VMLOAD instructions 12f08c3bdfSopenharmony_ci * in a nested virtual machine even when the parent guest disables 13f08c3bdfSopenharmony_ci * intercepting either instruction. If KVM does not override the disabled 14f08c3bdfSopenharmony_ci * intercepts, it'll give the nested VM read/write access to a few bytes 15f08c3bdfSopenharmony_ci * of an arbitrary physical memory page. Unauthorized memory access fixed in: 16f08c3bdfSopenharmony_ci * 17f08c3bdfSopenharmony_ci * commit c7dfa4009965a9b2d7b329ee970eb8da0d32f0bc 18f08c3bdfSopenharmony_ci * Author: Maxim Levitsky <mlevitsk@redhat.com> 19f08c3bdfSopenharmony_ci * Date: Mon Jul 19 16:05:00 2021 +0300 20f08c3bdfSopenharmony_ci * 21f08c3bdfSopenharmony_ci * KVM: nSVM: always intercept VMLOAD/VMSAVE when nested (CVE-2021-3656) 22f08c3bdfSopenharmony_ci */ 23f08c3bdfSopenharmony_ci 24f08c3bdfSopenharmony_ci#include "kvm_test.h" 25f08c3bdfSopenharmony_ci 26f08c3bdfSopenharmony_ci#ifdef COMPILE_PAYLOAD 27f08c3bdfSopenharmony_ci#if defined(__i386__) || defined(__x86_64__) 28f08c3bdfSopenharmony_ci 29f08c3bdfSopenharmony_ci#include "kvm_x86_svm.h" 30f08c3bdfSopenharmony_ci 31f08c3bdfSopenharmony_cistatic void *vmsave_buf; 32f08c3bdfSopenharmony_ci 33f08c3bdfSopenharmony_ci/* Load FS, GS, TR and LDTR state from vmsave_buf */ 34f08c3bdfSopenharmony_cistatic int guest_vmload(void) 35f08c3bdfSopenharmony_ci{ 36f08c3bdfSopenharmony_ci asm ( 37f08c3bdfSopenharmony_ci "vmload %0\n" 38f08c3bdfSopenharmony_ci : 39f08c3bdfSopenharmony_ci : "a" (vmsave_buf) 40f08c3bdfSopenharmony_ci ); 41f08c3bdfSopenharmony_ci return 0; 42f08c3bdfSopenharmony_ci} 43f08c3bdfSopenharmony_ci 44f08c3bdfSopenharmony_ci/* Save current FS, GS, TR and LDTR state to vmsave_buf */ 45f08c3bdfSopenharmony_cistatic int guest_vmsave(void) 46f08c3bdfSopenharmony_ci{ 47f08c3bdfSopenharmony_ci asm ( 48f08c3bdfSopenharmony_ci "vmsave %0\n" 49f08c3bdfSopenharmony_ci : 50f08c3bdfSopenharmony_ci : "a" (vmsave_buf) 51f08c3bdfSopenharmony_ci ); 52f08c3bdfSopenharmony_ci return 0; 53f08c3bdfSopenharmony_ci} 54f08c3bdfSopenharmony_ci 55f08c3bdfSopenharmony_cistatic int cmp_descriptor(const struct kvm_vmcb_descriptor *a, 56f08c3bdfSopenharmony_ci const struct kvm_vmcb_descriptor *b) 57f08c3bdfSopenharmony_ci{ 58f08c3bdfSopenharmony_ci int ret; 59f08c3bdfSopenharmony_ci 60f08c3bdfSopenharmony_ci ret = a->selector != b->selector; 61f08c3bdfSopenharmony_ci ret = ret || a->attrib != b->attrib; 62f08c3bdfSopenharmony_ci ret = ret || a->limit != b->limit; 63f08c3bdfSopenharmony_ci ret = ret || a->base != b->base; 64f08c3bdfSopenharmony_ci return ret; 65f08c3bdfSopenharmony_ci} 66f08c3bdfSopenharmony_ci 67f08c3bdfSopenharmony_ci/* Return non-zero if the VMCB fields touched by vmsave/vmload differ */ 68f08c3bdfSopenharmony_cistatic int cmp_vmcb(const struct kvm_vmcb *a, const struct kvm_vmcb *b) 69f08c3bdfSopenharmony_ci{ 70f08c3bdfSopenharmony_ci int ret; 71f08c3bdfSopenharmony_ci 72f08c3bdfSopenharmony_ci ret = cmp_descriptor(&a->fs, &b->fs); 73f08c3bdfSopenharmony_ci ret = ret || cmp_descriptor(&a->gs, &b->gs); 74f08c3bdfSopenharmony_ci ret = ret || cmp_descriptor(&a->tr, &b->tr); 75f08c3bdfSopenharmony_ci ret = ret || cmp_descriptor(&a->ldtr, &b->ldtr); 76f08c3bdfSopenharmony_ci ret = ret || a->kernel_gs_base != b->kernel_gs_base; 77f08c3bdfSopenharmony_ci ret = ret || a->star != b->star; 78f08c3bdfSopenharmony_ci ret = ret || a->lstar != b->lstar; 79f08c3bdfSopenharmony_ci ret = ret || a->cstar != b->cstar; 80f08c3bdfSopenharmony_ci ret = ret || a->sfmask != b->sfmask; 81f08c3bdfSopenharmony_ci ret = ret || a->sysenter_cs != b->sysenter_cs; 82f08c3bdfSopenharmony_ci ret = ret || a->sysenter_esp != b->sysenter_esp; 83f08c3bdfSopenharmony_ci ret = ret || a->sysenter_eip != b->sysenter_eip; 84f08c3bdfSopenharmony_ci return ret; 85f08c3bdfSopenharmony_ci} 86f08c3bdfSopenharmony_ci 87f08c3bdfSopenharmony_civoid main(void) 88f08c3bdfSopenharmony_ci{ 89f08c3bdfSopenharmony_ci uint16_t ss; 90f08c3bdfSopenharmony_ci uint64_t rsp; 91f08c3bdfSopenharmony_ci struct kvm_svm_vcpu *vcpu; 92f08c3bdfSopenharmony_ci 93f08c3bdfSopenharmony_ci kvm_init_svm(); 94f08c3bdfSopenharmony_ci vcpu = kvm_create_svm_vcpu(guest_vmload, 1); 95f08c3bdfSopenharmony_ci kvm_vmcb_set_intercept(vcpu->vmcb, SVM_INTERCEPT_VMLOAD, 0); 96f08c3bdfSopenharmony_ci vmsave_buf = kvm_alloc_vmcb(); 97f08c3bdfSopenharmony_ci 98f08c3bdfSopenharmony_ci /* Save allocated stack for later VM reinit */ 99f08c3bdfSopenharmony_ci ss = vcpu->vmcb->ss.selector; 100f08c3bdfSopenharmony_ci rsp = vcpu->vmcb->rsp; 101f08c3bdfSopenharmony_ci 102f08c3bdfSopenharmony_ci /* Load partial state from vmsave_buf and save it to vcpu->vmcb */ 103f08c3bdfSopenharmony_ci kvm_svm_vmrun(vcpu); 104f08c3bdfSopenharmony_ci 105f08c3bdfSopenharmony_ci if (vcpu->vmcb->exitcode != SVM_EXIT_HLT) 106f08c3bdfSopenharmony_ci tst_brk(TBROK, "Nested VM exited unexpectedly"); 107f08c3bdfSopenharmony_ci 108f08c3bdfSopenharmony_ci if (cmp_vmcb(vcpu->vmcb, vmsave_buf)) { 109f08c3bdfSopenharmony_ci tst_res(TFAIL, "Nested VM can read host memory"); 110f08c3bdfSopenharmony_ci return; 111f08c3bdfSopenharmony_ci } 112f08c3bdfSopenharmony_ci 113f08c3bdfSopenharmony_ci /* Load state from vcpu->vmcb and save it to vmsave_buf */ 114f08c3bdfSopenharmony_ci memset(vmsave_buf, 0xaa, sizeof(struct kvm_vmcb)); 115f08c3bdfSopenharmony_ci kvm_init_guest_vmcb(vcpu->vmcb, 1, ss, (void *)rsp, guest_vmsave); 116f08c3bdfSopenharmony_ci kvm_vmcb_set_intercept(vcpu->vmcb, SVM_INTERCEPT_VMSAVE, 0); 117f08c3bdfSopenharmony_ci kvm_svm_vmrun(vcpu); 118f08c3bdfSopenharmony_ci 119f08c3bdfSopenharmony_ci if (vcpu->vmcb->exitcode != SVM_EXIT_HLT) 120f08c3bdfSopenharmony_ci tst_brk(TBROK, "Nested VM exited unexpectedly"); 121f08c3bdfSopenharmony_ci 122f08c3bdfSopenharmony_ci if (cmp_vmcb(vcpu->vmcb, vmsave_buf)) { 123f08c3bdfSopenharmony_ci tst_res(TFAIL, "Nested VM can overwrite host memory"); 124f08c3bdfSopenharmony_ci return; 125f08c3bdfSopenharmony_ci } 126f08c3bdfSopenharmony_ci 127f08c3bdfSopenharmony_ci tst_res(TPASS, "VMLOAD and VMSAVE were intercepted by kernel"); 128f08c3bdfSopenharmony_ci} 129f08c3bdfSopenharmony_ci 130f08c3bdfSopenharmony_ci#else /* defined(__i386__) || defined(__x86_64__) */ 131f08c3bdfSopenharmony_ciTST_TEST_TCONF("Test supported only on x86"); 132f08c3bdfSopenharmony_ci#endif /* defined(__i386__) || defined(__x86_64__) */ 133f08c3bdfSopenharmony_ci 134f08c3bdfSopenharmony_ci#else /* COMPILE_PAYLOAD */ 135f08c3bdfSopenharmony_ci 136f08c3bdfSopenharmony_cistatic struct tst_test test = { 137f08c3bdfSopenharmony_ci .test_all = tst_kvm_run, 138f08c3bdfSopenharmony_ci .setup = tst_kvm_setup, 139f08c3bdfSopenharmony_ci .cleanup = tst_kvm_cleanup, 140f08c3bdfSopenharmony_ci .supported_archs = (const char *const []) { 141f08c3bdfSopenharmony_ci "x86_64", 142f08c3bdfSopenharmony_ci "x86", 143f08c3bdfSopenharmony_ci NULL 144f08c3bdfSopenharmony_ci }, 145f08c3bdfSopenharmony_ci .tags = (struct tst_tag[]){ 146f08c3bdfSopenharmony_ci {"linux-git", "c7dfa4009965"}, 147f08c3bdfSopenharmony_ci {"CVE", "2021-3656"}, 148f08c3bdfSopenharmony_ci {} 149f08c3bdfSopenharmony_ci } 150f08c3bdfSopenharmony_ci}; 151f08c3bdfSopenharmony_ci 152f08c3bdfSopenharmony_ci#endif /* COMPILE_PAYLOAD */ 153