{
  "threat_severity" : "Moderate",
  "public_date" : "2024-09-18T00:00:00Z",
  "bugzilla" : {
    "description" : "kernel: userfaultfd: fix checks for huge PMDs",
    "id" : "2313135",
    "url" : "https://bugzilla.redhat.com/show_bug.cgi?id=2313135"
  },
  "cvss3" : {
    "cvss3_base_score" : "4.7",
    "cvss3_scoring_vector" : "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:N/I:N/A:H",
    "status" : "verified"
  },
  "cwe" : "CWE-362",
  "details" : [ "In the Linux kernel, the following vulnerability has been resolved:\nuserfaultfd: fix checks for huge PMDs\nPatch series \"userfaultfd: fix races around pmd_trans_huge() check\", v2.\nThe pmd_trans_huge() code in mfill_atomic() is wrong in three different\nways depending on kernel version:\n1. The pmd_trans_huge() check is racy and can lead to a BUG_ON() (if you hit\nthe right two race windows) - I've tested this in a kernel build with\nsome extra mdelay() calls. See the commit message for a description\nof the race scenario.\nOn older kernels (before 6.5), I think the same bug can even\ntheoretically lead to accessing transhuge page contents as a page table\nif you hit the right 5 narrow race windows (I haven't tested this case).\n2. As pointed out by Qi Zheng, pmd_trans_huge() is not sufficient for\ndetecting PMDs that don't point to page tables.\nOn older kernels (before 6.5), you'd just have to win a single fairly\nwide race to hit this.\nI've tested this on 6.1 stable by racing migration (with a mdelay()\npatched into try_to_migrate()) against UFFDIO_ZEROPAGE - on my x86\nVM, that causes a kernel oops in ptlock_ptr().\n3. On newer kernels (>=6.5), for shmem mappings, khugepaged is allowed\nto yank page tables out from under us (though I haven't tested that),\nso I think the BUG_ON() checks in mfill_atomic() are just wrong.\nI decided to write two separate fixes for these (one fix for bugs 1+2, one\nfix for bug 3), so that the first fix can be backported to kernels\naffected by bugs 1+2.\nThis patch (of 2):\nThis fixes two issues.\nI discovered that the following race can occur:\nmfill_atomic                other thread\n============                ============\n<zap PMD>\npmdp_get_lockless() [reads none pmd]\n<bail if trans_huge>\n<if none:>\n<pagefault creates transhuge zeropage>\n__pte_alloc [no-op]\n<zap PMD>\n<bail if pmd_trans_huge(*dst_pmd)>\nBUG_ON(pmd_none(*dst_pmd))\nI have experimentally verified this in a kernel with extra mdelay() calls;\nthe BUG_ON(pmd_none(*dst_pmd)) triggers.\nOn kernels newer than commit 0d940a9b270b (\"mm/pgtable: allow\npte_offset_map[_lock]() to fail\"), this can't lead to anything worse than\na BUG_ON(), since the page table access helpers are actually designed to\ndeal with page tables concurrently disappearing; but on older kernels\n(<=6.4), I think we could probably theoretically race past the two\nBUG_ON() checks and end up treating a hugepage as a page table.\nThe second issue is that, as Qi Zheng pointed out, there are other types\nof huge PMDs that pmd_trans_huge() can't catch: devmap PMDs and swap PMDs\n(in particular, migration PMDs).\nOn <=6.4, this is worse than the first issue: If mfill_atomic() runs on a\nPMD that contains a migration entry (which just requires winning a single,\nfairly wide race), it will pass the PMD to pte_offset_map_lock(), which\nassumes that the PMD points to a page table.\nBreakage follows: First, the kernel tries to take the PTE lock (which will\ncrash or maybe worse if there is no \"struct page\" for the address bits in\nthe migration entry PMD - I think at least on X86 there usually is no\ncorresponding \"struct page\" thanks to the PTE inversion mitigation, amd64\nlooks different).\nIf that didn't crash, the kernel would next try to write a PTE into what\nit wrongly thinks is a page table.\nAs part of fixing these issues, get rid of the check for pmd_trans_huge()\nbefore __pte_alloc() - that's redundant, we're going to have to check for\nthat after the __pte_alloc() anyway.\nBackport note: pmdp_get_lockless() is pmd_read_atomic() in older kernels." ],
  "affected_release" : [ {
    "product_name" : "Red Hat Enterprise Linux 9",
    "release_date" : "2025-05-13T00:00:00Z",
    "advisory" : "RHSA-2025:6966",
    "cpe" : "cpe:/a:redhat:enterprise_linux:9",
    "package" : "kernel-0:5.14.0-570.12.1.el9_6"
  }, {
    "product_name" : "Red Hat Enterprise Linux 9",
    "release_date" : "2025-05-13T00:00:00Z",
    "advisory" : "RHSA-2025:6966",
    "cpe" : "cpe:/o:redhat:enterprise_linux:9",
    "package" : "kernel-0:5.14.0-570.12.1.el9_6"
  } ],
  "package_state" : [ {
    "product_name" : "Red Hat Enterprise Linux 6",
    "fix_state" : "Not affected",
    "package_name" : "kernel",
    "cpe" : "cpe:/o:redhat:enterprise_linux:6"
  }, {
    "product_name" : "Red Hat Enterprise Linux 7",
    "fix_state" : "Out of support scope",
    "package_name" : "kernel",
    "cpe" : "cpe:/o:redhat:enterprise_linux:7"
  }, {
    "product_name" : "Red Hat Enterprise Linux 7",
    "fix_state" : "Out of support scope",
    "package_name" : "kernel-rt",
    "cpe" : "cpe:/o:redhat:enterprise_linux:7"
  }, {
    "product_name" : "Red Hat Enterprise Linux 8",
    "fix_state" : "Will not fix",
    "package_name" : "kernel",
    "cpe" : "cpe:/o:redhat:enterprise_linux:8"
  }, {
    "product_name" : "Red Hat Enterprise Linux 8",
    "fix_state" : "Will not fix",
    "package_name" : "kernel-rt",
    "cpe" : "cpe:/o:redhat:enterprise_linux:8"
  }, {
    "product_name" : "Red Hat Enterprise Linux 9",
    "fix_state" : "Will not fix",
    "package_name" : "kernel-rt",
    "cpe" : "cpe:/o:redhat:enterprise_linux:9"
  } ],
  "references" : [ "https://www.cve.org/CVERecord?id=CVE-2024-46787\nhttps://nvd.nist.gov/vuln/detail/CVE-2024-46787\nhttps://lore.kernel.org/linux-cve-announce/2024091852-CVE-2024-46787-8b6d@gregkh/T" ],
  "name" : "CVE-2024-46787",
  "csaw" : false
}