Skip to content

Commit

Permalink
[release-branch.go1.23] database/sql: fix panic with concurrent Conn …
Browse files Browse the repository at this point in the history
…and Close

The current implementation has a panic when the database is closed
concurrently with a new connection attempt.

connRequestSet.CloseAndRemoveAll sets connRequestSet.s to a nil slice.
If this happens between calls to connRequestSet.Add and
connRequestSet.Delete, there is a panic when trying to write to the nil
slice. This is sequence is likely to occur in DB.conn, where the mutex
is released between calls to db.connRequests.Add and
db.connRequests.Delete

This change updates connRequestSet.CloseAndRemoveAll to set the curIdx
to -1 for all pending requests before setting its internal slice to nil.
CloseAndRemoveAll already iterates the full slice to close all the request
channels. It seems appropriate to set curIdx to -1 before deleting the
slice for 3 reasons:
1. connRequestSet.deleteIndex also sets curIdx to -1
2. curIdx will not be relevant to anything after the slice is set to nil
3. connRequestSet.Delete already checks for negative indices

For #68949
Fixes #69041

Change-Id: I6b7ebc5a71b67322908271d13865fa12f2469b87
GitHub-Last-Rev: 7d26691
GitHub-Pull-Request: #68953
Reviewed-on: https://go-review.googlesource.com/c/go/+/607238
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Auto-Submit: Ian Lance Taylor <iant@golang.org>
Commit-Queue: Ian Lance Taylor <iant@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
(cherry picked from commit 08707d6)
Reviewed-on: https://go-review.googlesource.com/c/go/+/609255
  • Loading branch information
nklaassen authored and prattmic committed Aug 28, 2024
1 parent 9625a7f commit 6de5a71
Show file tree
Hide file tree
Showing 2 changed files with 14 additions and 2 deletions.
5 changes: 3 additions & 2 deletions src/database/sql/sql.go
Original file line number Diff line number Diff line change
Expand Up @@ -1368,8 +1368,8 @@ func (db *DB) conn(ctx context.Context, strategy connReuseStrategy) (*driverConn

db.waitDuration.Add(int64(time.Since(waitStart)))

// If we failed to delete it, that means something else
// grabbed it and is about to send on it.
// If we failed to delete it, that means either the DB was closed or
// something else grabbed it and is about to send on it.
if !deleted {
// TODO(bradfitz): rather than this best effort select, we
// should probably start a goroutine to read from req. This best
Expand Down Expand Up @@ -3594,6 +3594,7 @@ type connRequestAndIndex struct {
// and clears the set.
func (s *connRequestSet) CloseAndRemoveAll() {
for _, v := range s.s {
*v.curIdx = -1
close(v.req)
}
s.s = nil
Expand Down
11 changes: 11 additions & 0 deletions src/database/sql/sql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4920,6 +4920,17 @@ func TestConnRequestSet(t *testing.T) {
t.Error("wasn't random")
}
})
t.Run("close-delete", func(t *testing.T) {
reset()
ch := make(chan connRequest)
dh := s.Add(ch)
wantLen(1)
s.CloseAndRemoveAll()
wantLen(0)
if s.Delete(dh) {
t.Error("unexpected delete after CloseAndRemoveAll")
}
})
}

func BenchmarkConnRequestSet(b *testing.B) {
Expand Down

0 comments on commit 6de5a71

Please sign in to comment.