Skip to content

Commit 37e2e7c

Browse files
committed
fix: snapshot IsBusy state to prevent TOCTOU panic in partitionHealthy and release callReq+stream on addCall failure
partitionHealthy: IsBusy() reads a live atomic in-flight counter. Calling it twice (counting pass + placement pass) creates a TOCTOU race: if hosts flip from healthy to busy between passes, the placement indices can overflow past the slice bounds causing an index-out-of-range panic. Fix by snapshotting the busy state once per host in pass 1 and reusing it in pass 2, using a small stack buffer for the common case. exec: when addCall() fails, the callReq obtained from the pool and the reserved stream ID were leaked. Fix by clearing the stream and returning the callReq to the pool before returning the error.
1 parent b8edd55 commit 37e2e7c

File tree

2 files changed

+19
-5
lines changed

2 files changed

+19
-5
lines changed

conn.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,6 +1226,8 @@ func (c *Conn) exec(ctx context.Context, req frameBuilder, tracer Tracer, reques
12261226
}
12271227

12281228
if err := c.addCall(call); err != nil {
1229+
c.streams.Clear(stream)
1230+
putCallReq(call)
12291231
return nil, &QueryError{err: err, potentiallyExecuted: false}
12301232
}
12311233

policies.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -769,10 +769,22 @@ func partitionHealthy(replicas []*HostInfo, s *Session) {
769769
return
770770
}
771771

772-
// Two-pass stable partition: first pass counts, second places.
772+
// Snapshot IsBusy state once per host to avoid TOCTOU races between
773+
// the counting pass and the placement pass. IsBusy reads a live atomic
774+
// in-flight counter, so a host can flip between passes. If healthyCount
775+
// becomes stale, the placement indices (hi/ui) can overflow.
776+
var busyBuf [8]bool
777+
var busy []bool
778+
if n <= len(busyBuf) {
779+
busy = busyBuf[:n]
780+
} else {
781+
busy = make([]bool, n)
782+
}
783+
773784
healthyCount := 0
774-
for _, h := range replicas {
775-
if !h.IsBusy(s) {
785+
for i, h := range replicas {
786+
busy[i] = h.IsBusy(s)
787+
if !busy[i] {
776788
healthyCount++
777789
}
778790
}
@@ -792,8 +804,8 @@ func partitionHealthy(replicas []*HostInfo, s *Session) {
792804
copy(tmp, replicas)
793805

794806
hi, ui := 0, healthyCount
795-
for _, h := range tmp {
796-
if !h.IsBusy(s) {
807+
for i, h := range tmp {
808+
if !busy[i] {
797809
replicas[hi] = h
798810
hi++
799811
} else {

0 commit comments

Comments
 (0)