)]}'
{
  "commit": "ebd6b5eac866f903f1cc7586ae6291004984e702",
  "tree": "806c89759f39687f3e575761dcb1b6fb2177620c",
  "parents": [
    "0201b6be13358346c48913710f8f21096fdc38f7"
  ],
  "author": {
    "name": "Aaron Sacks",
    "email": "contact@xchglabs.com",
    "time": "Tue May 12 02:07:42 2026 -0400"
  },
  "committer": {
    "name": "Derek Taylor",
    "email": "ddtaylor@google.com",
    "time": "Fri Jun 26 11:09:06 2026 -0700"
  },
  "message": "KVM: Reject wrapped offset in kvm_reset_dirty_gfn()\n\ncommit 577a8d3bae0531f0e5ccfac919cd8192f920a804 upstream.\n\nkvm_reset_dirty_gfn() guards the gfn range with\n\n\tif (!memslot || (offset + __fls(mask)) \u003e\u003d memslot-\u003enpages)\n\t\treturn;\n\nbut offset is u64 and the addition is unchecked.  The check can be\nsilently bypassed by a u64 wrap.\n\nThe dirty ring backing those entries is MAP_SHARED at\nKVM_DIRTY_LOG_PAGE_OFFSET of the vcpu fd, so the VMM can rewrite the\nslot and offset fields of any entry between when the kernel pushes\nthem and when KVM_RESET_DIRTY_RINGS consumes them.  On reset,\nkvm_dirty_ring_reset() re-reads the values via READ_ONCE() and feeds\nthem straight back into this check; only the flags handshake is\ntreated as the handover, the slot/offset payload is taken on trust.\n\nCrafting two entries\n\n\tentry[i].offset   \u003d 0xffffffffffffffc1\n\tentry[i+1].offset \u003d 0\n\nmakes the coalescing loop in kvm_dirty_ring_reset() compute\n\n\tdelta \u003d (s64)(0 - 0xffffffffffffffc1) \u003d 63\n\nwhich falls in [0, BITS_PER_LONG), so it folds entry[i+1] into the\nexisting mask by setting bit 63.  The trailing kvm_reset_dirty_gfn()\ncall then sees offset \u003d 0xffffffffffffffc1 and __fls(mask) \u003d 63;\nthe sum is 0 in u64 and the bounds check passes.\n\nThat offset propagates into kvm_arch_mmu_enable_log_dirty_pt_masked()\nunchanged.  On the legacy MMU path -- kvm_memslots_have_rmaps() \u003d\u003d\ntrue, i.e. shadow paging, any VM that has allocated shadow roots, or\na write-tracked slot -- it reaches gfn_to_rmap(), which indexes\nslot-\u003earch.rmap[0][] with a near-U64_MAX gfn.  That is an\nout-of-bounds load of a kvm_rmap_head, followed by a conditional\nclear of PT_WRITABLE_MASK in whatever the loaded pointer points at.\nThe path is reachable from any process holding /dev/kvm.\n\nRange-check offset on its own first, so the addition cannot wrap.\nmemslot-\u003enpages is bounded well below U64_MAX, so once offset \u003c\nnpages holds, offset + __fls(mask) (with __fls(mask) \u003c BITS_PER_LONG)\nstays in range.\n\nBUG\u003db/527721430\nTEST\u003dpresubmit\nRELEASE_NOTE\u003dFixed CVE-2026-52969 in the Linux kernel.\n\ncos-patch: security-moderate\nFixes: fb04a1eddb1a (\"KVM: X86: Implement ring-based dirty memory tracking\")\nCc: stable@vger.kernel.org\nChange-Id: I7ae615c5c88a84890d1eaea905ad00dc99756535\nSigned-off-by: Aaron Sacks \u003ccontact@xchglabs.com\u003e\nLink: https://patch.msgid.link/20260512060742.1628959-1-contact@xchglabs.com/\nSigned-off-by: Paolo Bonzini \u003cpbonzini@redhat.com\u003e\nSigned-off-by: Greg Kroah-Hartman \u003cgregkh@linuxfoundation.org\u003e\nSigned-off-by: Kernel CVE Triage Automation \u003ccloud-image-kernel-cve-triage-automation@prod.google.com\u003e\nReviewed-on: https://cos-review.googlesource.com/c/third_party/kernel/+/164504\nReviewed-by: Derek Taylor \u003cddtaylor@google.com\u003e\nTested-by: Cusky Presubmit Bot \u003cpresubmit@cos-infra-prod.iam.gserviceaccount.com\u003e\nReviewed-by: Chenglong Tang \u003cchenglongtang@google.com\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "7bc74969a819afb5198fc2dfb7c9e83b80f967e0",
      "old_mode": 33188,
      "old_path": "virt/kvm/dirty_ring.c",
      "new_id": "9cbed8140e04caa81d9219cb0e6f7e3a39d2502c",
      "new_mode": 33188,
      "new_path": "virt/kvm/dirty_ring.c"
    }
  ]
}
