Skip to content

Conversation

@0x2b3bfa0
Copy link

@0x2b3bfa0 0x2b3bfa0 commented May 17, 2025

I found a panic in docker compose and asked GitHub Copilot to fix it. I don't have time to make myself responsible for the quality of this pull request, feel free to close it unmerged.

I wonder if the mutex should not be enclosed into the scope where it's being used, but should be passed from the scope that declares the images map:

return s.pullRequiredImages(ctx, project, images, quietPull)

GitHub Copilot Description

Fix concurrent map writes error in pullRequiredImages function in pkg/compose/pull.go.

  • Add a sync.Mutex to protect concurrent access to the map images.
  • Lock the sync.Mutex before writing to the map images.
  • Unlock the sync.Mutex after writing to the map images.

For more details, open the Copilot Workspace session.

Panic Log

fatal error: concurrent map writes

goroutine 86 [running]:
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1.1()
	github.com/docker/compose/v2/pkg/compose/pull.go:328 +0x236
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 84
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc00051aed0?)
	runtime/sema.go:71 +0x25
sync.(*WaitGroup).Wait(0x25c5480?)
	sync/waitgroup.go:118 +0x48
golang.org/x/sync/errgroup.(*Group).Wait(0xc0006a5040)
	golang.org/x/[email protected]/errgroup/errgroup.go:56 +0x25
github.com/docker/compose/v2/pkg/progress.RunWithStatus({0x2ba9388, 0xc0002db7d0}, 0xc00067fdf0, 0xc0003197d0, {0x27c8359, 0x7})
	github.com/docker/compose/v2/pkg/progress/writer.go:97 +0x225
github.com/docker/compose/v2/pkg/progress.Run({0x2ba9388, 0xc0002db7d0}, 0xc0006a5000, 0xc0003197d0)
	github.com/docker/compose/v2/pkg/progress/writer.go:61 +0x85
github.com/docker/compose/v2/pkg/compose.(*composeService).Up(0xc0003d64c0, {0x2ba9388, 0xc0002db7d0}, _, {{0x0, {0xc0003fe8a0, 0x0, 0x1}, 0x0, 0x0, ...}, ...})
	github.com/docker/compose/v2/pkg/compose/up.go:40 +0x213
github.com/docker/compose/v2/cmd/compose.runUp({_, _}, {_, _}, {_, _}, {0x0, 0x1, {0x27c670c, 0x6}, ...}, ...)
	github.com/docker/compose/v2/cmd/compose/up.go:319 +0xb54
github.com/docker/compose/v2/cmd/compose.upCommand.func2({0x2ba9388, 0xc0002db7d0}, 0xc0002c1290, {0xc0003fe8a0, 0x0, 0x1})
	github.com/docker/compose/v2/cmd/compose/up.go:143 +0x29f
github.com/docker/compose/v2/cmd/compose.upCommand.(*ProjectOptions).WithServices.func5({0x2ba93c0, 0xc00031a140}, {0xc0003fe8a0, 0x0, 0x1})
	github.com/docker/compose/v2/cmd/compose/compose.go:187 +0x22d
github.com/docker/compose/v2/cmd/compose.upCommand.(*ProjectOptions).WithServices.Adapt.func7({0x2ba93c0?, 0xc00031a140?}, 0x2?, {0xc0003fe8a0?, 0x2b8dfe8?, 0x4307a57?})
	github.com/docker/compose/v2/cmd/compose/compose.go:137 +0x30
github.com/docker/compose/v2/cmd/compose.upCommand.(*ProjectOptions).WithServices.Adapt.AdaptCmd.func8(0xc000596908, {0xc0003fe8a0, 0x0, 0x1})
	github.com/docker/compose/v2/cmd/compose/compose.go:121 +0x143
github.com/docker/cli/cli-plugins/plugin.RunPlugin.func1.1.2(0xc000596908, {0xc0003fe8a0, 0x0, 0x1})
	github.com/docker/[email protected]+incompatible/cli-plugins/plugin/plugin.go:65 +0x6c
github.com/docker/compose/v2/cmd/cmdtrace.Setup.wrapRunE.func2(0xc000596908?, {0xc0003fe8a0?, 0x0?, 0x1?})
	github.com/docker/compose/v2/cmd/cmdtrace/cmd_span.go:85 +0x63
github.com/spf13/cobra.(*Command).execute(0xc000596908, {0xc0003dcda0, 0x1, 0x1})
	github.com/spf13/[email protected]/command.go:1015 +0xa94
github.com/spf13/cobra.(*Command).ExecuteC(0xc00049ef08)
	github.com/spf13/[email protected]/command.go:1148 +0x40c
github.com/spf13/cobra.(*Command).Execute(...)
	github.com/spf13/[email protected]/command.go:1071
github.com/docker/cli/cli-plugins/plugin.RunPlugin(0xc000242c80, 0xc000596608, {{0x27c5150, 0x5}, {0x27cf09e, 0xb}, {0x2b790c8, 0x7}, {0x0, 0x0}, ...})
	github.com/docker/[email protected]+incompatible/cli-plugins/plugin/plugin.go:80 +0x145
github.com/docker/cli/cli-plugins/plugin.Run(0x29213b0, {{0x27c5150, 0x5}, {0x27cf09e, 0xb}, {0x2b790c8, 0x7}, {0x0, 0x0}, {0x0, ...}})
	github.com/docker/[email protected]+incompatible/cli-plugins/plugin/plugin.go:95 +0x105
main.pluginMain()
	github.com/docker/compose/v2/cmd/main.go:38 +0xa5
main.main()
	github.com/docker/compose/v2/cmd/main.go:98 +0x19c

goroutine 35 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c66b0, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0004d3b00?, 0xc000086fbf?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0004d3b00, {0xc000086fbf, 0x1, 0x1})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0004d3b00, {0xc000086fbf?, 0x0?, 0x0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc000125198, {0xc000086fbf?, 0x0?, 0x0?})
	net/net.go:189 +0x45
github.com/docker/cli/cli-plugins/socket.ConnectAndWait.func1()
	github.com/docker/[email protected]+incompatible/cli-plugins/socket/socket.go:162 +0x45
created by github.com/docker/cli/cli-plugins/socket.ConnectAndWait in goroutine 1
	github.com/docker/[email protected]+incompatible/cli-plugins/socket/socket.go:159 +0x118

goroutine 83 [select]:
github.com/docker/compose/v2/pkg/progress.(*plainWriter).Start(0xc00050a100, {0x2ba8f50, 0x412eac0})
	github.com/docker/compose/v2/pkg/progress/plain.go:34 +0x67
github.com/docker/compose/v2/pkg/progress.RunWithStatus.func1()
	github.com/docker/compose/v2/pkg/progress/writer.go:83 +0x2a
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 14
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 9 [select]:
go.opentelemetry.io/otel/sdk/trace.(*batchSpanProcessor).processQueue(0xc00058a000)
	go.opentelemetry.io/otel/[email protected]/trace/batch_span_processor.go:302 +0x114
go.opentelemetry.io/otel/sdk/trace.NewBatchSpanProcessor.func1()
	go.opentelemetry.io/otel/[email protected]/trace/batch_span_processor.go:117 +0x4e
created by go.opentelemetry.io/otel/sdk/trace.NewBatchSpanProcessor in goroutine 1
	go.opentelemetry.io/otel/[email protected]/trace/batch_span_processor.go:115 +0x2e5

goroutine 49 [syscall]:
os/signal.signal_recv()
	runtime/sigqueue.go:152 +0x29
os/signal.loop()
	os/signal/signal_unix.go:23 +0x13
created by os/signal.Notify.func1.1 in goroutine 1
	os/signal/signal.go:151 +0x1f

goroutine 11 [chan receive]:
github.com/docker/compose/v2/cmd/compose.upCommand.AdaptCmd.func4.1()
	github.com/docker/compose/v2/cmd/compose/compose.go:115 +0x27
created by github.com/docker/compose/v2/cmd/compose.upCommand.AdaptCmd.func4 in goroutine 1
	github.com/docker/compose/v2/cmd/compose/compose.go:114 +0x10a

goroutine 12 [chan receive]:
github.com/docker/compose/v2/cmd/compose.upCommand.(*ProjectOptions).WithServices.Adapt.AdaptCmd.func8.1()
	github.com/docker/compose/v2/cmd/compose/compose.go:115 +0x27
created by github.com/docker/compose/v2/cmd/compose.upCommand.(*ProjectOptions).WithServices.Adapt.AdaptCmd.func8 in goroutine 1
	github.com/docker/compose/v2/cmd/compose/compose.go:114 +0x10a

goroutine 13 [select]:
github.com/docker/compose/v2/pkg/progress.(*plainWriter).Start(0xc00050afa0, {0x2ba8f50, 0x412eac0})
	github.com/docker/compose/v2/pkg/progress/plain.go:34 +0x67
github.com/docker/compose/v2/pkg/progress.RunWithStatus.func1()
	github.com/docker/compose/v2/pkg/progress/writer.go:83 +0x2a
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 1
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 14 [semacquire]:
sync.runtime_Semacquire(0xc00051a138?)
	runtime/sema.go:71 +0x25
sync.(*WaitGroup).Wait(0x25c5480?)
	sync/waitgroup.go:118 +0x48
golang.org/x/sync/errgroup.(*Group).Wait(0xc0006a45c0)
	golang.org/x/[email protected]/errgroup/errgroup.go:56 +0x25
github.com/docker/compose/v2/pkg/progress.RunWithStatus({0x2ba9388, 0xc000705950}, 0xc0006a3800, 0xc0003197d0, {0x27c8359, 0x7})
	github.com/docker/compose/v2/pkg/progress/writer.go:97 +0x225
github.com/docker/compose/v2/pkg/progress.Run({0x2ba9388, 0xc000705950}, 0xc0007059b0, 0xc0003197d0)
	github.com/docker/compose/v2/pkg/progress/writer.go:61 +0x85
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages(0xc0003d64c0, {0x2ba9388, 0xc000705950}, 0xc0002c1290, 0xc00068ee70, 0x0)
	github.com/docker/compose/v2/pkg/compose/pull.go:320 +0x2ef
github.com/docker/compose/v2/pkg/compose.(*composeService).ensureImagesExists.func1({0x2ba9388?, 0xc000705950?})
	github.com/docker/compose/v2/pkg/compose/build.go:278 +0x37
github.com/docker/compose/v2/pkg/compose.(*composeService).ensureImagesExists.SpanWrapFunc.func3({0x2ba9388, 0xc00068ee40})
	github.com/docker/compose/v2/internal/tracing/wrap.go:43 +0x13d
github.com/docker/compose/v2/pkg/compose.(*composeService).ensureImagesExists(0xc0003d64c0, {0x2ba9388, 0xc00068ee40}, 0xc0002c1290, 0x0, 0x0)
	github.com/docker/compose/v2/pkg/compose/build.go:280 +0x29e
github.com/docker/compose/v2/pkg/compose.(*composeService).create(0xc0003d64c0, {0x2ba9388, 0xc00068ee40}, 0xc0002c1290, {0x0, {0xc000508400, 0xe, 0x10}, 0x0, 0x0, ...})
	github.com/docker/compose/v2/pkg/compose/create.go:83 +0xdf
github.com/docker/compose/v2/pkg/compose.(*composeService).Up.func1({0x2ba9388, 0xc00068ee40})
	github.com/docker/compose/v2/pkg/compose/up.go:41 +0x85
github.com/docker/compose/v2/pkg/compose.(*composeService).Up.SpanWrapFunc.func5({0x2ba9388, 0xc00068ee10})
	github.com/docker/compose/v2/internal/tracing/wrap.go:43 +0x13d
github.com/docker/compose/v2/pkg/progress.Run.func1({0x2ba9388?, 0xc00068ee10?})
	github.com/docker/compose/v2/pkg/progress/writer.go:62 +0x22
github.com/docker/compose/v2/pkg/progress.RunWithStatus.func2()
	github.com/docker/compose/v2/pkg/progress/writer.go:90 +0x70
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 1
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 23 [select]:
net/http.(*persistConn).readLoop(0xc0007426c0)
	net/http/transport.go:2325 +0xca5
created by net/http.(*Transport).dialConn in goroutine 22
	net/http/transport.go:1874 +0x154f

goroutine 79 [select]:
net/http.(*persistConn).readLoop(0xc0007987e0)
	net/http/transport.go:2325 +0xca5
created by net/http.(*Transport).dialConn in goroutine 78
	net/http/transport.go:1874 +0x154f

goroutine 54 [select]:
net/http.(*persistConn).readLoop(0xc00023b9e0)
	net/http/transport.go:2325 +0xca5
created by net/http.(*Transport).dialConn in goroutine 53
	net/http/transport.go:1874 +0x154f

goroutine 80 [select]:
net/http.(*persistConn).writeLoop(0xc0007987e0)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 78
	net/http/transport.go:1875 +0x15a5

goroutine 20 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c6250, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0001a2000?, 0xc000836000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0001a2000, {0xc000836000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0001a2000, {0xc000836000?, 0x0?, 0x2b805a0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc0004b8010, {0xc000836000?, 0x0?, 0x0?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc00082e000, {0xc000836000?, 0x777f25?, 0x2379d40?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc00053a060)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).Peek(0xc00053a060, 0x1)
	bufio/bufio.go:148 +0x53
net/http.(*persistConn).readLoop(0xc00082e000)
	net/http/transport.go:2205 +0x185
created by net/http.(*Transport).dialConn in goroutine 19
	net/http/transport.go:1874 +0x154f

goroutine 43 [select]:
net/http.(*persistConn).readLoop(0xc000742120)
	net/http/transport.go:2325 +0xca5
created by net/http.(*Transport).dialConn in goroutine 42
	net/http/transport.go:1874 +0x154f

goroutine 44 [select]:
net/http.(*persistConn).writeLoop(0xc000742120)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 42
	net/http/transport.go:1875 +0x15a5

goroutine 55 [select]:
net/http.(*persistConn).writeLoop(0xc00023b9e0)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 53
	net/http/transport.go:1875 +0x15a5

goroutine 24 [select]:
net/http.(*persistConn).writeLoop(0xc0007426c0)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 22
	net/http/transport.go:1875 +0x15a5

goroutine 21 [select]:
net/http.(*persistConn).writeLoop(0xc00082e000)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 19
	net/http/transport.go:1875 +0x15a5

goroutine 84 [semacquire]:
sync.runtime_Semacquire(0x0?)
	runtime/sema.go:71 +0x25
sync.(*WaitGroup).Wait(0xc000705980?)
	sync/waitgroup.go:118 +0x48
golang.org/x/sync/errgroup.(*Group).Wait(0xc0006a4700)
	golang.org/x/[email protected]/errgroup/errgroup.go:56 +0x25
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1({0x2ba9388, 0xc0007059e0})
	github.com/docker/compose/v2/pkg/compose/pull.go:340 +0x39b
github.com/docker/compose/v2/pkg/progress.Run.func1({0x2ba9388?, 0xc0007059e0?})
	github.com/docker/compose/v2/pkg/progress/writer.go:62 +0x22
github.com/docker/compose/v2/pkg/progress.RunWithStatus.func2()
	github.com/docker/compose/v2/pkg/progress/writer.go:90 +0x70
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 14
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 87 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c6020, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0002ba200?, 0xc00074f000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0002ba200, {0xc00074f000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0002ba200, {0xc00074f000?, 0x0?, 0x0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc000530088, {0xc00074f000?, 0x0?, 0xc00089ea80?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc000742120, {0xc00074f000?, 0x0?, 0x60?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc00021d2c0)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).ReadSlice(0xc00021d2c0, 0xa)
	bufio/bufio.go:376 +0x29
net/http/internal.readChunkLine(0x0?)
	net/http/internal/chunked.go:156 +0x1c
net/http/internal.(*chunkedReader).beginChunk(0xc00083c210)
	net/http/internal/chunked.go:49 +0x25
net/http/internal.(*chunkedReader).Read(0xc00083c210, {0xc0001c9202?, 0x0?, 0xc0008f0650?})
	net/http/internal/chunked.go:125 +0x131
net/http.(*body).readLocked(0xc0006a4a80, {0xc0001c9202?, 0xc00083f450?, 0xc0002b0080?})
	net/http/transfer.go:844 +0x3b
net/http.(*body).Read(0x4?, {0xc0001c9202?, 0x223ed20?, 0x4132600?})
	net/http/transfer.go:836 +0x112
net/http.(*bodyEOFSignal).Read(0xc0006a4ac0, {0xc0001c9202, 0x5fe, 0x5fe})
	net/http/transport.go:2913 +0x13f
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.(*wrappedBody).Read(0xc0006a4b80, {0xc0001c9202?, 0x0?, 0x0?})
	go.opentelemetry.io/contrib/instrumentation/net/http/[email protected]/transport.go:229 +0x2d
encoding/json.(*Decoder).refill(0xc000388b40)
	encoding/json/stream.go:165 +0x188
encoding/json.(*Decoder).readValue(0xc000388b40)
	encoding/json/stream.go:140 +0x85
encoding/json.(*Decoder).Decode(0xc000388b40, {0x235b6a0, 0xc00081b050})
	encoding/json/stream.go:63 +0x75
github.com/docker/compose/v2/pkg/compose.(*composeService).pullServiceImage(_, {_, _}, {{0xc0004a64f8, 0x5}, {0x0, 0x0, 0x0}, 0x0, 0x0, ...}, ...)
	github.com/docker/compose/v2/pkg/compose/pull.go:231 +0x4b0
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1.1()
	github.com/docker/compose/v2/pkg/compose/pull.go:327 +0x170
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 84
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 88 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c5df0, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0003b2a80?, 0xc000920000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0003b2a80, {0xc000920000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0003b2a80, {0xc000920000?, 0x0?, 0x0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc0004b8160, {0xc000920000?, 0x0?, 0xc00089ec40?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc00082efc0, {0xc000920000?, 0x0?, 0x17?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc000676660)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).ReadSlice(0xc000676660, 0xa)
	bufio/bufio.go:376 +0x29
net/http/internal.readChunkLine(0x0?)
	net/http/internal/chunked.go:156 +0x1c
net/http/internal.(*chunkedReader).beginChunk(0xc00083c450)
	net/http/internal/chunked.go:49 +0x25
net/http/internal.(*chunkedReader).Read(0xc00083c450, {0xc000922c02?, 0x0?, 0xc00094e650?})
	net/http/internal/chunked.go:125 +0x131
net/http.(*body).readLocked(0xc0006a5380, {0xc000922c02?, 0xc00087c680?, 0xc00081c3c0?})
	net/http/transfer.go:844 +0x3b
net/http.(*body).Read(0x4?, {0xc000922c02?, 0x223ed20?, 0x4132600?})
	net/http/transfer.go:836 +0x112
net/http.(*bodyEOFSignal).Read(0xc0006a53c0, {0xc000922c02, 0x5fe, 0x5fe})
	net/http/transport.go:2913 +0x13f
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.(*wrappedBody).Read(0xc0006a5b40, {0xc000922c02?, 0x0?, 0x0?})
	go.opentelemetry.io/contrib/instrumentation/net/http/[email protected]/transport.go:229 +0x2d
encoding/json.(*Decoder).refill(0xc000389540)
	encoding/json/stream.go:165 +0x188
encoding/json.(*Decoder).readValue(0xc000389540)
	encoding/json/stream.go:140 +0x85
encoding/json.(*Decoder).Decode(0xc000389540, {0x235b6a0, 0xc00031f170})
	encoding/json/stream.go:63 +0x75
github.com/docker/compose/v2/pkg/compose.(*composeService).pullServiceImage(_, {_, _}, {{0xc000539fe0, 0x8}, {0x0, 0x0, 0x0}, 0x0, 0x0, ...}, ...)
	github.com/docker/compose/v2/pkg/compose/pull.go:231 +0x4b0
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1.1()
	github.com/docker/compose/v2/pkg/compose/pull.go:327 +0x170
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 84
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 89 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c5f08, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0002ba100?, 0xc00076f000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0002ba100, {0xc00076f000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0002ba100, {0xc00076f000?, 0x0?, 0x0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc000124058, {0xc00076f000?, 0x0?, 0xc00089ee00?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc0007987e0, {0xc00076f000?, 0x0?, 0x21?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc00053a480)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).ReadSlice(0xc00053a480, 0xa)
	bufio/bufio.go:376 +0x29
net/http/internal.readChunkLine(0x0?)
	net/http/internal/chunked.go:156 +0x1c
net/http/internal.(*chunkedReader).beginChunk(0xc00083c3c0)
	net/http/internal/chunked.go:49 +0x25
net/http/internal.(*chunkedReader).Read(0xc00083c3c0, {0xc000922602?, 0x0?, 0xc000480650?})
	net/http/internal/chunked.go:125 +0x131
net/http.(*body).readLocked(0xc0006a5240, {0xc000922602?, 0xc00083f450?, 0xc0002b0080?})
	net/http/transfer.go:844 +0x3b
net/http.(*body).Read(0x4?, {0xc000922602?, 0x223ed20?, 0x4132600?})
	net/http/transfer.go:836 +0x112
net/http.(*bodyEOFSignal).Read(0xc0006a5280, {0xc000922602, 0x5fe, 0x5fe})
	net/http/transport.go:2913 +0x13f
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.(*wrappedBody).Read(0xc0006a5340, {0xc000922602?, 0x0?, 0x0?})
	go.opentelemetry.io/contrib/instrumentation/net/http/[email protected]/transport.go:229 +0x2d
encoding/json.(*Decoder).refill(0xc000389400)
	encoding/json/stream.go:165 +0x188
encoding/json.(*Decoder).readValue(0xc000389400)
	encoding/json/stream.go:140 +0x85
encoding/json.(*Decoder).Decode(0xc000389400, {0x235b6a0, 0xc00081b170})
	encoding/json/stream.go:63 +0x75
github.com/docker/compose/v2/pkg/compose.(*composeService).pullServiceImage(_, {_, _}, {{0xc0004a6180, 0xa}, {0x0, 0x0, 0x0}, 0x0, 0x0, ...}, ...)
	github.com/docker/compose/v2/pkg/compose/pull.go:231 +0x4b0
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1.1()
	github.com/docker/compose/v2/pkg/compose/pull.go:327 +0x170
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 84
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 90 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c6598, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0003fc080?, 0xc000142000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0003fc080, {0xc000142000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0003fc080, {0xc000142000?, 0x0?, 0x0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc000530048, {0xc000142000?, 0x0?, 0xc00089efc0?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc0007426c0, {0xc000142000?, 0x0?, 0x17?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc00021daa0)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).ReadSlice(0xc00021daa0, 0xa)
	bufio/bufio.go:376 +0x29
net/http/internal.readChunkLine(0x0?)
	net/http/internal/chunked.go:156 +0x1c
net/http/internal.(*chunkedReader).beginChunk(0xc00083c4e0)
	net/http/internal/chunked.go:49 +0x25
net/http/internal.(*chunkedReader).Read(0xc00083c4e0, {0xc000923202?, 0x0?, 0xc00080a650?})
	net/http/internal/chunked.go:125 +0x131
net/http.(*body).readLocked(0xc0006a5b80, {0xc000923202?, 0xc00087c680?, 0xc00081c3c0?})
	net/http/transfer.go:844 +0x3b
net/http.(*body).Read(0x4?, {0xc000923202?, 0x223ed20?, 0x4132600?})
	net/http/transfer.go:836 +0x112
net/http.(*bodyEOFSignal).Read(0xc0006a5bc0, {0xc000923202, 0x5fe, 0x5fe})
	net/http/transport.go:2913 +0x13f
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.(*wrappedBody).Read(0xc0006a5c80, {0xc000923202?, 0x0?, 0x0?})
	go.opentelemetry.io/contrib/instrumentation/net/http/[email protected]/transport.go:229 +0x2d
encoding/json.(*Decoder).refill(0xc000389680)
	encoding/json/stream.go:165 +0x188
encoding/json.(*Decoder).readValue(0xc000389680)
	encoding/json/stream.go:140 +0x85
encoding/json.(*Decoder).Decode(0xc000389680, {0x235b6a0, 0xc00031e870})
	encoding/json/stream.go:63 +0x75
github.com/docker/compose/v2/pkg/compose.(*composeService).pullServiceImage(_, {_, _}, {{0xc0004a62d0, 0x5}, {0x0, 0x0, 0x0}, 0x0, 0x0, ...}, ...)
	github.com/docker/compose/v2/pkg/compose/pull.go:231 +0x4b0
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1.1()
	github.com/docker/compose/v2/pkg/compose/pull.go:327 +0x170
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 84
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 91 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c6138, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0001a2100?, 0xc0002f4000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0001a2100, {0xc0002f4000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0001a2100, {0xc0002f4000?, 0x0?, 0x0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc00008e308, {0xc0002f4000?, 0x0?, 0xc00089f180?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc00023b9e0, {0xc0002f4000?, 0x0?, 0x17?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc000119320)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).ReadSlice(0xc000119320, 0xa)
	bufio/bufio.go:376 +0x29
net/http/internal.readChunkLine(0x0?)
	net/http/internal/chunked.go:156 +0x1c
net/http/internal.(*chunkedReader).beginChunk(0xc00083c570)
	net/http/internal/chunked.go:49 +0x25
net/http/internal.(*chunkedReader).Read(0xc00083c570, {0xc000923802?, 0x0?, 0xc00047c650?})
	net/http/internal/chunked.go:125 +0x131
net/http.(*body).readLocked(0xc0006a5cc0, {0xc000923802?, 0xc00087c680?, 0xc00081c3c0?})
	net/http/transfer.go:844 +0x3b
net/http.(*body).Read(0x4?, {0xc000923802?, 0x223ed20?, 0x4132600?})
	net/http/transfer.go:836 +0x112
net/http.(*bodyEOFSignal).Read(0xc0006a5d00, {0xc000923802, 0x5fe, 0x5fe})
	net/http/transport.go:2913 +0x13f
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.(*wrappedBody).Read(0xc0006a5dc0, {0xc000923802?, 0x0?, 0x0?})
	go.opentelemetry.io/contrib/instrumentation/net/http/[email protected]/transport.go:229 +0x2d
encoding/json.(*Decoder).refill(0xc0003897c0)
	encoding/json/stream.go:165 +0x188
encoding/json.(*Decoder).readValue(0xc0003897c0)
	encoding/json/stream.go:140 +0x85
encoding/json.(*Decoder).Decode(0xc0003897c0, {0x235b6a0, 0xc00031fa70})
	encoding/json/stream.go:63 +0x75
github.com/docker/compose/v2/pkg/compose.(*composeService).pullServiceImage(_, {_, _}, {{0xc0004a6f80, 0x10}, {0x0, 0x0, 0x0}, 0x0, 0x0, ...}, ...)
	github.com/docker/compose/v2/pkg/compose/pull.go:231 +0x4b0
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1.1()
	github.com/docker/compose/v2/pkg/compose/pull.go:327 +0x170
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 84
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 92 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c5cd8, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0002ba480?, 0xc0008f2000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0002ba480, {0xc0008f2000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0002ba480, {0xc0008f2000?, 0x0?, 0x0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc0001240b8, {0xc0008f2000?, 0x0?, 0xc00089f340?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc000798900, {0xc0008f2000?, 0x0?, 0x17?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc00053a6c0)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).ReadSlice(0xc00053a6c0, 0xa)
	bufio/bufio.go:376 +0x29
net/http/internal.readChunkLine(0x0?)
	net/http/internal/chunked.go:156 +0x1c
net/http/internal.(*chunkedReader).beginChunk(0xc00083c330)
	net/http/internal/chunked.go:49 +0x25
net/http/internal.(*chunkedReader).Read(0xc00083c330, {0xc000922002?, 0x0?, 0xc00094a650?})
	net/http/internal/chunked.go:125 +0x131
net/http.(*body).readLocked(0xc0006a50c0, {0xc000922002?, 0xc00087c680?, 0xc00081c3c0?})
	net/http/transfer.go:844 +0x3b
net/http.(*body).Read(0x4?, {0xc000922002?, 0x223ed20?, 0x4132600?})
	net/http/transfer.go:836 +0x112
net/http.(*bodyEOFSignal).Read(0xc0006a5140, {0xc000922002, 0x5fe, 0x5fe})
	net/http/transport.go:2913 +0x13f
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp.(*wrappedBody).Read(0xc0006a5200, {0xc000922002?, 0x0?, 0x0?})
	go.opentelemetry.io/contrib/instrumentation/net/http/[email protected]/transport.go:229 +0x2d
encoding/json.(*Decoder).refill(0xc0003892c0)
	encoding/json/stream.go:165 +0x188
encoding/json.(*Decoder).readValue(0xc0003892c0)
	encoding/json/stream.go:140 +0x85
encoding/json.(*Decoder).Decode(0xc0003892c0, {0x235b6a0, 0xc00031e7e0})
	encoding/json/stream.go:63 +0x75
github.com/docker/compose/v2/pkg/compose.(*composeService).pullServiceImage(_, {_, _}, {{0xc0004a6919, 0x5}, {0x0, 0x0, 0x0}, 0x0, 0x0, ...}, ...)
	github.com/docker/compose/v2/pkg/compose/pull.go:231 +0x4b0
github.com/docker/compose/v2/pkg/compose.(*composeService).pullRequiredImages.func1.1()
	github.com/docker/compose/v2/pkg/compose/pull.go:327 +0x170
golang.org/x/sync/errgroup.(*Group).Go.func1()
	golang.org/x/[email protected]/errgroup/errgroup.go:79 +0x50
created by golang.org/x/sync/errgroup.(*Group).Go in goroutine 84
	golang.org/x/[email protected]/errgroup/errgroup.go:76 +0x96

goroutine 94 [select]:
net/http.(*persistConn).readLoop(0xc00082efc0)
	net/http/transport.go:2325 +0xca5
created by net/http.(*Transport).dialConn in goroutine 93
	net/http/transport.go:1874 +0x154f

goroutine 95 [select]:
net/http.(*persistConn).writeLoop(0xc00082efc0)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 93
	net/http/transport.go:1875 +0x15a5

goroutine 98 [select]:
net/http.(*persistConn).readLoop(0xc000798900)
	net/http/transport.go:2325 +0xca5
created by net/http.(*Transport).dialConn in goroutine 97
	net/http/transport.go:1874 +0x154f

goroutine 99 [select]:
net/http.(*persistConn).writeLoop(0xc000798900)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 97
	net/http/transport.go:1875 +0x15a5

goroutine 101 [IO wait]:
internal/poll.runtime_pollWait(0x7f1ae48c6368, 0x72)
	runtime/netpoll.go:351 +0x85
internal/poll.(*pollDesc).wait(0xc0002ba700?, 0xc0008fe000?, 0x0)
	internal/poll/fd_poll_runtime.go:84 +0x27
internal/poll.(*pollDesc).waitRead(...)
	internal/poll/fd_poll_runtime.go:89
internal/poll.(*FD).Read(0xc0002ba700, {0xc0008fe000, 0x1000, 0x1000})
	internal/poll/fd_unix.go:165 +0x27a
net.(*netFD).Read(0xc0002ba700, {0xc0008fe000?, 0x0?, 0x2b805a0?})
	net/fd_posix.go:55 +0x25
net.(*conn).Read(0xc000124118, {0xc0008fe000?, 0x0?, 0x0?})
	net/net.go:189 +0x45
net/http.(*persistConn).Read(0xc000798a20, {0xc0008fe000?, 0x777f25?, 0x2379d40?})
	net/http/transport.go:2052 +0x4a
bufio.(*Reader).fill(0xc00053a900)
	bufio/bufio.go:110 +0x103
bufio.(*Reader).Peek(0xc00053a900, 0x1)
	bufio/bufio.go:148 +0x53
net/http.(*persistConn).readLoop(0xc000798a20)
	net/http/transport.go:2205 +0x185
created by net/http.(*Transport).dialConn in goroutine 100
	net/http/transport.go:1874 +0x154f

goroutine 102 [select]:
net/http.(*persistConn).writeLoop(0xc000798a20)
	net/http/transport.go:2519 +0xe7
created by net/http.(*Transport).dialConn in goroutine 100
	net/http/transport.go:1875 +0x15a5

Fix concurrent map writes error in `pullRequiredImages` function in `pkg/compose/pull.go`.

* Add a `sync.Mutex` to protect concurrent access to the map `images`.
* Lock the `sync.Mutex` before writing to the map `images`.
* Unlock the `sync.Mutex` after writing to the map `images`.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/docker/compose?shareId=XXXX-XXXX-XXXX-XXXX).
@0x2b3bfa0 0x2b3bfa0 requested a review from a team as a code owner May 17, 2025 13:21
@0x2b3bfa0 0x2b3bfa0 requested review from glours and ndeloof May 17, 2025 13:21
0x2b3bfa0 added 3 commits May 17, 2025 15:22
Signed-off-by: Helio Machado <[email protected]>
…es` function

* Add `imagesMutex` to protect concurrent access to the `images` map
* Add `pulledImagesMutex` to protect concurrent access to the `pulledImages` map
* Lock and unlock `pulledImagesMutex` before and after writing to the `pulledImages` map
* Lock and unlock `imagesMutex` before and after reading from the `images` map
Signed-off-by: Helio Machado <[email protected]>
Copy link
Contributor

@ndeloof ndeloof left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't seem relevant to me
concurrent writes to pulledImages map are already guarded by a mutex
later access takes place after eg.Wait() so doesn't required synchronization

@ndeloof
Copy link
Contributor

ndeloof commented May 17, 2025

already fixed by #12752

@ndeloof ndeloof closed this May 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants