CVE-2021-46989
hfsplus: prevent corruption in shrinking truncate
Severity Score
Exploit Likelihood
Affected Versions
Public Exploits
0Exploited in Wild
-Decision
Descriptions
In the Linux kernel, the following vulnerability has been resolved: hfsplus: prevent corruption in shrinking truncate I believe there are some issues introduced by commit 31651c607151
("hfsplus: avoid deadlock on file truncation") HFS+ has extent records which always contains 8 extents. In case the
first extent record in catalog file gets full, new ones are allocated from
extents overflow file. In case shrinking truncate happens to middle of an extent record which
locates in extents overflow file, the logic in hfsplus_file_truncate() was
changed so that call to hfs_brec_remove() is not guarded any more. Right action would be just freeing the extents that exceed the new size
inside extent record by calling hfsplus_free_extents(), and then check if
the whole extent record should be removed. However since the guard
(blk_cnt > start) is now after the call to hfs_brec_remove(), this has
unfortunate effect that the last matching extent record is removed
unconditionally. To reproduce this issue, create a file which has at least 10 extents, and
then perform shrinking truncate into middle of the last extent record, so
that the number of remaining extents is not under or divisible by 8. This
causes the last extent record (8 extents) to be removed totally instead of
truncating into middle of it. Thus this causes corruption, and lost data. Fix for this is simply checking if the new truncated end is below the
start of this extent record, making it safe to remove the full extent
record. However call to hfs_brec_remove() can't be moved to it's previous
place since we're dropping ->tree_lock and it can cause a race condition
and the cached info being invalidated possibly corrupting the node data. Another issue is related to this one. When entering into the block
(blk_cnt > start) we are not holding the ->tree_lock. We break out from
the loop not holding the lock, but hfs_find_exit() does unlock it. Not
sure if it's possible for someone else to take the lock under our feet,
but it can cause hard to debug errors and premature unlocking. Even if
there's no real risk of it, the locking should still always be kept in
balance. Thus taking the lock now just before the check.
En el kernel de Linux, se resolvió la siguiente vulnerabilidad: hfsplus: evita la corrupción al reducir y truncar Creo que hay algunos problemas introducidos por el commit 31651c607151 ("hfsplus: evita el punto muerto en el truncamiento de archivos") HFS+ tiene registros de extensión que siempre contienen 8 extensiones. En caso de que el primer registro de extensión en el archivo de catálogo se llene, se asignan nuevos registros desde el archivo de desbordamiento de extensiones. En caso de que se produzca un truncamiento reducido en la mitad de un registro de extensión que se ubica en un archivo de desbordamiento de extensiones, la lógica en hfsplus_file_truncate() se cambió para que la llamada a hfs_brec_remove() ya no esté protegida. La acción correcta sería simplemente liberar las extensiones que exceden el nuevo tamaño dentro del registro de extensión llamando a hfsplus_free_extents() y luego verificar si se debe eliminar todo el registro de extensión. Sin embargo, dado que la guardia (blk_cnt > start) está ahora después de la llamada a hfs_brec_remove(), esto tiene el efecto desafortunado de que el último registro de extensión coincidente se elimina incondicionalmente. Para reproducir este problema, cree un archivo que tenga al menos 10 extensiones y luego realice un truncamiento reducido hasta la mitad del último registro de extensión, de modo que el número de extensiones restantes no sea menor o divisible por 8. Esto hace que el último registro de extensión ( 8 extensiones) para eliminarse por completo en lugar de truncarse a la mitad. Por tanto, esto provoca corrupción y pérdida de datos. La solución para esto es simplemente verificar si el nuevo final truncado está debajo del inicio de este registro de extensión, lo que hace que sea seguro eliminar el registro de extensión completo. Sin embargo, la llamada a hfs_brec_remove() no se puede mover a su lugar anterior ya que estamos eliminando ->tree_lock y puede provocar una condición de ejecución y la invalidación de la información almacenada en caché, posiblemente corrompiendo los datos del nodo. Otro tema está relacionado con éste. Al ingresar al bloque (blk_cnt > start) no mantenemos ->tree_lock. Salimos del bucle sin mantener el bloqueo, pero hfs_find_exit() lo desbloquea. No estoy seguro de si es posible que otra persona tome el bloqueo bajo nuestros pies, pero puede causar errores difíciles de depurar y desbloqueo prematuro. Aunque no exista ningún riesgo real, el bloqueo siempre debe mantenerse en equilibrio. Tomando así el candado ahora justo antes del control.
In the Linux kernel, the following vulnerability has been resolved: hfsplus: prevent corruption in shrinking truncate I believe there are some issues introduced by commit 31651c607151 ("hfsplus: avoid deadlock on file truncation") HFS+ has extent records which always contains 8 extents. In case the first extent record in catalog file gets full, new ones are allocated from extents overflow file. In case shrinking truncate happens to middle of an extent record which locates in extents overflow file, the logic in hfsplus_file_truncate() was changed so that call to hfs_brec_remove() is not guarded any more. Right action would be just freeing the extents that exceed the new size inside extent record by calling hfsplus_free_extents(), and then check if the whole extent record should be removed. However since the guard (blk_cnt > start) is now after the call to hfs_brec_remove(), this has unfortunate effect that the last matching extent record is removed unconditionally. To reproduce this issue, create a file which has at least 10 extents, and then perform shrinking truncate into middle of the last extent record, so that the number of remaining extents is not under or divisible by 8. This causes the last extent record (8 extents) to be removed totally instead of truncating into middle of it. Thus this causes corruption, and lost data. Fix for this is simply checking if the new truncated end is below the start of this extent record, making it safe to remove the full extent record. However call to hfs_brec_remove() can't be moved to it's previous place since we're dropping ->tree_lock and it can cause a race condition and the cached info being invalidated possibly corrupting the node data. Another issue is related to this one. When entering into the block (blk_cnt > start) we are not holding the ->tree_lock. We break out from the loop not holding the lock, but hfs_find_exit() does unlock it. Not sure if it's possible for someone else to take the lock under our feet, but it can cause hard to debug errors and premature unlocking. Even if there's no real risk of it, the locking should still always be kept in balance. Thus taking the lock now just before the check.
CVSS Scores
SSVC
- Decision:Track
Timeline
- 2024-02-27 CVE Reserved
- 2024-02-28 CVE Published
- 2024-02-29 EPSS Updated
- 2024-12-19 CVE Updated
- ---------- Exploited in Wild
- ---------- KEV Due Date
- ---------- First Exploit
CWE
CAPEC
References (7)
URL | Tag | Source |
---|---|---|
https://git.kernel.org/stable/c/31651c607151f1034cfb57e5a78678bea54c362b | Vuln. Introduced |
URL | Date | SRC |
---|
URL | Date | SRC |
---|
Affected Vendors, Products, and Versions
Vendor | Product | Version | Other | Status | ||||||
---|---|---|---|---|---|---|---|---|---|---|
Vendor | Product | Version | Other | Status | <-- --> | Vendor | Product | Version | Other | Status |
Linux Search vendor "Linux" | Linux Kernel Search vendor "Linux" for product "Linux Kernel" | >= 4.19 < 4.19.191 Search vendor "Linux" for product "Linux Kernel" and version " >= 4.19 < 4.19.191" | en |
Affected
| ||||||
Linux Search vendor "Linux" | Linux Kernel Search vendor "Linux" for product "Linux Kernel" | >= 4.19 < 5.4.120 Search vendor "Linux" for product "Linux Kernel" and version " >= 4.19 < 5.4.120" | en |
Affected
| ||||||
Linux Search vendor "Linux" | Linux Kernel Search vendor "Linux" for product "Linux Kernel" | >= 4.19 < 5.10.38 Search vendor "Linux" for product "Linux Kernel" and version " >= 4.19 < 5.10.38" | en |
Affected
| ||||||
Linux Search vendor "Linux" | Linux Kernel Search vendor "Linux" for product "Linux Kernel" | >= 4.19 < 5.11.22 Search vendor "Linux" for product "Linux Kernel" and version " >= 4.19 < 5.11.22" | en |
Affected
| ||||||
Linux Search vendor "Linux" | Linux Kernel Search vendor "Linux" for product "Linux Kernel" | >= 4.19 < 5.12.5 Search vendor "Linux" for product "Linux Kernel" and version " >= 4.19 < 5.12.5" | en |
Affected
| ||||||
Linux Search vendor "Linux" | Linux Kernel Search vendor "Linux" for product "Linux Kernel" | >= 4.19 < 5.13 Search vendor "Linux" for product "Linux Kernel" and version " >= 4.19 < 5.13" | en |
Affected
|