github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/tools/checklocks/checklocks.go (about) 1 // Copyright 2020 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package checklocks performs lock analysis to identify and flag unprotected 16 // access to annotated fields. 17 // 18 // For detailed usage refer to README.md in the same directory. 19 // 20 // Note that this package uses the built-in atomics, in order to avoid the use 21 // of our own atomic package. This is because our own atomic package depends on 22 // our own sync package, which includes lock dependency analysis. This in turn 23 // requires goid, which introduces a dependency cycle. To avoid this, we simply 24 // use the simpler, built-in sync package. 25 // 26 // +checkalignedignore 27 package checklocks 28 29 import ( 30 "go/ast" 31 "go/token" 32 "go/types" 33 34 "golang.org/x/tools/go/analysis" 35 "golang.org/x/tools/go/analysis/passes/buildssa" 36 "golang.org/x/tools/go/ssa" 37 ) 38 39 // Analyzer is the main entrypoint. 40 var Analyzer = &analysis.Analyzer{ 41 Name: "checklocks", 42 Doc: "checks lock preconditions on functions and fields", 43 Run: run, 44 Requires: []*analysis.Analyzer{buildssa.Analyzer}, 45 FactTypes: []analysis.Fact{ 46 (*atomicAlignment)(nil), 47 (*lockGuardFacts)(nil), 48 (*lockFunctionFacts)(nil), 49 }, 50 } 51 52 var ( 53 enableInferred = true 54 enableAtomic = true 55 enableWrappers = true 56 ) 57 58 func init() { 59 Analyzer.Flags.BoolVar(&enableInferred, "inferred", true, "enable inferred locks") 60 Analyzer.Flags.BoolVar(&enableAtomic, "atomic", true, "enable atomic checks") 61 Analyzer.Flags.BoolVar(&enableWrappers, "wrappers", true, "enable analysis of wrappers") 62 } 63 64 // objectObservations tracks lock correlations. 65 type objectObservations struct { 66 counts map[types.Object]int 67 total int 68 } 69 70 // passContext is a pass with additional expected failures. 71 type passContext struct { 72 pass *analysis.Pass 73 failures map[positionKey]*failData 74 exemptions map[positionKey]struct{} 75 forced map[positionKey]struct{} 76 functions map[*ssa.Function]struct{} 77 observations map[types.Object]*objectObservations 78 } 79 80 // observationsFor retrieves observations for the given object. 81 func (pc *passContext) observationsFor(obj types.Object) *objectObservations { 82 if pc.observations == nil { 83 pc.observations = make(map[types.Object]*objectObservations) 84 } 85 oo, ok := pc.observations[obj] 86 if !ok { 87 oo = &objectObservations{ 88 counts: make(map[types.Object]int), 89 } 90 pc.observations[obj] = oo 91 } 92 return oo 93 } 94 95 // forAllGlobals applies the given function to all globals. 96 func (pc *passContext) forAllGlobals(fn func(ts *ast.ValueSpec)) { 97 for _, f := range pc.pass.Files { 98 for _, decl := range f.Decls { 99 d, ok := decl.(*ast.GenDecl) 100 if !ok || d.Tok != token.VAR { 101 continue 102 } 103 for _, gs := range d.Specs { 104 fn(gs.(*ast.ValueSpec)) 105 } 106 } 107 } 108 } 109 110 // forAllTypes applies the given function over all types. 111 func (pc *passContext) forAllTypes(fn func(ts *ast.TypeSpec)) { 112 for _, f := range pc.pass.Files { 113 for _, decl := range f.Decls { 114 d, ok := decl.(*ast.GenDecl) 115 if !ok || d.Tok != token.TYPE { 116 continue 117 } 118 for _, gs := range d.Specs { 119 fn(gs.(*ast.TypeSpec)) 120 } 121 } 122 } 123 } 124 125 // forAllFunctions applies the given function over all functions. 126 func (pc *passContext) forAllFunctions(fn func(fn *ast.FuncDecl)) { 127 for _, f := range pc.pass.Files { 128 for _, decl := range f.Decls { 129 d, ok := decl.(*ast.FuncDecl) 130 if !ok { 131 continue 132 } 133 fn(d) 134 } 135 } 136 } 137 138 // run is the main entrypoint. 139 func run(pass *analysis.Pass) (any, error) { 140 pc := &passContext{ 141 pass: pass, 142 failures: make(map[positionKey]*failData), 143 exemptions: make(map[positionKey]struct{}), 144 forced: make(map[positionKey]struct{}), 145 functions: make(map[*ssa.Function]struct{}), 146 } 147 148 // Find all line failure annotations. 149 pc.extractLineFailures() 150 151 // Find all struct declarations and export relevant facts. 152 pc.forAllGlobals(func(vs *ast.ValueSpec) { 153 if ss, ok := vs.Type.(*ast.StructType); ok { 154 structType := pc.pass.TypesInfo.TypeOf(vs.Type).Underlying().(*types.Struct) 155 pc.structLockGuardFacts(structType, ss) 156 } 157 pc.globalLockGuardFacts(vs) 158 }) 159 pc.forAllTypes(func(ts *ast.TypeSpec) { 160 if ss, ok := ts.Type.(*ast.StructType); ok { 161 structType := pc.pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct) 162 pc.structLockGuardFacts(structType, ss) 163 } 164 }) 165 166 // Check all alignments. 167 pc.forAllTypes(func(ts *ast.TypeSpec) { 168 typ, ok := pass.TypesInfo.TypeOf(ts.Name).(*types.Named) 169 if !ok { 170 return 171 } 172 pc.checkTypeAlignment(pass.Pkg, typ) 173 }) 174 175 // Find all function declarations and export relevant facts. 176 pc.forAllFunctions(func(fn *ast.FuncDecl) { 177 pc.functionFacts(fn) 178 }) 179 180 // Scan all code looking for invalid accesses. 181 state := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 182 for _, fn := range state.SrcFuncs { 183 // Import function facts generated above. 184 // 185 // Note that anonymous(closures) functions do not have an 186 // object but do show up in the SSA. They can only be invoked 187 // by named functions in the package, and they are analyzing 188 // inline on every call. Thus we skip the analysis here. They 189 // will be hit on calls, or picked up in the pass below. 190 if obj := fn.Object(); obj == nil { 191 continue 192 } 193 var lff lockFunctionFacts 194 pc.pass.ImportObjectFact(fn.Object(), &lff) 195 196 // Check the basic blocks in the function. 197 pc.checkFunction(nil, fn, &lff, nil, false /* force */) 198 } 199 for _, fn := range state.SrcFuncs { 200 // Ensure all anonymous functions are hit. They are not 201 // permitted to have any lock preconditions. 202 if obj := fn.Object(); obj != nil { 203 continue 204 } 205 var nolff lockFunctionFacts 206 pc.checkFunction(nil, fn, &nolff, nil, false /* force */) 207 } 208 209 // Check for inferred checklocks annotations. 210 if enableInferred { 211 pc.checkInferred() 212 } 213 214 // Check for expected failures. 215 pc.checkFailures() 216 217 return nil, nil 218 }