Skip to content

SpinResult.Reasons slice shallow-copied in Snapshot — latent race #3

Description

@rajfirke

Problem

SessionStore.Snapshot() in internal/metrics/session.go:156 does cp := *s which shallow-copies the Spin field, including the Reasons []string slice. The snapshot and the live SessionMetrics share the same backing array.

Currently safe because SpinDetector.Check() always returns a fresh SpinResult with a newly-allocated Reasons slice. But if Check() is ever changed to accumulate reasons across calls, the background goroutine would mutate the backing array while the TUI goroutine reads it.

Note: FilesChanged (map) IS deep-copied on lines 157-160, but Reasons is not.

Impact

Latent — safe today, one refactor away from a data race.

Suggested Fix

Deep-copy Spin.Reasons in Snapshot():

if len(s.Spin.Reasons) > 0 {
    cp.Spin.Reasons = make([]string, len(s.Spin.Reasons))
    copy(cp.Spin.Reasons, s.Spin.Reasons)
}

Files

  • internal/metrics/session.go:150-172

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingconcurrencyRace conditions, lock ordering, thread safetygood first issueGood for newcomers

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions