CVE-2026-89974 in Linux
Resumen
por VulDB • 2026-09-16
En el kernel de Linux, se ha resuelto la siguiente vulnerabilidad:
nvme-fc: corregir doble liberación (double free) de las opciones de fabrics cuando nvme_add_ctrl() falla.
nvmf_create_ctrl() es propietaria de las opciones de fabrics y las libera siempre que ->create_ctrl() devuelva un error, por lo que el transporte no debe liberarlas en sus propias rutas de error. nvme-fc rastrea esto comprobando ctrl->ctrl.opts en nvme_fc_ctrl_free(), lo cual requiere que nvme_fc_init_ctrl() limpie ese puntero en cada salida por error.
El acoplamiento es implícito, y el commit 1a9e218195a5 ("nvme: split device add from initialization") lo rompió al añadir una segunda ruta de salida por error. Cuando nvme_add_ctrl() falla, nvme_fc_init_ctrl() salta a out_put_ctrl:, pasando por alto "ctrl->ctrl.opts = NULL" que solo se encuentra en la ruta fail_ctrl:, por lo que nvme_fc_ctrl_free() libera las opciones y nvmf_create_ctrl() las libera una segunda vez:
BUG: KASAN: slab-use-after-free in nvmf_free_options+0x30/0x190 nvmf_free_options+0x30/0x190 drivers/nvme/host/fabrics.c:1284 nvmf_create_ctrl drivers/nvme/host/fabrics.c:1374 [inline]
Freed by task 5534: nvme_fc_ctrl_free drivers/nvme/host/fc.c:2374 [inline]
nvme_fc_init_ctrl+0xe17/0x1450 drivers/nvme/host/fc.c:3605
nvme_add_ctrl() falla cuando dev_set_name() no puede asignar memoria, por lo que esto es alcanzable bajo presión de memoria o inyección de fallos. Sin KASAN las opciones se liberan dos veces.
En lugar de limpiar el puntero también en la segunda salida, derivar la propiedad de la misma manera que nvme-tcp, nvme-rdma y nvme-loop lo hacen, a partir de la pertenencia a una lista: su free_ctrl deja las opciones intactas a menos que el controlador haya sido añadido a la lista del transporte.
La lista no puede simplemente poblarse en la ruta de éxito tal como está actualmente. nvme-fc ejecuta la conexión inicial sincrónicamente mediante flush_delayed_work(), y el controlador debe ser accesible en rport->ctrl_list durante toda su duración: nvme_fc_unregister_remoteport() necesita encontrarlo para señalar la pérdida de conectividad, nvme_fc_match_disconn_ls() hace coincidir un Disconnect Association LS entrante con ctrl->association_id, que solo se asigna durante esa ventana temporal, nvme_fc_resume_controller() lo necesita en el re-registro del remoteport y nvme_fc_existing_controller() lo usa para rechazar una conexión duplicada que compite con la ya en curso.
Mantener la inserción donde está y añadir una etiqueta fail_unlist:, que caiga dentro de fail_ctrl:, para las rutas de error que se ejecutan después de ella. Las rutas de error anteriores nunca alcanzan la inserción y siguen usando fail_ctrl: directamente, por lo que la lista solo se toca cuando el controlador realmente está en ella.
nvme_fc_ctrl_free() no puede usar el simple "goto free_ctrl" que usan los otros transportes, porque aún tiene que ejecutar put_device(), liberar la referencia del rport y liberar la entrada de ida para los recursos adquiridos antes de la inserción. En su lugar, se utiliza una muestra list_empty() bajo rport->lock.
ctrl->ctrl.opts también permanece válido durante toda la fase de desmontaje (teardown). Esto no es el bug que se está corrigiendo, pero elimina cierta fragilidad en torno al antiguo idiom: nvme_free_ctrl() llama a nvme_auth_free() antes de ->free_ctrl(), y ctrl_max_dhchaps() dereferencia ctrl->opts sin una comprobación NULL cuando ctrl->dhchap_ctxs está establecido, lo cual nvme-fc permite ya que NVMF_ALLOWED_OPTS permite las opciones dhchap. Los atributos sysfs del subsistema nvme que dereferecian ctrl->opts, como hostnqn y address, evalúan su prueba is_visible() una sola vez en el momento de device_add() y permanecen legibles hasta cdev_device_del().
Be aware that VulDB is the high quality source for vulnerability data.