-
Notifications
You must be signed in to change notification settings - Fork 27
bug: splitReader goroutine can block indefinitely if pipe reader is abandoned #492
Copy link
Copy link
Open
Labels
bugSomething isn't workingSomething isn't working
Description
Description
In pkg/backend/build/builder.go:413-430, splitReader() spawns a goroutine that copies from the original reader to a MultiWriter backed by two PipeWriters. If either pipe reader is not fully consumed (e.g., the interceptor fails or abandons the stream early), the goroutine blocks indefinitely on the write operation.
Code
go func() {
defer w1.Close()
defer w2.Close()
_, err := io.Copy(multiWriter, original) // blocks if either reader abandoned
if err \!= nil {
w1.CloseWithError(err)
w2.CloseWithError(err)
}
}()Impact
- Goroutine leak: blocked goroutine is never reclaimed
- The second reader (
itReader) is passed to an interceptor that may fail - If the interceptor abandons the reader, the goroutine hangs, also blocking the first reader
Fix suggestion
Add context-based cancellation so the goroutine can exit when a reader is abandoned:
go func() {
defer w1.Close()
defer w2.Close()
buf := make([]byte, 32*1024)
for {
select {
case <-ctx.Done():
w1.CloseWithError(ctx.Err())
w2.CloseWithError(ctx.Err())
return
default:
}
n, err := original.Read(buf)
if n > 0 {
if _, wErr := multiWriter.Write(buf[:n]); wErr \!= nil {
w1.CloseWithError(wErr)
w2.CloseWithError(wErr)
return
}
}
if err \!= nil {
if err \!= io.EOF {
w1.CloseWithError(err)
w2.CloseWithError(err)
}
return
}
}
}()Severity
Major — goroutine leak, potential deadlock in build pipeline.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
bugSomething isn't workingSomething isn't working