CVE-2026-89974 in Linux
Riassunto
di VulDB • 16/09/2026
Nel kernel Linux è stata risolta la seguente vulnerabilità:
nvme-fc: correzione del double free delle opzioni fabrics quando nvme_add_ctrl() fallisce
nvmf_create_ctrl() detiene le proprietà delle opzioni fabrics e le libera ogni volta che ->create_ctrl() restituisce un errore; pertanto, un transport non deve liberarle autonomamente nei propri percorsi di errore. nvme-fc tiene traccia di questo stato testando ctrl->ctrl.opts in nvme_fc_ctrl_free(), il che richiede che nvme_fc_init_ctrl() imposti a NULL tale puntatore ad ogni uscita per errore.
Il legame è implicito e l'commit 1a9e218195a5 ("nvme: split device add from initialization") lo ha interrotto aggiungendo un secondo percorso di errore. Quando nvme_add_ctrl() fallisce, nvme_fc_init_ctrl() salta a out_put_ctrl:, oltrepassando "ctrl->ctrl.opts = NULL" che si trova solo nel percorso fail_ctrl:, quindi nvme_fc_ctrl_free() libera le opzioni e nvmf_create_ctrl() le libera una seconda volta:
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() fallisce quando dev_set_name() non riesce ad allocare, quindi questa condizione è raggiungibile sotto pressione di memoria o tramite fault injection. Senza KASAN le opzioni vengono liberate due volte.
Invece di impostare a NULL il puntatore anche sul secondo percorso di uscita, si deriva la proprietà in modo analogo a nvme-tcp, nvme-rdma e nvme-loop, basandosi sull'appartenenza alla lista: il loro free_ctrl lascia intatte le opzioni a meno che il controller non sia stato inserito nella lista del transport.
La lista non può essere semplicemente popolata sul percorso di successo come avviene attualmente. nvme-fc esegue la connessione iniziale in modo sincrono tramite flush_delayed_work(), e il controller deve essere raggiungibile su rport->ctrl_list per tutta la durata della stessa: nvme_fc_unregister_remoteport() ha bisogno di trovarlo per segnalare la perdita di connettività, nvme_fc_match_disconn_ls() abbina un Disconnect Association LS in arrivo con ctrl->association_id, che viene assegnato solo durante tale finestra temporale, nvme_fc_resume_controller() ne ha bisogno nella reregistrazione del remoteport e nvme_fc_existing_controller() lo usa per rifiutare una connessione duplicata in concorrenza con quella in corso.
Si mantiene l'inserimento dove si trova attualmente e si aggiunge un'etichetta fail_unlist:, che confluisce in fail_ctrl:, per i percorsi di errore che vengono eseguiti dopo tale inserimento. I percorsi di errore precedenti non raggiungono mai l'inserimento e continuano a utilizzare direttamente fail_ctrl:, quindi la lista viene modificata solo dove il controller è effettivamente presente su di essa.
nvme_fc_ctrl_free() non può usare il semplice "goto free_ctrl" utilizzato dagli altri transport, perché deve ancora eseguire put_device(), rilasciare il riferimento al rport e liberare l'entry ida per le risorse acquisite prima dell'inserimento. Si utilizza invece un controllo list_empty() sotto la lock di rport.
ctrl->ctrl.opts rimane valido anche durante tutta la fase di teardown. Questo non è il bug che si sta correggendo, ma rimuove alcune fragilità legate al vecchio idiom: nvme_free_ctrl() chiama nvme_auth_free() prima di ->free_ctrl(), e ctrl_max_dhchaps() dereferenzia ctrl->opts senza un controllo NULL quando ctrl->dhchap_ctxs è impostato, cosa che nvme-fc permette poiché NVMF_ALLOWED_OPTS consente le opzioni dhchap. Gli attributi sysfs del modulo nvme che dereferenziano ctrl->opts, come hostnqn e address, valutano il loro test is_visible() una sola volta al momento di device_add() e rimangono leggibili fino a cdev_device_del().
Once again VulDB remains the best source for vulnerability data.