Skip to content

Commit cf826bf

Browse files
Mark Freemangopherbot
authored andcommitted
go/types, types2: set t.underlying exactly once in resolveUnderlying
While the locking in Named.resolveUnderlying is mostly fine, we do not perform an atomic read before the write to underlying. If resolveUnderlying returns and another thread was waiting on the lock, it can perform a second (in this case identical) write to t.underlying. A reader thread on n.underlying can thus observe either of those writes, tripping the race detector. Michael was kind enough to provide a diagram: T1 T2 1. t.stateHas(underlying) // false 2. t.stateHas(underlying) // false 3. t.mu.Lock() // acquired 4. t.mu.Lock() // blocked 5. t.underlying = u 6. t.setState(underlying) 7. t.mu.Unlock() 8. t.underlying = u // overwritten 9. t.setState(underlying) 10. t.mu.Unlock() Adding a second check before setting t.underlying prevents the write on line 8. Change-Id: Ia43a6d3ba751caef436b9926c6ece2a71dfb9d38 Reviewed-on: https://go-review.googlesource.com/c/go/+/714300 Auto-Submit: Mark Freeman <[email protected]> Reviewed-by: Michael Pratt <[email protected]> Reviewed-by: Robert Griesemer <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent c4e9108 commit cf826bf

File tree

2 files changed

+14
-0
lines changed

2 files changed

+14
-0
lines changed

src/cmd/compile/internal/types2/named.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ func (n *Named) resolveUnderlying() {
630630
t.resolve()
631631
t.mu.Lock()
632632
defer t.mu.Unlock()
633+
633634
assert(t.fromRHS != nil || t.allowNilRHS)
634635
rhs = t.fromRHS
635636

@@ -640,6 +641,12 @@ func (n *Named) resolveUnderlying() {
640641

641642
// set underlying for all Named types in the chain
642643
for t := range seen {
644+
// Careful, t.underlying has lock-free readers. Since we might be racing
645+
// another call to resolveUnderlying, we have to avoid overwriting
646+
// t.underlying. Otherwise, the race detector will be tripped.
647+
if t.stateHas(underlying) {
648+
continue
649+
}
643650
t.underlying = u
644651
t.setState(underlying)
645652
}

src/go/types/named.go

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)