fix: prevent nil deref on session reinstatement during cleanup race

When a new login arrives while the previous connection's async cleanup
is still running, LookupISCSISession finds the stale session but
LookupConnection returns nil (old connection already closed). This
caused a nil pointer dereference when accessing existConn.session
during session reinstatement.

Fix by checking existConn != nil before reinstatement. If the old
connection is already gone, unbind the stale session and create
a fresh one instead.

Also add sync.Once to removeConnectionFromSession to prevent
concurrent goroutine and main-path cleanup from racing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lei Xue
2026-03-17 14:37:16 +08:00
parent b7e5c4a7d2
commit 8ffe5ec5ce
2 changed files with 24 additions and 14 deletions

View File

@@ -88,7 +88,8 @@ type iscsiConnection struct {
rxTask *iscsiTask
txTask *iscsiTask
readLock *sync.RWMutex
readLock *sync.RWMutex
cleanupOnce sync.Once
}
type taskState int

View File

@@ -340,21 +340,24 @@ func (s *ISCSITargetDriver) UnBindISCSISession(sess *ISCSISession) {
// removeConnectionFromSession removes a connection from its session.
// If the session has no remaining connections, the session is unbound.
// This is safe to call concurrently; cleanup runs at most once per connection.
func (s *ISCSITargetDriver) removeConnectionFromSession(conn *iscsiConnection) {
sess := conn.session
if sess == nil {
return
}
conn.cleanupOnce.Do(func() {
sess := conn.session
if sess == nil {
return
}
sess.ConnectionsRWMutex.Lock()
delete(sess.Connections, conn.cid)
remaining := len(sess.Connections)
sess.ConnectionsRWMutex.Unlock()
sess.ConnectionsRWMutex.Lock()
delete(sess.Connections, conn.cid)
remaining := len(sess.Connections)
sess.ConnectionsRWMutex.Unlock()
if remaining == 0 {
s.UnBindISCSISession(sess)
}
conn.session = nil
if remaining == 0 {
s.UnBindISCSISession(sess)
}
conn.session = nil
})
}
func (s *ISCSITargetDriver) BindISCSISession(conn *iscsiConnection) error {
@@ -433,7 +436,13 @@ func (s *ISCSITargetDriver) BindISCSISession(conn *iscsiConnection) error {
if conn.loginParam.tsih == ISCSI_UNSPEC_TSIH {
log.Infof("Session Reinstatement initiator name:%v,target name:%v,ISID:0x%x",
conn.loginParam.initiator, conn.loginParam.target, conn.loginParam.isid)
newSess, err = s.ReInstatement(existConn.session, conn)
if existConn != nil {
newSess, err = s.ReInstatement(existConn.session, conn)
} else {
// Old connection already closed; unbind the stale session and create new
s.UnBindISCSISession(existSess)
newSess, err = s.NewISCSISession(conn)
}
if err != nil {
return err
}