CVE-2024-46787

In the Linux kernel, the following vulnerability has been resolved: userfaultfd: fix checks for huge PMDs Patch series "userfaultfd: fix races around pmd_trans_huge() check", v2. The pmd_trans_huge() code in mfill_atomic() is wrong in three different ways depending on kernel version: 1. The pmd_trans_huge() check is racy and can lead to a BUG_ON() (if you hit the right two race windows) - I've tested this in a kernel build with some extra mdelay() calls. See the commit message for a description of the race scenario. On older kernels (before 6.5), I think the same bug can even theoretically lead to accessing transhuge page contents as a page table if you hit the right 5 narrow race windows (I haven't tested this case). 2. As pointed out by Qi Zheng, pmd_trans_huge() is not sufficient for detecting PMDs that don't point to page tables. On older kernels (before 6.5), you'd just have to win a single fairly wide race to hit this. I've tested this on 6.1 stable by racing migration (with a mdelay() patched into try_to_migrate()) against UFFDIO_ZEROPAGE - on my x86 VM, that causes a kernel oops in ptlock_ptr(). 3. On newer kernels (>=6.5), for shmem mappings, khugepaged is allowed to yank page tables out from under us (though I haven't tested that), so I think the BUG_ON() checks in mfill_atomic() are just wrong. I decided to write two separate fixes for these (one fix for bugs 1+2, one fix for bug 3), so that the first fix can be backported to kernels affected by bugs 1+2. This patch (of 2): This fixes two issues. I discovered that the following race can occur: mfill_atomic other thread ============ ============ <zap PMD> pmdp_get_lockless() [reads none pmd] <bail if trans_huge> <if none:> <pagefault creates transhuge zeropage> __pte_alloc [no-op] <zap PMD> <bail if pmd_trans_huge(*dst_pmd)> BUG_ON(pmd_none(*dst_pmd)) I have experimentally verified this in a kernel with extra mdelay() calls; the BUG_ON(pmd_none(*dst_pmd)) triggers. On kernels newer than commit 0d940a9b270b ("mm/pgtable: allow pte_offset_map[_lock]() to fail"), this can't lead to anything worse than a BUG_ON(), since the page table access helpers are actually designed to deal with page tables concurrently disappearing; but on older kernels (<=6.4), I think we could probably theoretically race past the two BUG_ON() checks and end up treating a hugepage as a page table. The second issue is that, as Qi Zheng pointed out, there are other types of huge PMDs that pmd_trans_huge() can't catch: devmap PMDs and swap PMDs (in particular, migration PMDs). On <=6.4, this is worse than the first issue: If mfill_atomic() runs on a PMD that contains a migration entry (which just requires winning a single, fairly wide race), it will pass the PMD to pte_offset_map_lock(), which assumes that the PMD points to a page table. Breakage follows: First, the kernel tries to take the PTE lock (which will crash or maybe worse if there is no "struct page" for the address bits in the migration entry PMD - I think at least on X86 there usually is no corresponding "struct page" thanks to the PTE inversion mitigation, amd64 looks different). If that didn't crash, the kernel would next try to write a PTE into what it wrongly thinks is a page table. As part of fixing these issues, get rid of the check for pmd_trans_huge() before __pte_alloc() - that's redundant, we're going to have to check for that after the __pte_alloc() anyway. Backport note: pmdp_get_lockless() is pmd_read_atomic() in older kernels.
Configurations

Configuration 1 (hide)

OR cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc1:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc2:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc3:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc4:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc5:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc6:*:*:*:*:*:*

History

20 Nov 2024, 15:33

Type Values Removed Values Added
CWE NVD-CWE-noinfo
First Time Linux linux Kernel
Linux
CVSS v2 : unknown
v3 : unknown
v2 : unknown
v3 : 4.7
References () https://git.kernel.org/stable/c/3c6b4bcf37845c9359aed926324bed66bdd2448d - () https://git.kernel.org/stable/c/3c6b4bcf37845c9359aed926324bed66bdd2448d - Patch
References () https://git.kernel.org/stable/c/71c186efc1b2cf1aeabfeff3b9bd5ac4c5ac14d8 - () https://git.kernel.org/stable/c/71c186efc1b2cf1aeabfeff3b9bd5ac4c5ac14d8 - Patch
References () https://git.kernel.org/stable/c/98cc18b1b71e23fe81a5194ed432b20c2d81a01a - () https://git.kernel.org/stable/c/98cc18b1b71e23fe81a5194ed432b20c2d81a01a - Patch
CPE cpe:2.3:o:linux:linux_kernel:6.11:rc6:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc3:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc5:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc1:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc4:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.11:rc2:*:*:*:*:*:*

20 Sep 2024, 12:30

Type Values Removed Values Added
Summary
  • (es) En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: userfaultfd: corregir comprobaciones para PMD enormes Serie de parches "userfaultfd: corregir ejecucións en torno a la comprobación pmd_trans_huge()", v2. El código pmd_trans_huge() en mfill_atomic() es incorrecto de tres maneras diferentes según la versión del kernel: 1. La comprobación pmd_trans_huge() es rápida y puede llevar a un BUG_ON() (si alcanza las dos ventanas de ejecución correctas) - He probado esto en una compilación del kernel con algunas llamadas mdelay() adicionales. Vea el mensaje de confirmación para obtener una descripción del escenario de ejecución. En kernels más antiguos (antes de 6.5), creo que el mismo error puede incluso teóricamente llevar a acceder a los contenidos de la página transhuge como una tabla de páginas si alcanza las 5 ventanas de ejecución estrechas correctas (no he probado este caso). 2. Como señaló Qi Zheng, pmd_trans_huge() no es suficiente para detectar PMD que no apuntan a tablas de páginas. En kernels más antiguos (anteriores a 6.5), solo tendrías que ganar una única ejecución bastante amplia para alcanzar esto. He probado esto en 6.1 estable haciendo una ejecución de migración (con un mdelay() parcheado en try_to_migrate()) contra UFFDIO_ZEROPAGE - en mi VM x86, eso causa un error de kernel en ptlock_ptr(). 3. En kernels más nuevos (&gt;=6.5), para asignaciones shmem, khugepaged puede arrancar tablas de páginas de debajo de nosotros (aunque no lo he probado), así que creo que las comprobaciones BUG_ON() en mfill_atomic() son simplemente incorrectas. Decidí escribir dos correcciones separadas para estos (una corrección para los errores 1+2, una corrección para el error 3), de modo que la primera corrección pueda ser retroportada a kernels afectados por errores 1+2. Este parche (de 2): Esto corrige dos problemas. Descubrí que puede ocurrir la siguiente ejecución: mfill_atomic other thread ============ ============ pmdp_get_lockless() [reads none pmd] __pte_alloc [no-op] BUG_ON(pmd_none(*dst_pmd)) He verificado esto experimentalmente en un kernel con llamadas mdelay() adicionales; se activa BUG_ON(pmd_none(*dst_pmd)). En los kernels más nuevos que el commit 0d940a9b270b ("mm/pgtable: permitir que pte_offset_map[_lock]() falle"), esto no puede llevar a nada peor que un BUG_ON(), ya que los ayudantes de acceso a la tabla de páginas están manipulados para lidiar con la desaparición simultánea de tablas de páginas; pero en kernels más antiguos (&lt;=6.4), creo que probablemente podríamos teóricamente pasar por alto las dos comprobaciones de BUG_ON() y terminar tratando una página enorme como una tabla de páginas. El segundo problema es que, como señaló Qi Zheng, hay otros tipos de PMD enormes que pmd_trans_huge() no puede detectar: PMD de devmap y PMD de intercambio (en particular, PMD de migración). En &lt;=6.4, esto es peor que el primer problema: si mfill_atomic() se ejecuta en un PMD que contiene una entrada de migración (que solo requiere ganar una ejecución única y bastante amplia), pasará el PMD a pte_offset_map_lock(), que asume que el PMD apunta a una tabla de páginas. A continuación, se produce una ruptura: primero, el núcleo intenta tomar el bloqueo PTE (que se bloqueará o tal vez será peor si no hay una "página de estructura" para los bits de dirección en el PMD de la entrada de migración; creo que al menos en X86 no suele haber una "página de estructura" correspondiente gracias a la mitigación de inversión de PTE; amd64 se ve diferente). Si eso no se bloquea, el núcleo intentará escribir un PTE en lo que cree erróneamente que es una tabla de páginas. Como parte de la solución de estos problemas, elimine la verificación de pmd_trans_huge() antes de __pte_alloc(); eso es redundante, vamos a tener que verificar eso después de __pte_alloc() de todos modos. --- truncada ----

18 Sep 2024, 08:15

Type Values Removed Values Added
New CVE

Information

Published : 2024-09-18 08:15

Updated : 2024-11-20 15:33


NVD link : CVE-2024-46787

Mitre link : CVE-2024-46787

CVE.ORG link : CVE-2024-46787


JSON object : View

Products Affected

linux

  • linux_kernel