Skip to content

fix: clone shared maps to prevent concurrent map read/write panics#6030

Open
jun-mercury wants to merge 1 commit intonektos:masterfrom
jun-mercury:fix/concurrent-map-writes
Open

fix: clone shared maps to prevent concurrent map read/write panics#6030
jun-mercury wants to merge 1 commit intonektos:masterfrom
jun-mercury:fix/concurrent-map-writes

Conversation

@jun-mercury
Copy link
Copy Markdown

Summary

Reusable workflows evaluate expressions in parallel goroutines that share mutable map references for secrets, vars, and job outputs. When one goroutine interpolates secrets in-place (line 584: secrets[k] = rc.caller.runContext.ExprEval.Interpolate(ctx, v)) while another reads the same map, Go panics with:

fatal error: concurrent map iteration and map write

Fix

Clone maps at sharing boundaries using maps.Clone() (Go 1.21+):

Site Why it's shared
getWorkflowSecrets: job.Secrets() Returned by reference; mutated in-place by Interpolate loop
getWorkflowSecrets: rc.caller.runContext.Config.Secrets Inherited secrets shared across all caller goroutines
getWorkflowSecrets: rc.Config.Secrets Direct config reference returned to multiple consumers
getWorkflowVars: rc.Config.Vars Config vars shared across goroutines
NewExpressionEvaluator(WithEnv): jobs[needs].Outputs Job outputs read by multiple downstream needs evaluations

Context

PR #5026 previously fixed a concurrent map write in valueMasker (logger.go) by adding a sync.Mutex. The sites in expression.go were not addressed — they share maps by reference without any synchronization. maps.Clone() is a simpler fix here since the callers don't need the shared reference; they only need a snapshot.

Fixes #2199

Reusable workflows evaluate expressions in parallel goroutines that
share mutable map references for secrets, vars, and job outputs.
When one goroutine interpolates secrets in-place while another reads
the same map, Go panics with "concurrent map iteration and map write".

Clone maps at sharing boundaries using maps.Clone() (Go 1.21+):
- getWorkflowSecrets: clone job.Secrets() before in-place interpolation
- getWorkflowSecrets: clone inherited secrets from caller config
- getWorkflowSecrets: clone config secrets on direct return
- getWorkflowVars: clone config vars on return
- NewExpressionEvaluator(WithEnv): clone job outputs in needs map

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fatal error: concurrent map iteration and map write

1 participant