| Added |
Translation |
|
Title: Linux, Description: En el kernel de Linux, la siguiente vulnerabilidad ha sido resuelta:
perf/x86: Mover la configuración del puntero de evento antes en x86_pmu_enable()
Un sistema AMD EPYC de producción falló con una desreferencia de puntero NULL en el gestor NMI de la PMU:
BUG: desreferencia de puntero NULL del kernel, dirección: 0000000000000198
RIP: x86_perf_event_update+0xc/0xa0
Traza de Llamada:
[NMI]
amd_pmu_v2_handle_irq+0x1a6/0x390
perf_event_nmi_handler+0x24/0x40
La instrucción que causó el fallo es 'cmpq $0x0, 0x198(%rdi)' con RDI=0, correspondiente a la comprobación 'if (unlikely(!hwc- event_base))' en x86_perf_event_update() donde hwc = &event- hw y event es NULL.
La inspección con drgn del vmcore en la CPU 106 mostró una discrepancia entre cpuc- active_mask y cpuc- events[]:
active_mask: 0x1e (bits 1, 2, 3, 4)
events[1]: 0xff1100136cbd4f38 (válido)
events[2]: 0x0 (NULL, pero el bit 2 de active_mask está activado)
events[3]: 0xff1100076fd2cf38 (válido)
events[4]: 0xff1100079e990a90 (válido)
El evento que debería ocupar events[2] se encontró en event_list[2] con hw.idx=2 y hw.state=0x0, confirmando que x86_pmu_start() se había ejecutado (lo que borra hw.state y establece active_mask) pero events[2] nunca fue poblado.
Otro evento (event_list[0]) tenía hw.state=0x7 (STOPPED|UPTODATE|ARCH), mostrando que se detuvo cuando la PMU reprogramó los eventos, confirmando que la secuencia de estrangulamiento y luego reprogramación ocurrió.
La causa raíz es el commit 7e772a93eb61 ( perf/x86: Solucionar acceso a evento NULL y posible pérdida de registro PEBS ) que movió la asignación de cpuc- events[idx] fuera de x86_pmu_start() y al paso 2 de x86_pmu_enable(), después de la comprobación PERF_HES_ARCH. Esto rompió cualquier ruta que llama a pmu- start() sin pasar por x86_pmu_enable() -- específicamente la ruta de desestrangulamiento:
perf_adjust_freq_unthr_events()
- perf_event_unthrottle_group()
- perf_event_unthrottle()
- event- pmu- start(event, 0)
- x86_pmu_start() // establece active_mask pero no events[]
La secuencia de la condición de carrera es:
1. Un grupo de eventos perf desborda, lo que activa el estrangulamiento del grupo a través de perf_event_throttle_group(). Todos los eventos se detienen: los bits de active_mask se borran, events[] se preserva (x86_pmu_stop ya no borra events[] después del commit 7e772a93eb61).
2. Mientras aún está estrangulado (PERF_HES_STOPPED), x86_pmu_enable() se ejecuta debido a otra actividad de planificación. Los eventos detenidos que necesitan mover contadores obtienen PERF_HES_ARCH establecido y events[old_idx] borrado. En el paso 2 de x86_pmu_enable(), PERF_HES_ARCH hace que estos eventos se omitan -- events[new_idx] nunca se establece.
3. El tick del temporizador desestrángula el grupo a través de pmu- start(). Dado que el commit 7e772a93eb61 eliminó la asignación de events[] de x86_pmu_start(), active_mask[new_idx] se establece pero events[new_idx] permanece NULL.
4. Se dispara una NMI de desbordamiento de PMC. El gestor itera los contadores activos, encuentra active_mask[2] establecido, lee events[2] que es NULL, y falla al desreferenciarlo.
Mover la asignación de cpuc- events[hwc- idx] en x86_pmu_enable() a antes de la comprobación PERF_HES_ARCH, para que events[] se pueble incluso para eventos que no se inician inmediatamente. Esto asegura que la ruta de desestrangulamiento a través de pmu- start() siempre encuentre un puntero de evento válido.
|