Skip to content

cmp: avoid time.Location initialization race in reports - #400

Open
1678092075 wants to merge 1 commit into
google:masterfrom
1678092075:fix/time-local-race
Open

cmp: avoid time.Location initialization race in reports#400
1678092075 wants to merge 1 commit into
google:masterfrom
1678092075:fix/time-local-race

Conversation

@1678092075

Copy link
Copy Markdown

Problem

When cmp.Diff produces a high-verbosity report that avoids String methods, it can reflect over the unexported fields of a time.Time. If the referenced time.Location is being lazily initialized at the same time, the report races with the standard library's initialization writes.

This is the reporting race described in #381 and golang/go#74460.

Root cause

The fallback report bypasses fmt.Stringer so it can expose otherwise hidden differences. For time.Time, that means recursively reading its private Location state without first synchronizing with lazy location initialization.

Change

Before reflectively formatting a time.Time with Stringer formatting disabled, initialize the specific Location referenced by that value. This keeps the existing reflective output, including map-key disambiguation, while avoiding concurrent reads during lazy initialization.

The regression coverage:

  • runs the race scenario in a fresh child process so the location starts uninitialized;
  • replaces the global time.Local after constructing the value, verifying that the value's actual location is synchronized;
  • verifies that distinct time.Time map keys with identical String output remain distinguishable.

Verification

  • GOCACHE=/tmp/go-cmp-cache-regression go test -race ./cmp -run '^TestDiffDoesNotRaceWithTimeLocalInitialization$' -count=5 — pass
  • GOCACHE=/tmp/go-cmp-cache-target go test ./cmp -run '^TestFormatMapKeyDisambiguatesTimeLocations$' -count=3 — pass
  • GOCACHE=/tmp/go-cmp-cache-race go test -race ./... — pass
  • test -z "$(gofmt -d .)" — pass
  • git diff --check HEAD^ HEAD — pass

Compatibility and risk

There is no public API change. The special case only runs when report formatting intentionally bypasses Stringer methods; ordinary time.Time formatting and comparison behavior are unchanged.

Fixes #381.

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.

Race condition when using go-cmp on structs that include time.Time

1 participant