Skip to content

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

@aftersnow

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.

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