diff --git a/tools/checklocks/analysis.go b/tools/checklocks/analysis.go index 9b880d7db..8a8f18ea5 100644 --- a/tools/checklocks/analysis.go +++ b/tools/checklocks/analysis.go @@ -322,9 +322,17 @@ func (pc *passContext) checkFieldAccess(inst almostInst, structObj ssa.Value, fi pc.checkGuards(inst, structObj, fieldObj, ls, isWrite) } +// noReferrers wraps an instruction as an almostInst. +type noReferrers struct { + ssa.Instruction +} + +// Referrers implements almostInst.Referrers. +func (noReferrers) Referrers() *[]ssa.Instruction { return nil } + // checkGlobalAccess checks the validity of a global access. -func (pc *passContext) checkGlobalAccess(g *ssa.Global, ls *lockState, isWrite bool) { - pc.checkGuards(g, g, g.Object(), ls, isWrite) +func (pc *passContext) checkGlobalAccess(inst ssa.Instruction, g *ssa.Global, ls *lockState, isWrite bool) { + pc.checkGuards(noReferrers{inst}, g, g.Object(), ls, isWrite) } func (pc *passContext) checkCall(call callCommon, lff *lockFunctionFacts, ls *lockState) { @@ -592,7 +600,7 @@ func (pc *passContext) checkInstruction(inst ssa.Instruction, lff *lockFunctionF continue } _, isWrite := inst.(*ssa.Store) - pc.checkGlobalAccess(g, ls, isWrite) + pc.checkGlobalAccess(inst, g, ls, isWrite) } // Process the instruction. diff --git a/tools/checklocks/facts.go b/tools/checklocks/facts.go index cdac713a9..2d9d6380a 100644 --- a/tools/checklocks/facts.go +++ b/tools/checklocks/facts.go @@ -167,6 +167,9 @@ type globalGuard struct { // ObjectName indicates the object from which resolution should occur. ObjectName string + // PackageName is the package where the object lives. + PackageName string + // FieldList is the traversal path from object. FieldList fieldList } @@ -179,7 +182,11 @@ type ssaPackager interface { // resolveCommon implements resolution for all cases. func (g *globalGuard) resolveCommon(pc *passContext, ls *lockState) resolvedValue { state := pc.pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) - v := state.Pkg.Members[g.ObjectName].(ssa.Value) + pkg := state.Pkg + if g.PackageName != "" && g.PackageName != state.Pkg.Pkg.Path() { + pkg = state.Pkg.Prog.ImportedPackage(g.PackageName) + } + v := pkg.Members[g.ObjectName].(ssa.Value) return makeResolvedValue(v, g.FieldList) } @@ -627,8 +634,9 @@ func (pc *passContext) findGlobalGuard(pos token.Pos, guardName string) (*global return nil, false } return &globalGuard{ - ObjectName: parts[0], - FieldList: fl, + ObjectName: parts[0], + PackageName: pc.pass.Pkg.Path(), + FieldList: fl, }, true } diff --git a/tools/checklocks/test/BUILD b/tools/checklocks/test/BUILD index 21a68fbdf..9ef6a0a1b 100644 --- a/tools/checklocks/test/BUILD +++ b/tools/checklocks/test/BUILD @@ -27,4 +27,5 @@ go_library( # control expected failures for analysis. marshal = False, stateify = False, + deps = ["//tools/checklocks/test/crosspkg"], ) diff --git a/tools/checklocks/test/crosspkg/BUILD b/tools/checklocks/test/crosspkg/BUILD new file mode 100644 index 000000000..03fa92297 --- /dev/null +++ b/tools/checklocks/test/crosspkg/BUILD @@ -0,0 +1,12 @@ +load("//tools:defs.bzl", "go_library") + +package(licenses = ["notice"]) + +go_library( + name = "crosspkg", + srcs = ["crosspkg.go"], + # See next level up. + marshal = False, + stateify = False, + visibility = ["//tools/checklocks/test:__pkg__"], +) diff --git a/tools/checklocks/test/crosspkg/crosspkg.go b/tools/checklocks/test/crosspkg/crosspkg.go new file mode 100644 index 000000000..ccc0d6bad --- /dev/null +++ b/tools/checklocks/test/crosspkg/crosspkg.go @@ -0,0 +1,26 @@ +// Copyright 2022 The gVisor Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package crosspkg is a second package for testing. +package crosspkg + +import ( + "sync" +) + +var ( + // +checklocks:FooMu + Foo int + FooMu sync.Mutex +) diff --git a/tools/checklocks/test/globals.go b/tools/checklocks/test/globals.go index 656b0c9a3..03c7473d6 100644 --- a/tools/checklocks/test/globals.go +++ b/tools/checklocks/test/globals.go @@ -16,6 +16,8 @@ package test import ( "sync" + + "gvisor.dev/gvisor/tools/checklocks/test/crosspkg" ) var ( @@ -83,3 +85,13 @@ func testGlobalInvalid() { otherStruct.guardedField2 = 1 // +checklocksfail otherStruct.guardedField3 = 1 // +checklocksfail } + +func testCrosspkgGlobalValid() { + crosspkg.FooMu.Lock() + crosspkg.Foo = 1 + crosspkg.FooMu.Unlock() +} + +func testCrosspkgGlobalInvalid() { + crosspkg.Foo = 1 // +checklocksfail +}