Instituto Nacional de ciberseguridad. Sección Incibe
Instituto Nacional de Ciberseguridad. Sección INCIBE-CERT

Vulnerabilidad en kernel de Linux (CVE-2022-49999)

Gravedad CVSS v3.1:
ALTA
Tipo:
CWE-787 Escritura fuera de límites
Fecha de publicación:
18/06/2025
Última modificación:
14/11/2025

Descripción

En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: btrfs: corrección de corrupción de caché de espacio y posibles asignaciones dobles. Al probar space_cache v2 en un conjunto grande de máquinas, encontramos algunos síntomas: 1. Errores "no se puede agregar espacio libre :-17" (EEXIST). 2. Falta de información de espacio libre, a veces detectados con el error "falta información de espacio libre para X". 3. Espacio contabilizado dos veces: rangos asignados en el árbol de extensiones y marcados como libres en dicho árbol, rangos marcados como asignados dos veces en el árbol de extensiones o rangos marcados como libres dos veces en dicho árbol. Si estos últimos se almacenaban en el disco, el siguiente reinicio generaría el error BUG_ON() en add_new_free_space(). 4. En algunos hosts sin corrupción en disco ni mensajes de error, la caché de espacio en memoria (volcada con drgn) no coincidía con el árbol de espacio libre. Todos estos síntomas tienen la misma causa subyacente: una competencia entre el almacenamiento en caché del espacio libre de un grupo de bloques y su devolución a la caché de espacio en memoria para las extensiones fijadas provoca la duplicación de un rango libre en la caché de espacio. Esta competencia se produce cuando se almacena en caché el espacio libre del árbol de espacio libre (space_cache=v2) o del árbol de extensiones (nospace_cache, o space_cache=v1 si es necesario regenerar la caché). Se supone que struct btrfs_block_group::last_byte_to_unpin y struct btrfs_block_group::progress protegen contra esta competencia, pero el commit d0c2f4fa555e ("btrfs: hacer que las sincronizaciones simultáneas esperen menos al esperar el commit de una transacción") interrumpió esto sutilmente al permitir que varias transacciones desanclaran extensiones simultáneamente. Específicamente, la ejecución es la siguiente: 1. Se elimina una extensión de un grupo de bloques no almacenados en caché en la transacción A. 2. Se llama a btrfs_commit_transaction() para la transacción A. 3. btrfs_run_delayed_refs() -> __btrfs_free_extent() ejecuta la referencia retrasada para la extensión eliminada. 4. __btrfs_free_extent() -> do_free_extent_accounting() -> add_to_free_space_tree() agrega la extensión eliminada nuevamente al árbol de espacio libre. 5. do_free_extent_accounting() -> btrfs_update_block_group() -> btrfs_cache_block_group() pone en cola el grupo de bloques para almacenar en caché. block_group->progress se establece en block_group->start. 6. btrfs_commit_transaction() para la transacción A llama a switch_commit_roots(). Establece block_group->last_byte_to_unpin en block_group->progress, que es block_group->start porque el grupo de bloques aún no se ha almacenado en caché. 7. El hilo de caché accede a nuestro grupo de bloques. Dado que las raíces de las confirmaciones ya se han cambiado, load_free_space_tree() detecta la extensión eliminada como libre y la añade a la caché de espacio. Finaliza el almacenamiento en caché y establece block_group->progress en U64_MAX. 8. btrfs_commit_transaction() avanza la transacción A a TRANS_STATE_SUPER_COMMITTED. 9. fsync llama a btrfs_commit_transaction() para la transacción B. Dado que la transacción A ya está en TRANS_STATE_SUPER_COMMITTED y el commit es para fsync, avanza. 10. btrfs_commit_transaction() para la transacción B llama a switch_commit_roots(). Esta vez, el grupo de bloques ya se ha almacenado en caché, por lo que establece block_group->last_byte_to_unpin en U64_MAX. 11. btrfs_commit_transaction() para la transacción A llama a btrfs_finish_extent_commit(), que llama a unpin_extent_range() para la extensión eliminada. Ve que last_byte_to_unpin está establecido en U64_MAX (¡por la transacción B!), por lo que vuelve a añadir la extensión eliminada a la caché de espacio. Esto explica todos nuestros síntomas anteriores: * Si la secuencia de eventos es exactamente la descrita anteriormente, cuando se vuelve a añadir el espacio libre en el paso 11, fallará con EEXIST. * ---truncado---

Productos y versiones vulnerables

CPE Desde Hasta
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.12 (incluyendo) 5.15.65 (excluyendo)
cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:* 5.16 (incluyendo) 5.19.6 (excluyendo)
cpe:2.3:o:linux:linux_kernel:6.0:rc1:*:*:*:*:*:*
cpe:2.3:o:linux:linux_kernel:6.0:rc2:*:*:*:*:*:*