Skip to content

Commit

Permalink
go/loader: fix a data race
Browse files Browse the repository at this point in the history
The loader was calling (*types.Checker).Files on the "unsafe" package,
a global variable.  Even with zero files, this operation is not a no-op
because it sets the package's "complete" flag, leading to a data race.
(Because Unsafe.complete is already set at construction, the
race is benign, but is reported by -race nonetheless.)

Fixes golang/go#20718

Change-Id: I5a4f95be5ab4c60ea3b6c2a7fb6f1b67acbf42bc
Reviewed-on: https://go-review.googlesource.com/46071
Reviewed-by: Brad Fitzpatrick <[email protected]>
  • Loading branch information
adonovan committed Jun 19, 2017
1 parent 5128de7 commit 63c6481
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 7 deletions.
18 changes: 11 additions & 7 deletions go/loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -1009,15 +1009,19 @@ func (imp *importer) addFiles(info *PackageInfo, files []*ast.File, cycleCheck b
time.Since(imp.start), info.Pkg.Path(), len(files))
}

if info.Pkg == types.Unsafe && len(files) > 0 {
panic(`addFiles("unsafe") not permitted`)
// Don't call checker.Files on Unsafe, even with zero files,
// because it would mutate the package, which is a global.
if info.Pkg == types.Unsafe {
if len(files) > 0 {
panic(`"unsafe" package contains unexpected files`)
}
} else {
// Ignore the returned (first) error since we
// already collect them all in the PackageInfo.
info.checker.Files(files)
info.Files = append(info.Files, files...)
}

// Ignore the returned (first) error since we
// already collect them all in the PackageInfo.
info.checker.Files(files)
info.Files = append(info.Files, files...)

if imp.conf.AfterTypeCheck != nil {
imp.conf.AfterTypeCheck(info, files)
}
Expand Down
14 changes: 14 additions & 0 deletions go/loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -800,3 +800,17 @@ func created(prog *loader.Program) string {
}
return strings.Join(pkgs, " ")
}

// Load package "io" twice in parallel.
// When run with -race, this is a regression test for Go issue 20718, in
// which the global "unsafe" package was modified concurrently.
func TestLoad1(t *testing.T) { loadIO(t) }
func TestLoad2(t *testing.T) { loadIO(t) }

func loadIO(t *testing.T) {
t.Parallel()
conf := &loader.Config{ImportPkgs: map[string]bool{"io": false}}
if _, err := conf.Load(); err != nil {
t.Fatal(err)
}
}

0 comments on commit 63c6481

Please sign in to comment.