From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from lists.ozlabs.org (lists.ozlabs.org [112.213.38.117]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 677C960A2604 for ; Fri, 30 Oct 2026 21:21:24 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key, unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20260707 header.b=ETZLAQHF Received: from boromir.ozlabs.org (localhost [127.0.0.1]) by lists.ozlabs.org (Postfix) with ESMTP id 4jGYtl2DGVz30V0; Sat, 31 Oct 2026 08:21:19 +1100 (AEDT) Authentication-Results: lists.ozlabs.org; arc=none smtp.remote-ip="2607:f8b0:4864:20::648" ARC-Seal: i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1793395279; cv=none; b=UcxobpmUYsCZCumlPelbrFe40aiaijcN1djL2UJdoCE0tNP7adfIESUEccIllgLYj3hvsB+RAUUrmntG5/UNSACEqtNzAaaEIbk/d+GfM2JXgnnZ2e86ZxxHpPo23EQL2eS2hMQsoSDNb+u636hI2zquFHmsZ93oXyIAa/ki4XkAPweuNBkTA+dSIh/XF/uH0MOW0LcmhM+bLs06O/ad2QBVSpHZ6yxKozztHrOV8/9WLCWu8qWLH/tO7lSfLqilyFvAEqNOiruaVPlAgytM/APtJh2SblkAkGjpE+t5G2gcsclz7fDMf6A/+lcCXwDPpDFKW1viRFlhyAU2sr4ngA== ARC-Message-Signature: i=1; a=rsa-sha256; d=lists.ozlabs.org; s=201707; t=1793395279; c=relaxed/relaxed; bh=pEIWclQ1SBWM1+Z99wz5ixAlhPbNjur+F04EjDXrB30=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=b9D/vLomBjGqwoS3eu96jXRqQYQUL8ei9Qjc6I5vJnqwqFK4+JI92l4r9W6OBz2OqNwOSdGaNRlNtjHPleEoAaAIpLdhEak6n5dUP8Q8541pqVSf01wtHVh138XgWiwcIyiz1GX2yGxXNwNUwBMNbnc1ljPmnGDvQk2MSiZFjpLghtcFmqDD2NjZVRHA4huOHAPFItG0/+IFi/B63qWoN5CSpTUjYw1aWIqvIBFJQymbK08QInQIgZmeD934VEvtPEq6kJQJeKC9csuhocCQopaQdkCq8x5wKeW0UjfqWN336hYGCCDnSyquKar4zjePh7zrYo3E3FDfyRdR9p7Q2w== ARC-Authentication-Results: i=1; lists.ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20260707 header.b=ETZLAQHF; dkim-atps=neutral; spf=pass (client-ip=2607:f8b0:4864:20::648; helo=mail-pl1-x648.google.com; envelope-from=3sgrlagykdfebxt62vz77z4x.v75416dg88v-wxe41bcb.7i4tub.7az@flex--seanjc.bounces.google.com; receiver=lists.ozlabs.org) smtp.mailfrom=flex--seanjc.bounces.google.com Authentication-Results: lists.ozlabs.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: lists.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=google.com header.i=@google.com header.a=rsa-sha256 header.s=20260707 header.b=ETZLAQHF; dkim-atps=neutral Authentication-Results: lists.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=flex--seanjc.bounces.google.com (client-ip=2607:f8b0:4864:20::648; helo=mail-pl1-x648.google.com; envelope-from=3sgrlagykdfebxt62vz77z4x.v75416dg88v-wxe41bcb.7i4tub.7az@flex--seanjc.bounces.google.com; receiver=lists.ozlabs.org) Received: from mail-pl1-x648.google.com (mail-pl1-x648.google.com [IPv6:2607:f8b0:4864:20::648]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange x25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by lists.ozlabs.org (Postfix) with ESMTPS id 4jGYtj0CSmz2yYK for ; Sat, 31 Oct 2026 08:21:16 +1100 (AEDT) Received: by mail-pl1-x648.google.com with SMTP id d9443c01a7336-2eaae62796dso14056825ad.0 for ; Fri, 30 Oct 2026 14:21:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20260707; t=1793395275; x=1794000075; darn=lists.ozlabs.org; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:reply-to:from:to:cc:subject:date:message-id :reply-to:content-type; bh=pEIWclQ1SBWM1+Z99wz5ixAlhPbNjur+F04EjDXrB30=; b=ETZLAQHFegROs2OhSzk3nqJwkivwBNyMLutDSGoEPoWSKxPV7MUjUf4+3S41VYdzSO CCjnnxYaLf02GVnHdBC7pjfOnKwECGpygzocXGtAXchaBrtDFdMtiO+9lRkaHMF0vNNm cwaZjJRtzjEGO0tK2qx0ooI03c/U+l+lyFGm3FsHd0w+qefJNsOjizNB8dCxpwL48j5S ohfazDrrMkUQ1FdSNL56k1i+7qQ8/nVTIAIIl+tzMLBZQQmeqgw7y9t93Xk+dBIB0tP9 bPzv76tgsCGKXuOwYuzg13j/vH76zCHlUF1sgAenpeuQVEz6EVYvpPPdcWsJ2NWsq9eU CVCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1793395275; x=1794000075; h=content-type:cc:to:from:subject:message-id:references:mime-version :in-reply-to:date:reply-to:x-gm-message-state:from:to:cc:subject :date:message-id:reply-to:content-type; bh=pEIWclQ1SBWM1+Z99wz5ixAlhPbNjur+F04EjDXrB30=; b=swyCa0BbfnHob0lJEkK88Jrh8/UMKfTC/cxAE9581H9mvm/tdWNZ2r77bisnzqGR/A hYv57oeqMrgvLky0ej1Cby/ClNfkzvoVQuADSphvVXvinxuj3mcmnEnKUofJjNCAW1Ay u7UrnOMKgOCWM2Wf2dVAZ3Pq4SraZ7rb3za1aB0CabxYT59SRj+kofc4nd5v/nTIEQYU k+eZtiOy+D8nN2807A/GO8NDJcIAhLXTuYdD0KFQ6cXJlNczPXX+lCOqHC58HizqN9v3 0KojKuuB97O6be+ZswfB2UAmwe9FYAhoXFTaExvUUqIrcVdt0778w1XrBOnUrU6g4ezT dg+A== X-Forwarded-Encrypted: i=1; AKwUvBxUSjBW71eCflw40sAAFcXrYwGvizz28cpq8d2XVgGi0nJNIpfaUsDzS4dXWJWMC3fsr5VXxxMgLsNZoSU=@lists.ozlabs.org X-Gm-Message-State: AFq9FYJV72szEiwZV1Cd0IripsVJbiUTpnrFJKHfaLRjbnT+Q6ss04VP +xCn/yusOd3T8HuumZW/M64Ry3RnenJ4V0VB1yJlAVBz26v8WPFkEPcrMM08JOHOJYxVZbqm3MQ d9ixn6Q== X-Received: from plhu11.prod.google.com ([2002:a17:903:124b:b0:2e9:fe74:4523]) (user=seanjc job=prod-delivery.src-stubby-dispatcher) by 2002:a17:902:d50a:b0:2e2:de8e:7315 with SMTP id d9443c01a7336-2eb364b6214mr32078575ad.38.1793395274491; Fri, 30 Oct 2026 14:21:14 -0700 (PDT) Reply-To: Sean Christopherson Date: Fri, 30 Oct 2026 14:20:49 -0700 In-Reply-To: <20261030212112.2598640-1-seanjc@google.com> X-Mailing-List: linuxppc-dev@lists.ozlabs.org List-Id: List-Help: List-Owner: List-Post: List-Archive: , List-Subscribe: , , Precedence: list X-Archived-By: Mime-Version: 1.0 References: <20261030212112.2598640-1-seanjc@google.com> X-Mailer: git-send-email 2.56.0.385.gac671c819a-goog Message-ID: <20261030212112.2598640-2-seanjc@google.com> Subject: [PATCH v4 01/23] KVM: Reject user accesses to guest memory if current->mm != kvm->mm From: Sean Christopherson To: Madhavan Srinivasan , Sean Christopherson , Paolo Bonzini Cc: Nicholas Piggin , linuxppc-dev@lists.ozlabs.org, kvm@vger.kernel.org, linux-kernel@vger.kernel.org, Jim Mattson , James Houghton , Chris Mason Content-Type: text/plain; charset="UTF-8" X-Spam-Status: No, score=-26.3 required=3.0 tests=BAYES_00,DKIMWL_WL_MED, DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_CR_TRAILER,INCLUDES_PATCH,MAILING_LIST_MULTI,SPF_HELO_PASS, SPF_PASS,USER_AGENT_GIT,USER_IN_DEF_DKIM_WL autolearn=ham autolearn_force=no version=4.0.1 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on aws-us-west-2-korg-listserv-alma10-1 Reject user accesses to guest memory, which are supposed to be done only in the context of KVM_RUN or similar operations, if the current address space is not the VM's (host userspace) address space. If KVM writes to guest memory after the owning host process has exited, or if the VM is being destroyed in the context of a different process, then writing using the wrong address space will corrupt a different process' memory. Reject the access but don't WARN() or KVM_BUG_ON() event though attempting to access guest memory with a mismatched address space is a blatant KVM bug, because unfortunately KVM is buggy. On KVM VMX, when a vCPU is destroyed while L2 is active, KVM synthesizes a nested VM-Exit to force the vCPU out of L2 in order to free the nested VMX assets, and a side effect of a nested VM-Exit is that it flushes the cached shadow VMCS12 back to guest memory: vmx_vcpu_free() |-> nested_vmx_free_vcpu() |-> vmx_leave_nested() |-> nested_vmx_vmexit(vcpu, -1, 0, 0) |-> nested_flush_cached_shadow_vmcs12() |-> kvm_write_guest_cached() |-> __copy_to_user(ghc->hva, ...) Fix the bug broadly even though the "real" bug is that KVM abuses the nested VM-Exit flow for non-architectural purposes, as there may be other such violations lurking. For now, punt on fixing individual bugs and hardening the common flows, e.g. with WARNs. Opportunistically provide wrappers in anticipation of adding more checks and hardening, i.e. growing the logic beyond checking current->mm. Fixes: 61ada7488ffd ("KVM: nVMX: Cache shadow vmcs12 on VMEntry and flush to memory on VMExit") Cc: stable@vger.kernel.org Reported-by: Jim Mattson Closes: https://lore.kernel.org/all/20260908132838.2116068-1-jmattson@google.com Reviewed-by: James Houghton Signed-off-by: Sean Christopherson --- arch/x86/kvm/vmx/sgx.c | 2 +- arch/x86/kvm/vmx/vmx.c | 8 ++++---- include/linux/kvm_host.h | 25 +++++++++++++++++++++++-- virt/kvm/kvm_main.c | 24 ++++++++++++------------ 4 files changed, 40 insertions(+), 19 deletions(-) diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c index 771c75a58343..52a6d0f8bb9e 100644 --- a/arch/x86/kvm/vmx/sgx.c +++ b/arch/x86/kvm/vmx/sgx.c @@ -64,7 +64,7 @@ static void sgx_handle_emulation_failure(struct kvm_vcpu *vcpu, u64 addr, static int sgx_read_hva(struct kvm_vcpu *vcpu, unsigned long hva, void *data, unsigned int size) { - if (__copy_from_user(data, (void __user *)hva, size)) { + if (kvm_copy_from_user(vcpu->kvm, data, (void __user *)hva, size)) { sgx_handle_emulation_failure(vcpu, hva, size); return -EFAULT; } diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index b341af4c6057..d433cd846ac4 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -4021,16 +4021,16 @@ static int init_rmode_tss(struct kvm *kvm, void __user *ua) int i; for (i = 0; i < 3; i++) { - if (__copy_to_user(ua + PAGE_SIZE * i, zero_page, PAGE_SIZE)) + if (kvm_copy_to_user(kvm, ua + PAGE_SIZE * i, zero_page, PAGE_SIZE)) return -EFAULT; } data = TSS_BASE_SIZE + TSS_REDIRECTION_SIZE; - if (__copy_to_user(ua + TSS_IOPB_BASE_OFFSET, &data, sizeof(u16))) + if (kvm_copy_to_user(kvm, ua + TSS_IOPB_BASE_OFFSET, &data, sizeof(u16))) return -EFAULT; data = ~0; - if (__copy_to_user(ua + RMODE_TSS_SIZE - 1, &data, sizeof(u8))) + if (kvm_copy_to_user(kvm, ua + RMODE_TSS_SIZE - 1, &data, sizeof(u8))) return -EFAULT; return 0; @@ -4065,7 +4065,7 @@ static int init_rmode_identity_map(struct kvm *kvm) for (i = 0; i < (PAGE_SIZE / sizeof(tmp)); i++) { tmp = (i << 22) + (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_PSE); - if (__copy_to_user(uaddr + i * sizeof(tmp), &tmp, sizeof(tmp))) { + if (kvm_copy_to_user(kvm, uaddr + i * sizeof(tmp), &tmp, sizeof(tmp))) { r = -EFAULT; goto out; } diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index e7fc94e2910a..be27b4e8f12d 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -1353,13 +1353,34 @@ int kvm_write_guest_offset_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, gpa_t gpa, unsigned long len); +static __always_inline __must_check bool kvm_can_do_uaccess(struct kvm *kvm) +{ + return current->mm == kvm->mm; +} + +#define BUILD_KVM_COPY_USER_WRAPPER(fn, to_user, from_user) \ +static __always_inline __must_check unsigned long kvm_##fn(struct kvm *kvm, \ + void to_user *to, \ + const void from_user *from, \ + unsigned long n) \ +{ \ + if (!kvm_can_do_uaccess(kvm)) \ + return n; \ + \ + return __##fn(to, from, n); \ +} +BUILD_KVM_COPY_USER_WRAPPER(copy_from_user, , __user) +BUILD_KVM_COPY_USER_WRAPPER(copy_from_user_inatomic, , __user) +BUILD_KVM_COPY_USER_WRAPPER(copy_to_user, __user, ) +BUILD_KVM_COPY_USER_WRAPPER(copy_to_user_inatomic, __user, ) + #define __kvm_get_guest(kvm, gfn, offset, v) \ ({ \ unsigned long __addr = gfn_to_hva(kvm, gfn); \ typeof(v) __user *__uaddr = (typeof(__uaddr))(__addr + offset); \ int __ret = -EFAULT; \ \ - if (!kvm_is_error_hva(__addr)) \ + if (!kvm_is_error_hva(__addr) && kvm_can_do_uaccess(kvm)) \ __ret = get_user(v, __uaddr); \ __ret; \ }) @@ -1379,7 +1400,7 @@ int kvm_gfn_to_hva_cache_init(struct kvm *kvm, struct gfn_to_hva_cache *ghc, typeof(v) __user *__uaddr = (typeof(__uaddr))(__addr + offset); \ int __ret = -EFAULT; \ \ - if (!kvm_is_error_hva(__addr)) \ + if (!kvm_is_error_hva(__addr) && kvm_can_do_uaccess(kvm)) \ __ret = put_user(v, __uaddr); \ if (!__ret) \ mark_page_dirty(kvm, gfn); \ diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index cf4cb586f140..218456372b11 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -3279,8 +3279,8 @@ static int next_segment(unsigned long len, int offset) } /* Copy @len bytes from guest memory at '(@gfn * PAGE_SIZE) + @offset' to @data */ -static int __kvm_read_guest_page(struct kvm_memory_slot *slot, gfn_t gfn, - void *data, int offset, int len) +static int __kvm_read_guest_page(struct kvm *kvm, struct kvm_memory_slot *slot, + gfn_t gfn, void *data, int offset, int len) { int r; unsigned long addr; @@ -3291,7 +3291,7 @@ static int __kvm_read_guest_page(struct kvm_memory_slot *slot, gfn_t gfn, addr = gfn_to_hva_memslot_prot(slot, gfn, NULL); if (kvm_is_error_hva(addr)) return -EFAULT; - r = __copy_from_user(data, (void __user *)addr + offset, len); + r = kvm_copy_from_user(kvm, data, (void __user *)addr + offset, len); if (r) return -EFAULT; return 0; @@ -3302,7 +3302,7 @@ int kvm_read_guest_page(struct kvm *kvm, gfn_t gfn, void *data, int offset, { struct kvm_memory_slot *slot = gfn_to_memslot(kvm, gfn); - return __kvm_read_guest_page(slot, gfn, data, offset, len); + return __kvm_read_guest_page(kvm, slot, gfn, data, offset, len); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_read_guest_page); @@ -3311,7 +3311,7 @@ int kvm_vcpu_read_guest_page(struct kvm_vcpu *vcpu, gfn_t gfn, void *data, { struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); - return __kvm_read_guest_page(slot, gfn, data, offset, len); + return __kvm_read_guest_page(vcpu->kvm, slot, gfn, data, offset, len); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_vcpu_read_guest_page); @@ -3355,8 +3355,8 @@ int kvm_vcpu_read_guest(struct kvm_vcpu *vcpu, gpa_t gpa, void *data, unsigned l } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_vcpu_read_guest); -static int __kvm_read_guest_atomic(struct kvm_memory_slot *slot, gfn_t gfn, - void *data, int offset, unsigned long len) +static int __kvm_read_guest_atomic(struct kvm *kvm, struct kvm_memory_slot *slot, + gfn_t gfn, void *data, int offset, unsigned long len) { int r; unsigned long addr; @@ -3368,7 +3368,7 @@ static int __kvm_read_guest_atomic(struct kvm_memory_slot *slot, gfn_t gfn, if (kvm_is_error_hva(addr)) return -EFAULT; pagefault_disable(); - r = __copy_from_user_inatomic(data, (void __user *)addr + offset, len); + r = kvm_copy_from_user_inatomic(kvm, data, (void __user *)addr + offset, len); pagefault_enable(); if (r) return -EFAULT; @@ -3382,7 +3382,7 @@ int kvm_vcpu_read_guest_atomic(struct kvm_vcpu *vcpu, gpa_t gpa, struct kvm_memory_slot *slot = kvm_vcpu_gfn_to_memslot(vcpu, gfn); int offset = offset_in_page(gpa); - return __kvm_read_guest_atomic(slot, gfn, data, offset, len); + return __kvm_read_guest_atomic(vcpu->kvm, slot, gfn, data, offset, len); } EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_vcpu_read_guest_atomic); @@ -3400,7 +3400,7 @@ static int __kvm_write_guest_page(struct kvm *kvm, addr = gfn_to_hva_memslot(memslot, gfn); if (kvm_is_error_hva(addr)) return -EFAULT; - r = __copy_to_user((void __user *)addr + offset, data, len); + r = kvm_copy_to_user(kvm, (void __user *)addr + offset, data, len); if (r) return -EFAULT; mark_page_dirty_in_slot(kvm, memslot, gfn); @@ -3538,7 +3538,7 @@ int kvm_write_guest_offset_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, if (unlikely(!ghc->memslot)) return kvm_write_guest(kvm, gpa, data, len); - r = __copy_to_user((void __user *)ghc->hva + offset, data, len); + r = kvm_copy_to_user(kvm, (void __user *)ghc->hva + offset, data, len); if (r) return -EFAULT; mark_page_dirty_in_slot(kvm, ghc->memslot, gpa >> PAGE_SHIFT); @@ -3576,7 +3576,7 @@ int kvm_read_guest_offset_cached(struct kvm *kvm, struct gfn_to_hva_cache *ghc, if (unlikely(!ghc->memslot)) return kvm_read_guest(kvm, gpa, data, len); - r = __copy_from_user(data, (void __user *)ghc->hva + offset, len); + r = kvm_copy_from_user(kvm, data, (void __user *)ghc->hva + offset, len); if (r) return -EFAULT; -- 2.56.0.385.gac671c819a-goog