[BUG] scsi: target: iscsi: connection-recovery (cr) double-free / list corruption in ERL=2

From: Yu Zhang

Date: Fri Oct 16 2026 - 02:55:37 EST


struct iscsi_conn_recovery has no refcount; its lifetime is governed only by
sess->cr_i_lock / cr_a_lock, which protect list membership, not the object once
a pointer escapes the lock.

iscsit_get_inactive_connection_recovery_entry() looks up a cr by CID and returns
it after dropping cr_i_lock, with no reference taken:

spin_lock(&sess->cr_i_lock);
list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
if (cr->cid == cid) {
spin_unlock(&sess->cr_i_lock);
return cr;
}
}

The caller then runs iscsit_discard_cr_cmds_by_expstatsn(cr, ...) with no lock
spanning lookup -> use; when cr->cmd_count reaches 0 it frees the cr:

iscsit_remove_inactive_connection_recovery_entry(cr, sess);
iscsit_attach_active_connection_recovery_entry(sess, cr);
iscsit_remove_active_connection_recovery_entry(cr, sess); /* kfree(cr) */

Two contexts run this get-then-discard on the same session with no
serialization between them: the REMOVECONNFORRECOVERY logout handler
(iscsi_target.c, per-connection tx thread) and the ERL=2 implicit logout on
login path (iscsi_target_login.c). If both obtain the same cr (both find it on
cr_inactive_list before either removes it) and both reach the cmd_count == 0
branch, the cr is unlinked and kfree()d twice -> double-free, with a list_del on
an already-freed entry in between -> list corruption.

Reported-by: Yu Zhang <yuz08559@xxxxxxxxx>