In the Linux kernel, the following vulnerability has been resolved: x86/mm: Eliminate window where TLB flushes may be inadvertently skipped tl;dr: There is a window in the mm switching code where the new CR3 is set and the CPU should be getting TLB flushes for the new mm. But should_flush_tlb() has a bug and suppresses the flush. Fix it by widening the window where should_flush_tlb() sends an IPI. Long Version: === History === There were a few things leading up to this. First, updating mm_cpumask() was observed to be too expensive, so it was made lazier. But being lazy caused too many unnecessary IPIs to CPUs due to the now-lazy mm_cpumask(). So code was added to cull mm_cpumask() periodically[2]. But that culling was a bit too aggressive and skipped sending TLB flushes to CPUs that need them. So here we are again. === Problem === The too-aggressive code in should_flush_tlb() strikes in this window: // Turn on IPIs for this CPU/mm combination, but only // if should_flush_tlb() agrees: cpumask_set_cpu(cpu, mm_cpumask(next)); next_tlb_gen = atomic64_read(&next->context.tlb_gen); choose_new_asid(next, next_tlb_gen, &new_asid, &need_flush); load_new_mm_cr3(need_flush); // ^ After 'need_flush' is set to false, IPIs *MUST* // be sent to this CPU and not be ignored. this_cpu_write(cpu_tlbstate.loaded_mm, next); // ^ Not until this point does should_flush_tlb() // become true! should_flush_tlb() will suppress TLB flushes between load_new_mm_cr3() and writing to 'loaded_mm', which is a window where they should not be suppressed. Whoops. === Solution === Thankfully, the fuzzy "just about to write CR3" window is already marked with loaded_mm==LOADED_MM_SWITCHING. Simply checking for that state in should_flush_tlb() is sufficient to ensure that the CPU is targeted with an IPI. This will cause more TLB flush IPIs. But the window is relatively small and I do not expect this to cause any kind of measurable performance impact. Update the comment where LOADED_MM_SWITCHING is written since it grew yet another user. Peter Z also raised a concern that should_flush_tlb() might not observe 'loaded_mm' and 'is_lazy' in the same order that switch_mm_irqs_off() writes them. Add a barrier to ensure that they are observed in the order they are written.
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: x86/mm: Eliminar la ventana donde los vaciados de TLB pueden omitirse inadvertidamente tl;dr: Hay una ventana en el código de conmutación mm donde se establece el nuevo CR3 y la CPU debería obtener vaciados de TLB para el nuevo mm. Pero should_flush_tlb() tiene un error y suprime el vaciado. Arréglelo ampliando la ventana donde should_flush_tlb() envía una IPI. Versión larga: === Historial === Hubo algunas cosas que llevaron a esto. Primero, se observó que actualizar mm_cpumask() era demasiado costoso, por lo que se hizo más perezoso. Pero ser perezoso causó demasiados IPI innecesarios a las CPU debido al ahora perezoso mm_cpumask(). Entonces se agregó código para descartar mm_cpumask() periódicamente[2]. Pero ese descarte fue demasiado agresivo y omitió el envío de vaciados de TLB a las CPU que los necesitan. Así que aquí estamos de nuevo. === Problema === El código demasiado agresivo en should_flush_tlb() ataca en esta ventana: // Activa las IPI para esta combinación de CPU/mm, pero solo si should_flush_tlb() está de acuerdo: cpumask_set_cpu(cpu, mm_cpumask(next)); next_tlb_gen = atomic64_read(&next->context.tlb_gen); choose_new_asid(next, next_tlb_gen, &new_asid, &need_flush); load_new_mm_cr3(need_flush); // ^ Después de que 'need_flush' se establece en falso, las IPI *DEBEN* // enviarse a esta CPU y no ignorarse. this_cpu_write(cpu_tlbstate.loaded_mm, next); // ^ ¡No es hasta este punto que should_flush_tlb() // se vuelve verdadero! should_flush_tlb() suprimirá los vaciados de TLB entre load_new_mm_cr3() y la escritura en 'loaded_mm', que es una ventana donde no deberían suprimirse. ¡Uy! === Solución === Afortunadamente, la ventana difusa "a punto de escribir CR3" ya está marcada con load_mm==LOADED_MM_SWITCHING. Simplemente comprobar ese estado en should_flush_tlb() es suficiente para asegurar que la CPU esté dirigida a un IPI. Esto provocará más IPI de vaciado de TLB. Sin embargo, la ventana es relativamente pequeña y no preveo que esto tenga ningún impacto medible en el rendimiento. Actualice el comentario donde se escribe LOADED_MM_SWITCHING, ya que ha generado otro usuario. Peter Z también planteó la preocupación de que should_flush_tlb() podría no observar 'loaded_mm' e 'is_lazy' en el mismo orden en que switch_mm_irqs_off() los escribe. Añade una barrera para garantizar que se observen en el orden en que están escritos.
CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H
| Attack Vector | LOCAL |
|---|---|
| Attack Complexity | LOW |
| Privileges Required | LOW |
| User Interaction | NONE |
| Scope | UNCHANGED |
| Confidentiality Impact | NONE |
| Integrity Impact | NONE |
| Availability Impact | HIGH |
| Source | Type | Description |
|---|---|---|
| [email protected] | Primary |
en
NVD-CWE-noinfo
|
| Vendor | Product | Version | Update | Type |
|---|---|---|---|---|
| linux | linux_kernel | * | <built-in method update of dict object at 0x702bfc2a4f00> | Operating System |
| linux | linux_kernel | * | <built-in method update of dict object at 0x702bfc2a43c0> | Operating System |
| linux | linux_kernel | * | <built-in method update of dict object at 0x702bde165dc0> | Operating System |
| linux | linux_kernel | * | <built-in method update of dict object at 0x702bdc9f5b80> | Operating System |
| linux | linux_kernel | * | <built-in method update of dict object at 0x702bfc2a7780> | Operating System |
| linux | linux_kernel | 6.15 | <built-in method update of dict object at 0x702bfc2a5f40> | Operating System |
| linux | linux_kernel | 6.15 | <built-in method update of dict object at 0x702b70374380> | Operating System |
| linux | linux_kernel | 6.15 | <built-in method update of dict object at 0x702bfc2a6d00> | Operating System |
| linux | linux_kernel | 6.15 | <built-in method update of dict object at 0x702bde167b00> | Operating System |
| linux | linux_kernel | 6.15 | <built-in method update of dict object at 0x702bfc2a74c0> | Operating System |
| debian | debian_linux | 11.0 | <built-in method update of dict object at 0x702bde1649c0> | Operating System |
| Vulnerable | CPE |
|---|---|
| Yes | cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:6.15:rc1:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:6.15:rc2:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:6.15:rc3:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:6.15:rc4:*:*:*:*:*:* |
| Yes | cpe:2.3:o:linux:linux_kernel:6.15:rc5:*:*:*:*:*:* |
| Vulnerable | CPE |
|---|---|
| Yes | cpe:2.3:o:debian:debian_linux:11.0:*:*:*:*:*:*:* |