CVE-2026-98530
En el kernel de Linux se ha resuelto la siguiente vulnerabilidad:
kprobes: Corregir el bloqueo permanente al vaciar el optimizador de kprobes
Escribir 0 en /proc/sys/debug/kprobes-optimization mientras una kprobe está optimizada con salto nunca regresa. El proceso que escribe queda dormido en estado D para siempre con kprobe_sysctl_mutex retenido, por lo que cualquier lectura o escritura posterior de esa sysctl también se queda bloqueada. Por ejemplo, con vfs_read+9 como dirección optimizable en esta compilación:
wait_for_kprobe_optimizer_locked() reinicializa optimizer_completion, pide al hilo del optimizador que vacíe las listas y se duerme en wait_for_completion().
Leer descripción completaMostrar menos
El hilo vacía las listas de (des)optimización, pero solo llama a complete() si completion_done() es verdadero, es decir, si la completion ya está terminada, lo que nunca ocurre mientras alguien espera. disarm_all_kprobes() y kprobe_trace_self_tests_init() esperan de la misma forma.
Llamar a complete() incondicionalmente no bastaría: el proceso que espera libera kprobe_mutex mientras duerme, y nada más serializa el manejador de sysctl frente al fichero "enabled" de debugfs. Un segundo vaciador que aún encuentre las listas no vacías, por ejemplo porque una kprobe desactivada está en cola para desoptimizarse, reinicializa la completion bajo el primero:
donde c es &optimizer_completion. Si ambas escrituras coinciden durante una pasada del optimizador, el escritor de sysctl se queda sin despertar de esta forma.
Se sustituye la completion por un contador de pasadas del optimizador, que se incrementa al final de cada pasada y se notifica con wake_up_var_locked(), ambos bajo kprobe_mutex. Un vaciador toma una muestra del contador y espera con wait_var_event_mutex(), que libera kprobe_mutex solo mientras duerme, de modo que un valor nuevo significa que se ha ejecutado una pasada completa entretanto. No se reinicializa nada, así que varios vaciadores pueden dormir en la espera a la vez.
Detalles técnicos trazas, registros y código del informe original
# cd /sys/kernel/tracing
# echo 'p:myprobe vfs_read+9' >> kprobe_events
# echo 1 > events/kprobes/myprobe/enable
# # esperar hasta que /sys/kernel/debug/kprobes/list muestre [OPTIMIZED]
# echo 0 > /proc/sys/debug/kprobes-optimization
INFO: task sh:246 blocked for more than 10 seconds.
Call Trace:
<TASK>
__schedule+0x1176/0x4f70
schedule+0xdc/0x2c0
schedule_timeout+0x17b/0x260
wait_for_completion+0x173/0x3c0
wait_for_kprobe_optimizer_locked+0xbc/0x130
proc_kprobes_optimization_handler+0x156/0x1b0
proc_sys_call_handler+0x324/0x490
vfs_write+0x52d/0xfe0
ksys_write+0xff/0x200
do_syscall_64+0x106/0x630
entry_SYSCALL_64_after_hwframe+0x77/0x7f
</TASK>
...
INFO: task cat:265 is blocked on a mutex likely owned by task sh:246.
escritura en sysctl escritura en "enabled" de debugfs
unoptimize_all_kprobes()
wait_for_kprobe_optimizer_locked()
init_completion(c)
mutex_unlock(&kprobe_mutex)
wait_for_completion(c)
disarm_all_kprobes()
wait_for_kprobe_optimizer_locked()
init_completion(c)
// c->wait se reinicia, el
// primer esperador sale de la cola
mutex_unlock(&kprobe_mutex)
wait_for_completion(c)
kprobe_optimizer()
complete(c)
// despierta solo al escritor de debugfsTraducción automática del texto original de NVD (en inglés).
CVSS
NVD no ha asignado puntuación CVSS a esta CVE (habitual desde el cambio de política de abril de 2026).
Probabilidad de explotación (EPSS)
- Probabilidad de explotación en los próximos 30 días
- 0.20 %
- Percentil entre todas las CVEs puntuadas
- 9
- Fecha de la puntuación
- 20/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).
Tecnologías afectadas (1)
⚠ Inferidas por IA a partir de la descripción — NVD aún no ha analizado esta CVE; no son CPE verificados.
Referencias
JSON original (NVD)
Mostrar
{
"id": "CVE-2026-98530",
"cveTags": [],
"metrics": {},
"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": "73c12f209462d1712c5f55f3021a1b65b2e084c3",
"lessThan": "f05a01fc7d4f731b4c100d2aef1e85ad77728862",
"versionType": "git"
},
{
"status": "affected",
"version": "73c12f209462d1712c5f55f3021a1b65b2e084c3",
"lessThan": "5bfa9f1a9dcb6ecb607adbc1c0226605c972935b",
"versionType": "git"
}
],
"programFiles": [
"kernel/kprobes.c"
],
"defaultStatus": "unaffected"
},
{
"repo": "https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git",
"vendor": "Linux",
"product": "Linux",
"versions": [
{
"status": "affected",
"version": "7.0"
},
{
"status": "unaffected",
"version": "0",
"lessThan": "7.0",
"versionType": "semver"
},
{
"status": "unaffected",
"version": "7.2.9",
"versionType": "semver",
"lessThanOrEqual": "7.2.*"
},
{
"status": "unaffected",
"version": "7.3",
"versionType": "original_commit_for_fix",
"lessThanOrEqual": "*"
}
],
"programFiles": [
"kernel/kprobes.c"
],
"defaultStatus": "affected"
}
]
}
],
"published": "2026-10-18T16:17:04.530",
"references": [
{
"url": "https://git.kernel.org/stable/c/5bfa9f1a9dcb6ecb607adbc1c0226605c972935b",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
},
{
"url": "https://git.kernel.org/stable/c/f05a01fc7d4f731b4c100d2aef1e85ad77728862",
"source": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
}
],
"vulnStatus": "Received",
"descriptions": [
{
"lang": "en",
"value": "In the Linux kernel, the following vulnerability has been resolved:\n\nkprobes: Fix permanent hang when flushing the kprobe optimizer\n\nWriting 0 to /proc/sys/debug/kprobes-optimization while a kprobe is\njump-optimized never returns. The writer sleeps in D state forever with\nkprobe_sysctl_mutex held, so any later read or write of that sysctl\nhangs as well. For example, with vfs_read+9 as an optimizable address\nin this build:\n\n # cd /sys/kernel/tracing\n # echo 'p:myprobe vfs_read+9' >> kprobe_events\n # echo 1 > events/kprobes/myprobe/enable\n # # wait until /sys/kernel/debug/kprobes/list shows [OPTIMIZED]\n # echo 0 > /proc/sys/debug/kprobes-optimization\n\n INFO: task sh:246 blocked for more than 10 seconds.\n Call Trace:\n <TASK>\n __schedule+0x1176/0x4f70\n schedule+0xdc/0x2c0\n schedule_timeout+0x17b/0x260\n wait_for_completion+0x173/0x3c0\n wait_for_kprobe_optimizer_locked+0xbc/0x130\n proc_kprobes_optimization_handler+0x156/0x1b0\n proc_sys_call_handler+0x324/0x490\n vfs_write+0x52d/0xfe0\n ksys_write+0xff/0x200\n do_syscall_64+0x106/0x630\n entry_SYSCALL_64_after_hwframe+0x77/0x7f\n </TASK>\n ...\n INFO: task cat:265 is blocked on a mutex likely owned by task sh:246.\n\nwait_for_kprobe_optimizer_locked() reinitializes optimizer_completion,\nasks the optimizer thread to flush and sleeps in wait_for_completion().\nThe thread drains the (un)optimizing lists, but calls complete() only\nif completion_done() is true, i.e. if the completion is already done,\nwhich never happens while someone waits. disarm_all_kprobes() and\nkprobe_trace_self_tests_init() wait the same way.\n\nCalling complete() unconditionally would not be enough: the waiter\ndrops kprobe_mutex while it sleeps, and nothing else serializes the\nsysctl handler against the debugfs \"enabled\" file. A second flusher\nthat still finds the lists non-empty, e.g. because a disabled probe is\nqueued for unoptimizing, reinitializes the completion under the first:\n\n sysctl write debugfs \"enabled\" write\n unoptimize_all_kprobes()\n wait_for_kprobe_optimizer_locked()\n init_completion(c)\n mutex_unlock(&kprobe_mutex)\n wait_for_completion(c)\n disarm_all_kprobes()\n wait_for_kprobe_optimizer_locked()\n init_completion(c)\n // c->wait is reset, the first\n // waiter is off the queue\n mutex_unlock(&kprobe_mutex)\n wait_for_completion(c)\n kprobe_optimizer()\n complete(c)\n // wakes the debugfs writer only\n\nwhere c is &optimizer_completion. Lining up the two writes during an\noptimizer pass loses the sysctl writer this way.\n\nReplace the completion with a counter of optimizer passes, bumped at the\nend of each pass and signalled with wake_up_var_locked(), both under\nkprobe_mutex. A flusher samples the count and waits with\nwait_var_event_mutex(), which drops kprobe_mutex only while sleeping, so\na new count means a whole pass ran in the meantime. Nothing is\nreinitialized, so several flushers can sleep in the wait at once."
}
],
"lastModified": "2026-10-19T10:19:08.410",
"sourceIdentifier": "416baaa9-dc9f-4396-8d5f-8c081fb06d67"
}