From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (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 705953148DA; Sat, 30 May 2026 17:27:49 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780162070; cv=none; b=Zb/ZDEBg03AOlRzIbfjpW65AKG+Kff7CFN+8BTC8dRC7HTvWcABXjlZexCQbaEFNlpaxfUPh8/FJNMgSx1WGBEjxqKYOF5/gtirhw07N1W5xZNJG+jPvfLCnP5MPnz+n9FZXyi+BxXCorzSrv50bLQvrjB1QxaQsfEgubQsDUnw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780162070; c=relaxed/simple; bh=DZxp7EcnKvNOIlGbng4fjq1rBedRuJC2M9iFPpXcfX8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oRDh4TvWb4MjpISeO0C70NhySzwo40qfM9VqHjKidukHVR+lFP6xvjK/vNxjJ/54gJs3btJ425AnF7Xenl45ziipMpiHsDYrO+8X+gMwm2Mj25DDjCRdnA2tWU+QDDaIEuHe+M+cJ+2JUHBoxtVVZaANnVMDXN7dw9+O+Wz+oPM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b=m5rWCseC; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=linuxfoundation.org header.i=@linuxfoundation.org header.b="m5rWCseC" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8A1931F00893; Sat, 30 May 2026 17:27:48 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linuxfoundation.org; s=korg; t=1780162069; bh=PGHRmF2qx4toFRF7t7s+/hWsKHcbuZ1Skh8mFSCidfA=; h=From:To:Cc:Subject:Date:In-Reply-To:References; b=m5rWCseCMbSp1lNZQCLFEH9dwXvGg3NKcr9uhE8eLGWkqMzwozhQR/j0D706rtEa+ eQe9YtQoRVNGFNSWaWEylygyTnuYSH0m9X5qe3VbeHUxOzCUat0OZzb+bXhdOaQkvM OpGCKuKR+WNfSlk65uo/FNOQkSa6GLU14VYPQzKE= From: Greg Kroah-Hartman To: stable@vger.kernel.org Cc: Greg Kroah-Hartman , patches@lists.linux.dev, Aaron Sacks , Paolo Bonzini Subject: [PATCH 6.1 812/969] KVM: Reject wrapped offset in kvm_reset_dirty_gfn() Date: Sat, 30 May 2026 18:05:36 +0200 Message-ID: <20260530160323.057071838@linuxfoundation.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260530160300.485627683@linuxfoundation.org> References: <20260530160300.485627683@linuxfoundation.org> User-Agent: quilt/0.69 X-stable: review X-Patchwork-Hint: ignore Precedence: bulk X-Mailing-List: patches@lists.linux.dev List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit 6.1-stable review patch. If anyone has any objections, please let me know. ------------------ From: Aaron Sacks commit 577a8d3bae0531f0e5ccfac919cd8192f920a804 upstream. kvm_reset_dirty_gfn() guards the gfn range with if (!memslot || (offset + __fls(mask)) >= memslot->npages) return; but offset is u64 and the addition is unchecked. The check can be silently bypassed by a u64 wrap. The dirty ring backing those entries is MAP_SHARED at KVM_DIRTY_LOG_PAGE_OFFSET of the vcpu fd, so the VMM can rewrite the slot and offset fields of any entry between when the kernel pushes them and when KVM_RESET_DIRTY_RINGS consumes them. On reset, kvm_dirty_ring_reset() re-reads the values via READ_ONCE() and feeds them straight back into this check; only the flags handshake is treated as the handover, the slot/offset payload is taken on trust. Crafting two entries entry[i].offset = 0xffffffffffffffc1 entry[i+1].offset = 0 makes the coalescing loop in kvm_dirty_ring_reset() compute delta = (s64)(0 - 0xffffffffffffffc1) = 63 which falls in [0, BITS_PER_LONG), so it folds entry[i+1] into the existing mask by setting bit 63. The trailing kvm_reset_dirty_gfn() call then sees offset = 0xffffffffffffffc1 and __fls(mask) = 63; the sum is 0 in u64 and the bounds check passes. That offset propagates into kvm_arch_mmu_enable_log_dirty_pt_masked() unchanged. On the legacy MMU path -- kvm_memslots_have_rmaps() == true, i.e. shadow paging, any VM that has allocated shadow roots, or a write-tracked slot -- it reaches gfn_to_rmap(), which indexes slot->arch.rmap[0][] with a near-U64_MAX gfn. That is an out-of-bounds load of a kvm_rmap_head, followed by a conditional clear of PT_WRITABLE_MASK in whatever the loaded pointer points at. The path is reachable from any process holding /dev/kvm. Range-check offset on its own first, so the addition cannot wrap. memslot->npages is bounded well below U64_MAX, so once offset < npages holds, offset + __fls(mask) (with __fls(mask) < BITS_PER_LONG) stays in range. Fixes: fb04a1eddb1a ("KVM: X86: Implement ring-based dirty memory tracking") Cc: stable@vger.kernel.org Signed-off-by: Aaron Sacks Link: https://patch.msgid.link/20260512060742.1628959-1-contact@xchglabs.com/ Signed-off-by: Paolo Bonzini Signed-off-by: Greg Kroah-Hartman --- virt/kvm/dirty_ring.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) --- a/virt/kvm/dirty_ring.c +++ b/virt/kvm/dirty_ring.c @@ -49,7 +49,8 @@ static void kvm_reset_dirty_gfn(struct k memslot = id_to_memslot(__kvm_memslots(kvm, as_id), id); - if (!memslot || (offset + __fls(mask)) >= memslot->npages) + if (!memslot || offset >= memslot->npages || + offset + __fls(mask) >= memslot->npages) return; KVM_MMU_LOCK(kvm);