FKIE_CVE-2026-23233
Vulnerability from fkie_nvd - Published: 2026-03-04 15:16 - Updated: 2026-03-04 18:08
Severity ?
Summary
In the Linux kernel, the following vulnerability has been resolved:
f2fs: fix to avoid mapping wrong physical block for swapfile
Xiaolong Guo reported a f2fs bug in bugzilla [1]
[1] https://bugzilla.kernel.org/show_bug.cgi?id=220951
Quoted:
"When using stress-ng's swap stress test on F2FS filesystem with kernel 6.6+,
the system experiences data corruption leading to either:
1 dm-verity corruption errors and device reboot
2 F2FS node corruption errors and boot hangs
The issue occurs specifically when:
1 Using F2FS filesystem (ext4 is unaffected)
2 Swapfile size is less than F2FS section size (2MB)
3 Swapfile has fragmented physical layout (multiple non-contiguous extents)
4 Kernel version is 6.6+ (6.1 is unaffected)
The root cause is in check_swap_activate() function in fs/f2fs/data.c. When the
first extent of a small swapfile (< 2MB) is not aligned to section boundaries,
the function incorrectly treats it as the last extent, failing to map
subsequent extents. This results in incorrect swap_extent creation where only
the first extent is mapped, causing subsequent swap writes to overwrite wrong
physical locations (other files' data).
Steps to Reproduce
1 Setup a device with F2FS-formatted userdata partition
2 Compile stress-ng from https://github.com/ColinIanKing/stress-ng
3 Run swap stress test: (Android devices)
adb shell "cd /data/stressng; ./stress-ng-64 --metrics-brief --timeout 60
--swap 0"
Log:
1 Ftrace shows in kernel 6.6, only first extent is mapped during second
f2fs_map_blocks call in check_swap_activate():
stress-ng-swap-8990: f2fs_map_blocks: ino=11002, file offset=0, start
blkaddr=0x43143, len=0x1
(Only 4KB mapped, not the full swapfile)
2 in kernel 6.1, both extents are correctly mapped:
stress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=0, start
blkaddr=0x13cd4, len=0x1
stress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=1, start
blkaddr=0x60c84b, len=0xff
The problematic code is in check_swap_activate():
if ((pblock - SM_I(sbi)->main_blkaddr) % blks_per_sec ||
nr_pblocks % blks_per_sec ||
!f2fs_valid_pinned_area(sbi, pblock)) {
bool last_extent = false;
not_aligned++;
nr_pblocks = roundup(nr_pblocks, blks_per_sec);
if (cur_lblock + nr_pblocks > sis->max)
nr_pblocks -= blks_per_sec;
/* this extent is last one */
if (!nr_pblocks) {
nr_pblocks = last_lblock - cur_lblock;
last_extent = true;
}
ret = f2fs_migrate_blocks(inode, cur_lblock, nr_pblocks);
if (ret) {
if (ret == -ENOENT)
ret = -EINVAL;
goto out;
}
if (!last_extent)
goto retry;
}
When the first extent is unaligned and roundup(nr_pblocks, blks_per_sec)
exceeds sis->max, we subtract blks_per_sec resulting in nr_pblocks = 0. The
code then incorrectly assumes this is the last extent, sets nr_pblocks =
last_lblock - cur_lblock (entire swapfile), and performs migration. After
migration, it doesn't retry mapping, so subsequent extents are never processed.
"
In order to fix this issue, we need to lookup block mapping info after
we migrate all blocks in the tail of swapfile.
References
Impacted products
| Vendor | Product | Version |
|---|
{
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "In the Linux kernel, the following vulnerability has been resolved:\n\nf2fs: fix to avoid mapping wrong physical block for swapfile\n\nXiaolong Guo reported a f2fs bug in bugzilla [1]\n\n[1] https://bugzilla.kernel.org/show_bug.cgi?id=220951\n\nQuoted:\n\n\"When using stress-ng\u0027s swap stress test on F2FS filesystem with kernel 6.6+,\nthe system experiences data corruption leading to either:\n1 dm-verity corruption errors and device reboot\n2 F2FS node corruption errors and boot hangs\n\nThe issue occurs specifically when:\n1 Using F2FS filesystem (ext4 is unaffected)\n2 Swapfile size is less than F2FS section size (2MB)\n3 Swapfile has fragmented physical layout (multiple non-contiguous extents)\n4 Kernel version is 6.6+ (6.1 is unaffected)\n\nThe root cause is in check_swap_activate() function in fs/f2fs/data.c. When the\nfirst extent of a small swapfile (\u003c 2MB) is not aligned to section boundaries,\nthe function incorrectly treats it as the last extent, failing to map\nsubsequent extents. This results in incorrect swap_extent creation where only\nthe first extent is mapped, causing subsequent swap writes to overwrite wrong\nphysical locations (other files\u0027 data).\n\nSteps to Reproduce\n1 Setup a device with F2FS-formatted userdata partition\n2 Compile stress-ng from https://github.com/ColinIanKing/stress-ng\n3 Run swap stress test: (Android devices)\nadb shell \"cd /data/stressng; ./stress-ng-64 --metrics-brief --timeout 60\n--swap 0\"\n\nLog:\n1 Ftrace shows in kernel 6.6, only first extent is mapped during second\nf2fs_map_blocks call in check_swap_activate():\nstress-ng-swap-8990: f2fs_map_blocks: ino=11002, file offset=0, start\nblkaddr=0x43143, len=0x1\n(Only 4KB mapped, not the full swapfile)\n2 in kernel 6.1, both extents are correctly mapped:\nstress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=0, start\nblkaddr=0x13cd4, len=0x1\nstress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=1, start\nblkaddr=0x60c84b, len=0xff\n\nThe problematic code is in check_swap_activate():\nif ((pblock - SM_I(sbi)-\u003emain_blkaddr) % blks_per_sec ||\n nr_pblocks % blks_per_sec ||\n !f2fs_valid_pinned_area(sbi, pblock)) {\n bool last_extent = false;\n\n not_aligned++;\n\n nr_pblocks = roundup(nr_pblocks, blks_per_sec);\n if (cur_lblock + nr_pblocks \u003e sis-\u003emax)\n nr_pblocks -= blks_per_sec;\n\n /* this extent is last one */\n if (!nr_pblocks) {\n nr_pblocks = last_lblock - cur_lblock;\n last_extent = true;\n }\n\n ret = f2fs_migrate_blocks(inode, cur_lblock, nr_pblocks);\n if (ret) {\n if (ret == -ENOENT)\n ret = -EINVAL;\n goto out;\n }\n\n if (!last_extent)\n goto retry;\n}\n\nWhen the first extent is unaligned and roundup(nr_pblocks, blks_per_sec)\nexceeds sis-\u003emax, we subtract blks_per_sec resulting in nr_pblocks = 0. The\ncode then incorrectly assumes this is the last extent, sets nr_pblocks =\nlast_lblock - cur_lblock (entire swapfile), and performs migration. After\nmigration, it doesn\u0027t retry mapping, so subsequent extents are never processed.\n\"\n\nIn order to fix this issue, we need to lookup block mapping info after\nwe migrate all blocks in the tail of swapfile."
},
{
"lang": "es",
"value": "En el kernel de Linux, la siguiente vulnerabilidad ha sido resuelta:\n\nf2fs: correcci\u00f3n para evitar mapear un bloque f\u00edsico incorrecto para el archivo de intercambio\n\nXiaolong Guo inform\u00f3 de un error de f2fs en bugzilla [1]\n\n[1] HTTPS://bugzilla.kernel.org/show_bug.cgi?id=220951\n\nCitado:\n\nCuando se utiliza la prueba de estr\u00e9s de intercambio de stress-ng en el sistema de archivos F2FS con kernel 6.6+, el sistema experimenta corrupci\u00f3n de datos que lleva a una de las siguientes situaciones:\n1 errores de corrupci\u00f3n de dm-verity y reinicio del dispositivo\n2 errores de corrupci\u00f3n de nodo F2FS y cuelgues de arranque\n\nEl problema ocurre espec\u00edficamente cuando:\n1 Se utiliza el sistema de archivos F2FS (ext4 no se ve afectado)\n2 El tama\u00f1o del archivo de intercambio es menor que el tama\u00f1o de secci\u00f3n de F2FS (2MB)\n3 El archivo de intercambio tiene un dise\u00f1o f\u00edsico fragmentado (m\u00faltiples extensiones no contiguas)\n4 La versi\u00f3n del kernel es 6.6+ (6.1 no se ve afectada)\n\nLa causa ra\u00edz est\u00e1 en la funci\u00f3n check_swap_activate() en fs/f2fs/data.c. Cuando la primera extensi\u00f3n de un archivo de intercambio peque\u00f1o (\u0026lt; 2MB) no est\u00e1 alineada con los l\u00edmites de secci\u00f3n, la funci\u00f3n lo trata incorrectamente como la \u00faltima extensi\u00f3n, fallando en mapear extensiones subsiguientes. Esto resulta en una creaci\u00f3n incorrecta de swap_extent donde solo la primera extensi\u00f3n es mapeada, causando que las escrituras de intercambio subsiguientes sobrescriban ubicaciones f\u00edsicas incorrectas (datos de otros archivos).\n\nPasos para Reproducir\n1 Configurar un dispositivo con una partici\u00f3n de datos de usuario formateada en F2FS\n2 Compilar stress-ng desde HTTPS://github.com/ColinIanKing/stress-ng\n3 Ejecutar la prueba de estr\u00e9s de intercambio: (dispositivos Android)\nadb shell \u0027cd /data/stressng; ./stress-ng-64 --metrics-brief --timeout 60 --swap 0\u0027\n\nRegistro:\n1 Ftrace muestra que en el kernel 6.6, solo la primera extensi\u00f3n se mapea durante la segunda llamada a f2fs_map_blocks en check_swap_activate():\nstress-ng-swap-8990: f2fs_map_blocks: ino=11002, file offset=0, start blkaddr=0x43143, len=0x1\n(Solo 4KB mapeados, no el archivo de intercambio completo)\n2 en el kernel 6.1, ambas extensiones se mapean correctamente:\nstress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=0, start blkaddr=0x13cd4, len=0x1\nstress-ng-swap-5966: f2fs_map_blocks: ino=28011, file offset=1, start blkaddr=0x60c84b, len=0xff\n\nEl c\u00f3digo problem\u00e1tico est\u00e1 en check_swap_activate():\nif ((pblock - SM_I(sbi)-\u0026gt;main_blkaddr) % blks_per_sec ||\n nr_pblocks % blks_per_sec ||\n !f2fs_valid_pinned_area(sbi, pblock)) {\n bool last_extent = false;\n\n not_aligned++;\n\n nr_pblocks = roundup(nr_pblocks, blks_per_sec);\n if (cur_lblock + nr_pblocks \u0026gt; sis-\u0026gt;max)\n nr_pblocks -= blks_per_sec;\n\n /* esta extensi\u00f3n es la \u00faltima */\n if (!nr_pblocks) {\n nr_pblocks = last_lblock - cur_lblock;\n last_extent = true;\n }\n\n ret = f2fs_migrate_blocks(inode, cur_lblock, nr_pblocks);\n if (ret) {\n if (ret == -ENOENT)\n ret = -EINVAL;\n goto out;\n }\n\n if (!last_extent)\n goto retry;\n}\n\nCuando la primera extensi\u00f3n no est\u00e1 alineada y roundup(nr_pblocks, blks_per_sec) excede sis-\u0026gt;max, restamos blks_per_sec resultando en nr_pblocks = 0. El c\u00f3digo asume incorrectamente que esta es la \u00faltima extensi\u00f3n, establece nr_pblocks = last_lblock - cur_lblock (archivo de intercambio completo), y realiza la migraci\u00f3n. Despu\u00e9s de la migraci\u00f3n, no reintenta el mapeo, por lo que las extensiones subsiguientes nunca se procesan.\n\nPara solucionar este problema, necesitamos buscar la informaci\u00f3n de mapeo de bloques despu\u00e9s de migrar todos los bloques en la cola del archivo de intercambio."
}
],
"id": "CVE-2026-23233",
"lastModified": "2026-03-04T18:08:05.730",
"metrics": {},
"published": "2026-03-04T15:16:13.603",
"references": [
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"url": "https://git.kernel.org/stable/c/1ff415eef513bf12deb058fc50d57788c46c48e6"
},
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"url": "https://git.kernel.org/stable/c/5c145c03188bc9ba1c29e0bc4d527a5978fc47f9"
},
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"url": "https://git.kernel.org/stable/c/607cb9d83838d2cd9f0406c2403ed61aadf0edff"
},
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"url": "https://git.kernel.org/stable/c/d4534a7f6c92baaf7e12a45fc6e37332cafafc33"
},
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"url": "https://git.kernel.org/stable/c/fee27b69dde1a05908b350eea42937af2387c4fe"
}
],
"sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"vulnStatus": "Awaiting Analysis"
}
Loading…
Loading…
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Loading…
Loading…