Vulnerabilidad en kernel de Linux (CVE-2025-21932)
Fecha de publicación:
01/04/2025
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: mm: abortar vma_modify() en caso de fallo de memoria insuficiente en la fusión. El resto de vma_modify() depende de que el estado de vmg permanezca intacto tras un intento de fusión. Normalmente, este es el caso; sin embargo, en el caso extremo de que un intento de fusión falle no porque el rango especificado no se pueda fusionar, sino debido a un error de memoria insuficiente al intentar confirmar la fusión, esta suposición se vuelve falsa. Esto da como resultado que vmg->start, end se modifique y, por lo tanto, los intentos posteriores de dividir el VMA se realizarán con valores de inicio/fin no válidos. Afortunadamente, es prácticamente imposible que logremos esto en la realidad, ya que requeriría un fallo de preasignación de nodos del árbol de maple que probablemente nunca ocurriría por ser "demasiado pequeño para fallar", es decir, el kernel simplemente seguiría reintentando la recuperación hasta que tuviera éxito. Sin embargo, este escenario sigue siendo teóricamente posible, y lo que estamos haciendo aquí es incorrecto, por lo que debemos corregirlo. La opción más segura, cuando ocurre este escenario, es simplemente abandonar la operación. Si no podemos asignar memoria para la fusión, tampoco podemos asignar memoria para la división (¡quizás incluso más!). Cualquier escenario donde esto ocurra estaría bajo una presión de memoria muy extrema (probablemente fatal), por lo que es mejor abandonar pronto. Por lo tanto, no hay duda de que es apropiado simplemente abandonar en este escenario. Sin embargo, en general, si es posible, nunca debemos asumir que el estado de VMG es estable después de un intento de fusión, ya que las operaciones de fusión actualizan los campos de VMG. Como resultado, también debemos aclarar esto almacenando inicio y fin en variables locales. El problema fue reportado originalmente por syzkaller y por Brad Spengler (a través de una discusión fuera de la lista), y en ambos casos se manifestó como una activación de la aserción: VM_WARN_ON_VMG(start >= end, vmg); In vma_merge_existing_range(). Parece que al menos un escenario en el que esto ocurre es uno en el que la fusión que se intenta se debe a una función madvise() en múltiples VMA, con este aspecto: inicio fin |<------>| |----------|------| | vma | siguiente | |----------|------| Cuando se invoca madvise_walk_vmas(), primero encontramos vma en lo anterior (determinando que prev sea igual a vma, ya que estamos desplazados hacia vma) y luego entramos en el bucle. Determinamos el final de vma que forma parte del rango que estamos ejecutando con madvise() estableciendo 'tmp' en este valor: /* Aquí vma->vm_start <= start < (end|vma->vm_end) */ tmp = vma->vm_end; Luego invocamos la operación madvise() a través de visit(), permitiendo que prev se actualice para apuntar a vma como parte de la operación: /* Aquí vma->vm_start <= start < tmp <= (end|vma->vm_end). */ error = visit(vma, &prev, start, tmp, arg); Donde el puntero de la función visit() en esta instancia es madvise_vma_behavior(). Como se observa en los informes de syzkaller, en última instancia es madvise_update_vma() el que se invoca, llamando a vma_modify_flags_name() y vma_modify() a su vez. Luego, en vma_modify(), intentamos la fusión: merged = vma_merge_existing_range(vmg); if (merged) return merged; Invocamos esto con vmg->start, end establecido en start, tmp como tal: start tmp |<--->| |----------|------| | vma | next | |----------|------| Nos encontramos en el escenario correcto de fusión, pero en el que no podemos eliminar la parte central (estamos desplazados hacia vma). Aquí tenemos un caso especial donde vmg->start, end se establecen en valores quizás poco intuitivos: pretendíamos reducir la VMA central y expandir la siguiente. Esto significa que vmg->start, end se establecen en... vma->vm_start, start. Ahora, commit_merge() falla y vmg->start, end se mantienen así. Esto significa que volvemos al resto de vma_modify() ---truncated---
Gravedad: Pendiente de análisis
Última modificación:
01/04/2025