Injectable Skill: Go Concurrency Safety
L1 trigger:
L1_PATTERN=trueAND target language = Go Inject Into: Every L1 depth agent working on Go code, in addition to the main skill Finding prefix:[GO-N]Status: v0.1 draft, Round 4 exemplars pending
When This Skill Activates
Supplement to the main L1 skills when the target is written in Go. It codifies the Go-specific traps that turn otherwise-correct logic into production bugs. These are drawn from the Cosmos-SDK, Geth, Erigon, and CometBFT bug histories.
1. Map Iteration Non-Determinism
Go maps iterate in unspecified order. Production bug class: any consensus computation that ranges over a map[K]V and uses the order in its output will produce different results on different nodes.
Detection:
- Ast-grep
for $_, $_ := range $MAPwhere$MAPis declared asmap[...]... - For each hit in a consensus-critical path, check if the iteration result affects any stored state, signed message, or hash input
Fix pattern:
keys := make([]string, 0, len(m))
for k := range m {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
v := m[k]
// use (k, v)
}
Alternative: use btree or sort.SliceStable on a converted slice.
Tag: [GO-MAP-ITER:{loc}:{consumer}]
2. Goroutine Leaks
Goroutines that are started but never terminated. Over hours/days, a leak accumulates and eventually OOMs the node.
Common patterns:
- Goroutine blocked on
<-chwhere the sender path can return without sending - Goroutine blocked on
http.Get/ network call without context timeout - Goroutine in a
forloop without a cancellation check - Goroutine spawned inside a handler that outlives the handler's parent context
Detection:
- Ast-grep
go $FUNC(...)/go func() { ... }() - For each, trace the function body: is there a termination condition? Is it guaranteed to fire?
- Check every
selectinside: does it have acase <-ctx.Done()?
Tag: [GO-GOROUTINE-LEAK:{spawn-loc}:{blocked-on}]
3. Mutex Deadlock Risk
Locking order inversions cause deadlocks. In node clients, locks are often acquired across subsystem boundaries (consensus holds lock A, calls into p2p which acquires lock B; p2p holds lock B, calls into consensus which acquires lock A → deadlock).
Detection:
- For every function that acquires 2+ mutexes (directly or via called functions), determine the lock order
- Look for lock order inversions across the same two locks
- Look for
Lock/Unlockpairs with an early return that skips Unlock (preferdefer Unlock)
Tag: [GO-LOCK-ORDER:{lock-a}:{lock-b}:{inversion-site}]
4. Channel Send on Closed Channel
Sending on a closed channel panics. In Go, closing a channel you don't own is usually wrong.
Detection:
- Every
close(ch): who ownsch? Can another goroutine send on it after close? for _, x := range chpattern: correct consumer side; check producer side always closes exactly once
Tag: [GO-CLOSED-CHAN:{loc}]
5. Panic Boundaries
A goroutine panic crashes the whole process (unless recovered). In RPC handlers, HTTP middleware typically recovers; in background workers, often not.
Detection:
- For each
go func()in background code: is the first linedefer func() { recover() }? - For each handler that calls user input-parsing code: is there a recover boundary?
panic()in consensus code: should never happen; anypanicinside the consensus package is a bug
Tag: [GO-PANIC:{loc}:{recovered}]
6. Context Cancellation Propagation
The context.Context pattern propagates cancellation. Every long-running operation should accept a context and check it.
Detection:
- Every function in a hot path that does I/O: does it take
ctxand honor cancellation? - Every goroutine in a pool: does it use the pool's context for shutdown?
context.Background()in production code is a smell — usually should be a child of a longer-lived context
Tag: [GO-CTX:{loc}:{missing-propagation}]
7. defer Gotchas
deferin a loop: the deferred function runs when the outer function returns, not when the loop iteration ends. Can accumulate.deferwith captured variables: closure captures by reference; variables may have changed by the time defer runs.defer f(x)evaluatesxat defer time;defer func() { f(x) }()evaluates at call time.
Tag: [GO-DEFER:{loc}:{issue}]
8. Integer Overflow
Go doesn't panic on integer overflow (wraps silently). In consensus math, this is dangerous.
Detection:
- Every
+,*,-onuint64/int64in consensus/fee/stake computation - Use
math.AddUint64or explicit overflow checks big.Intis safer for unbounded arithmetic
Tag: [GO-OVERFLOW:{loc}:{type}]
Output schema
- Language: Go
- Bug class prefix:
GO- - Preferred evidence tags: Same as parent skill
Known bug exemplars (v0.2 — Round 4 verified)
btcd signed-int transaction version (CVE-2024-34478) — transaction version treated as signed int instead of unsigned, combined with a data race in the consensus-rule check. Chain split and fund loss possible. Snyk advisory. Skill catch point: Section 8 (integer overflow) +
go test -race. Every signed/unsigned mismatch on a consensus-relevant field is a finding.Geth concurrent map iteration and map write panic (issue #17750) — map accessed from multiple goroutines without sync; Go runtime panics with
fatal error: concurrent map iteration and map write. go-ethereum #17750. Skill catch point: Section 3 — for every map field on a struct shared across goroutines, verify it is protected bysync.Map,sync.RWMutex, or channel serialization.Cosmos SDK map iteration determinism (EPIC #13039) — class-wide issue across multiple modules. Go's runtime intentionally randomizes
range mformap[K]V, making every state-machine iteration over a map a potential chain-halt vector. EPIC #13039. Skill catch point: Section 1 — mandatory check.Aptos 10/18/23 HashMap regression — performance refactor replaced a deterministic map with Rust
HashMap; since Aptos uses Move VM written in Rust, this is a Rust concurrency bug by mechanism but the same class applies to Go. Referenced inconsensus-safety-invariantsskill.
Runtime-gate additional check
Always run go test -race ./... and go vet ./... as part of the language-specific scanner. Race detector findings on consensus-relevant types (state, block, validator-set) are automatic High-severity candidates.
Cross-references
- Supplements all L1 skills when target = Go
- Primary consumer: any depth agent on Go code