CVE-2024-47741
In the Linux kernel, the following vulnerability has been resolved:
btrfs: fix race setting file private on concurrent lseek using same fd
When doing concurrent lseek(2) system calls against the same file descriptor, using multiple threads belonging to the same process, we have a short time window where a race happens and can result in a memory leak.
The race happens like this:
Besides the memory leak, there's also the detail that both tasks end up using the same cached state record in the private structure (struct btrfs_file_private::llseek_cached_state), which can result in a use-after-free problem since one task can free it while the other is still using it (only one task took a reference count on it).
Leer descripción completaMostrar menos
Also, sharing the cached state is not a good idea since it could result in incorrect results in the future - right now it should not be a problem because it end ups being used only in extent-io-tree.c:count_range_bits() where we do range validation before using the cached state.
Fix this by protecting the private assignment and check of a file while holding the inode's spinlock and keep track of the task that allocated the private, so that it's used only by that task in order to prevent user-after-free issues with the cached state record as well as potentially using it incorrectly in the future.
Detalles técnicos trazas, registros y código del informe original
1) A program opens a file descriptor for a file and then spawns two threads (with the pthreads library for example), lets call them task A and task B; 2) Task A calls lseek with SEEK_DATA or SEEK_HOLE and ends up at file.c:find_desired_extent() while holding a read lock on the inode; 3) At the start of find_desired_extent(), it extracts the file's private_data pointer into a local variable named 'private', which has a value of NULL; 4) Task B also calls lseek with SEEK_DATA or SEEK_HOLE, locks the inode in shared mode and enters file.c:find_desired_extent(), where it also extracts file->private_data into its local variable 'private', which has a NULL value; 5) Because it saw a NULL file private, task A allocates a private structure and assigns to the file structure; 6) Task B also saw a NULL file private so it also allocates its own file private and then assigns it to the same file structure, since both tasks are using the same file descriptor. At this point we leak the private structure allocated by task A.
CVSS
- Versión: 3.1
- Vector: CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H
- Puntuación base: 7
Probabilidad de explotación (EPSS)
- Probabilidad de explotación en los próximos 30 días: 0.17%
- Percentil entre todas las CVEs puntuadas: 5
- Fecha de la puntuación: 4/10/2026
EPSS (Exploit Prediction Scoring System, de FIRST) estima la probabilidad de que una vulnerabilidad sea explotada en 30 días. Complementa a CVSS (impacto) y a CISA KEV (explotación confirmada).
🎯 Técnicas ATT&CK
Cómo se explota esta vulnerabilidad y qué consigue el atacante, en el lenguaje de MITRE ATT&CK.
- Explotación
T1068Exploitation for Privilege Escalationprivilege escalation85 % - Impacto secundario
T1499.004Application or System Exploitationimpact60 %
Vulnerabilidad local (AV:L) sin interacción (UI:N) ni privilegios requeridos (PR:L) que permite race condition en lseek causando use-after-free y DoS; CWE-362 (race condition) típico de escalada de privilegios o DoS del kernel.
Inferido por nuestro agente de análisis a partir de la descripción oficial, el vector CVSS y la CWE, y comprobado por un supervisor. Puede contener errores.
🛡️ Mitigaciones ATT&CK que cubren estas técnicas
Tecnologías afectadas (1)
CWE
- CWE-362
Referencias
JSON original (NVD)
Mostrar
{
"id": "CVE-2024-47741",
"cveTags": [],
"metrics": {
"ssvcV203": [
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"ssvcData": {
"id": "CVE-2024-47741",
"role": "CISA Coordinator",
"options": [
{
"exploitation": "none"
},
{
"automatable": "no"
},
{
"technicalImpact": "partial"
}
],
"version": "2.0.3",
"timestamp": "2024-10-21T12:59:11.632637Z"
}
}
],
"cvssMetricV31": [
{
"type": "Secondary",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"cvssData": {
"scope": "UNCHANGED",
"version": "3.1",
"baseScore": 7.8,
"attackVector": "LOCAL",
"baseSeverity": "HIGH",
"vectorString": "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
"integrityImpact": "HIGH",
"userInteraction": "NONE",
"attackComplexity": "LOW",
"availabilityImpact": "HIGH",
"privilegesRequired": "LOW",
"confidentialityImpact": "HIGH"
},
"impactScore": 5.9,
"exploitabilityScore": 1.8
},
{
"type": "Primary",
"source": "nvd@nist.gov",
"cvssData": {
"scope": "UNCHANGED",
"version": "3.1",
"baseScore": 7,
"attackVector": "LOCAL",
"baseSeverity": "HIGH",
"vectorString": "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:H",
"integrityImpact": "HIGH",
"userInteraction": "NONE",
"attackComplexity": "HIGH",
"availabilityImpact": "HIGH",
"privilegesRequired": "LOW",
"confidentialityImpact": "HIGH"
},
"impactScore": 5.9,
"exploitabilityScore": 1
}
]
},
"affected": [
{
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67",
"affectedData": [
{
"repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
"vendor": "Linux",
"product": "Linux",
"versions": [
{
"status": "affected",
"version": "3c32c7212f1639471ec0197ff1179b8ef2e0f3d3",
"lessThan": "f56a6d9c267ec7fa558ede7755551c047b1034cd",
"versionType": "git"
},
{
"status": "affected",
"version": "3c32c7212f1639471ec0197ff1179b8ef2e0f3d3",
"lessThan": "a412ca489ac27b9d0e603499315b7139c948130d",
"versionType": "git"
},
{
"status": "affected",
"version": "3c32c7212f1639471ec0197ff1179b8ef2e0f3d3",
"lessThan": "33d1310d4496e904123dab9c28b2d8d2c1800f97",
"versionType": "git"
},
{
"status": "affected",
"version": "3c32c7212f1639471ec0197ff1179b8ef2e0f3d3",
"lessThan": "7ee85f5515e86a4e2a2f51969795920733912bad",
"versionType": "git"
}
],
"programFiles": [
"fs/btrfs/btrfs_inode.h",
"fs/btrfs/ctree.h",
"fs/btrfs/file.c"
],
"defaultStatus": "unaffected"
},
{
"repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
"vendor": "Linux",
"product": "Linux",
"versions": [
{
"status": "affected",
"version": "6.2"
},
{
"status": "unaffected",
"version": "0",
"lessThan": "6.2",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "6.6.54",
"versionType": "semver",
"lessThanOrEqual": "6.6.*"
},
{
"status": "unaffected",
"version": "6.10.13",
"versionType": "semver",
"lessThanOrEqual": "6.10.*"
},
{
"status": "unaffected",
"version": "6.11.2",
"versionType": "semver",
"lessThanOrEqual": "6.11.*"
},
{
"status": "unaffected",
"version": "6.12",
"versionType": "original_commit_for_fix",
"lessThanOrEqual": "*"
}
],
"programFiles": [
"fs/btrfs/btrfs_inode.h",
"fs/btrfs/ctree.h",
"fs/btrfs/file.c"
],
"defaultStatus": "affected"
}
]
}
],
"published": "2024-10-21T13:15:04.207",
"references": [
{
"url": "https://git.kernel.org/stable/c/33d1310d4496e904123dab9c28b2d8d2c1800f97",
"tags": [
"Patch"
],
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/7ee85f5515e86a4e2a2f51969795920733912bad",
"tags": [
"Patch"
],
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/a412ca489ac27b9d0e603499315b7139c948130d",
"tags": [
"Patch"
],
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/f56a6d9c267ec7fa558ede7755551c047b1034cd",
"tags": [
"Patch"
],
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
}
],
"vulnStatus": "Modified",
"weaknesses": [
{
"type": "Primary",
"source": "nvd@nist.gov",
"description": [
{
"lang": "en",
"value": "CWE-362"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "In the Linux kernel, the following vulnerability has been resolved:\n\nbtrfs: fix race setting file private on concurrent lseek using same fd\n\nWhen doing concurrent lseek(2) system calls against the same file\ndescriptor, using multiple threads belonging to the same process, we have\na short time window where a race happens and can result in a memory leak.\n\nThe race happens like this:\n\n1) A program opens a file descriptor for a file and then spawns two\n threads (with the pthreads library for example), lets call them\n task A and task B;\n\n2) Task A calls lseek with SEEK_DATA or SEEK_HOLE and ends up at\n file.c:find_desired_extent() while holding a read lock on the inode;\n\n3) At the start of find_desired_extent(), it extracts the file's\n private_data pointer into a local variable named 'private', which has\n a value of NULL;\n\n4) Task B also calls lseek with SEEK_DATA or SEEK_HOLE, locks the inode\n in shared mode and enters file.c:find_desired_extent(), where it also\n extracts file->private_data into its local variable 'private', which\n has a NULL value;\n\n5) Because it saw a NULL file private, task A allocates a private\n structure and assigns to the file structure;\n\n6) Task B also saw a NULL file private so it also allocates its own file\n private and then assigns it to the same file structure, since both\n tasks are using the same file descriptor.\n\n At this point we leak the private structure allocated by task A.\n\nBesides the memory leak, there's also the detail that both tasks end up\nusing the same cached state record in the private structure (struct\nbtrfs_file_private::llseek_cached_state), which can result in a\nuse-after-free problem since one task can free it while the other is\nstill using it (only one task took a reference count on it). Also, sharing\nthe cached state is not a good idea since it could result in incorrect\nresults in the future - right now it should not be a problem because it\nend ups being used only in extent-io-tree.c:count_range_bits() where we do\nrange validation before using the cached state.\n\nFix this by protecting the private assignment and check of a file while\nholding the inode's spinlock and keep track of the task that allocated\nthe private, so that it's used only by that task in order to prevent\nuser-after-free issues with the cached state record as well as potentially\nusing it incorrectly in the future."
},
{
"lang": "es",
"value": "En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad: btrfs: arreglo de ejecución que establece el archivo privado en lseek concurrente usando el mismo fd Al realizar llamadas al sistema lseek(2) concurrentes contra el mismo descriptor de archivo, usando múltiples subprocesos que pertenecen al mismo proceso, tenemos una ventana de tiempo corta donde ocurre una ejecución y puede resultar en una pérdida de memoria. La ejecución ocurre así: 1) Un programa abre un descriptor de archivo para un archivo y luego genera dos subprocesos (con la librería pthreads por ejemplo), llamémoslos tarea A y tarea B; 2) La tarea A llama a lseek con SEEK_DATA o SEEK_HOLE y termina en file.c:find_desired_extent() mientras mantiene un bloqueo de lectura en el inodo; 3) Al comienzo de find_desired_extent(), extrae el puntero private_data del archivo en una variable local llamada 'private', que tiene un valor de NULL; 4) La tarea B también llama a lseek con SEEK_DATA o SEEK_HOLE, bloquea el inodo en modo compartido e ingresa a file.c:find_desired_extent(), donde también extrae file->private_data en su variable local 'private', que tiene un valor NULL; 5) Debido a que vio un archivo privado NULL, la tarea A asigna una estructura privada y la asigna a la estructura de archivo; 6) La tarea B también vio un archivo privado NULL, por lo que también asigna su propio archivo privado y luego lo asigna a la misma estructura de archivo, ya que ambas tareas están usando el mismo descriptor de archivo. En este punto filtramos la estructura privada asignada por la tarea A. Además de la fuga de memoria, también está el detalle de que ambas tareas terminan usando el mismo registro de estado en caché en la estructura privada (struct btrfs_file_private::llseek_cached_state), lo que puede resultar en un problema de use after free, ya que una tarea puede liberarlo mientras la otra todavía lo está usando (solo una tarea tomó un recuento de referencia en él). Además, compartir el estado en caché no es una buena idea, ya que podría resultar en resultados incorrectos en el futuro; en este momento no debería ser un problema porque termina siendo utilizado solo en extended-io-tree.c:count_range_bits() donde realizamos la validación de rango antes de usar el estado en caché. Solucione esto protegiendo la asignación privada y la verificación de un archivo mientras mantiene el spinlock del inodo y realice un seguimiento de la tarea que asignó el privado, de modo que solo lo use esa tarea para evitar problemas de use after free con el registro de estado en caché, así como potencialmente usarlo incorrectamente en el futuro."
}
],
"lastModified": "2026-08-04T11:20:46.913",
"configurations": [
{
"nodes": [
{
"negate": false,
"cpeMatch": [
{
"criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
"vulnerable": true,
"matchCriteriaId": "D448821D-C085-4CAF-88FA-2DDE7BE21976",
"versionEndExcluding": "6.6.54",
"versionStartIncluding": "6.2"
},
{
"criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
"vulnerable": true,
"matchCriteriaId": "CE94BB8D-B0AB-4563-9ED7-A12122B56EBE",
"versionEndExcluding": "6.10.13",
"versionStartIncluding": "6.7"
},
{
"criteria": "cpe:2.3:o:linux:linux_kernel:*:*:*:*:*:*:*:*",
"vulnerable": true,
"matchCriteriaId": "AB755D26-97F4-43B6-8604-CD076811E181",
"versionEndExcluding": "6.11.2",
"versionStartIncluding": "6.11"
}
],
"operator": "OR"
}
]
}
],
"sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
}