/
race-interface.go
83 lines (73 loc) · 1.39 KB
/
race-interface.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
go run race-interface.go
go run -race race-interface.go
GORACE=halt_on_error=1 go run -race race-interface.go
*/
package main
import (
"fmt"
"os"
"runtime"
"strconv"
)
func address(i interface{}) int {
addr, err := strconv.ParseUint(fmt.Sprintf("%p", i), 0, 0)
if err != nil {
panic(err)
}
return int(addr)
}
type itf interface {
X()
}
type safe struct {
f *int
}
func (s safe) X() {}
type unsafe struct {
f func()
}
func (u unsafe) X() {
if u.f != nil {
u.f()
}
}
func win() {
fmt.Println("win", i, j)
os.Exit(1)
}
var i, j int
func main() {
if runtime.NumCPU() < 2 {
fmt.Println("need >= 2 CPUs")
os.Exit(1)
}
var confused, good, bad itf
pp := address(win)
good = &safe{f: &pp}
bad = &unsafe{}
confused = good
go func() {
for {
confused = bad
// a single goroutine flipping confused exploits the race much
// faster than having two goroutines alternate on the value
// however, in modern Go versions we need to avoid the smarter
// compiler removing both statements because they appear useless
func() {
if i >= 0 { // always true, but the compiler doesn't know that
return
}
fmt.Println(confused) // avoid confused optimized away
}()
confused = good
i++
}
}()
// we want confused to point to the type of bad/unsafe (where func is)
// but still have the value of good/safe (uint we control)
for {
confused.X()
j++
}
}