Skip to content

bug: splitReader goroutine can block indefinitely if pipe reader is abandoned #492

Description

@aftersnow

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions