github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 package checklocks 20 21 import ( 22 "go/ast" 23 "go/token" 24 "go/types" 25 26 "golang.org/x/tools/go/analysis" 27 "golang.org/x/tools/go/analysis/passes/buildssa" 28 "golang.org/x/tools/go/ssa" 29 ) 30 31 // Analyzer is the main entrypoint. 32 var Analyzer = &analysis.Analyzer{ 33 Name: "checklocks", 34 Doc: "checks lock preconditions on functions and fields", 35 Run: run, 36 Requires: []*analysis.Analyzer{buildssa.Analyzer}, 37 FactTypes: []analysis.Fact{(*atomicAlignment)(nil), (*lockFieldFacts)(nil), (*lockGuardFacts)(nil), (*lockFunctionFacts)(nil)}, 38 } 39 40 // passContext is a pass with additional expected failures. 41 type passContext struct { 42 pass *analysis.Pass 43 failures map[positionKey]*failData 44 exemptions map[positionKey]struct{} 45 forced map[positionKey]struct{} 46 functions map[*ssa.Function]struct{} 47 } 48 49 // forAllTypes applies the given function over all types. 50 func (pc *passContext) forAllTypes(fn func(ts *ast.TypeSpec)) { 51 for _, f := range pc.pass.Files { 52 for _, decl := range f.Decls { 53 d, ok := decl.(*ast.GenDecl) 54 if !ok || d.Tok != token.TYPE { 55 continue 56 } 57 for _, gs := range d.Specs { 58 fn(gs.(*ast.TypeSpec)) 59 } 60 } 61 } 62 } 63 64 // forAllFunctions applies the given function over all functions. 65 func (pc *passContext) forAllFunctions(fn func(fn *ast.FuncDecl)) { 66 for _, f := range pc.pass.Files { 67 for _, decl := range f.Decls { 68 d, ok := decl.(*ast.FuncDecl) 69 if !ok { 70 continue 71 } 72 fn(d) 73 } 74 } 75 } 76 77 // run is the main entrypoint. 78 func run(pass *analysis.Pass) (interface{}, error) { 79 pc := &passContext{ 80 pass: pass, 81 failures: make(map[positionKey]*failData), 82 exemptions: make(map[positionKey]struct{}), 83 forced: make(map[positionKey]struct{}), 84 functions: make(map[*ssa.Function]struct{}), 85 } 86 87 // Find all line failure annotations. 88 pc.extractLineFailures() 89 90 // Find all struct declarations and export relevant facts. 91 pc.forAllTypes(func(ts *ast.TypeSpec) { 92 if ss, ok := ts.Type.(*ast.StructType); ok { 93 pc.exportLockFieldFacts(ts, ss) 94 } 95 }) 96 pc.forAllTypes(func(ts *ast.TypeSpec) { 97 if ss, ok := ts.Type.(*ast.StructType); ok { 98 pc.exportLockGuardFacts(ts, ss) 99 } 100 }) 101 102 // Check all alignments. 103 pc.forAllTypes(func(ts *ast.TypeSpec) { 104 typ, ok := pass.TypesInfo.TypeOf(ts.Name).(*types.Named) 105 if !ok { 106 return 107 } 108 pc.checkTypeAlignment(pass.Pkg, typ) 109 }) 110 111 // Find all function declarations and export relevant facts. 112 pc.forAllFunctions(func(fn *ast.FuncDecl) { 113 pc.exportFunctionFacts(fn) 114 }) 115 116 // Scan all code looking for invalid accesses. 117 state := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 118 for _, fn := range state.SrcFuncs { 119 // Import function facts generated above. 120 // 121 // Note that anonymous(closures) functions do not have an 122 // object but do show up in the SSA. They can only be invoked 123 // by named functions in the package, and they are analyzing 124 // inline on every call. Thus we skip the analysis here. They 125 // will be hit on calls, or picked up in the pass below. 126 if obj := fn.Object(); obj == nil { 127 continue 128 } 129 var lff lockFunctionFacts 130 pc.pass.ImportObjectFact(fn.Object(), &lff) 131 132 // Do we ignore this? 133 if lff.Ignore { 134 continue 135 } 136 137 // Check the basic blocks in the function. 138 pc.checkFunction(nil, fn, &lff, nil, false /* force */) 139 } 140 for _, fn := range state.SrcFuncs { 141 // Ensure all anonymous functions are hit. They are not 142 // permitted to have any lock preconditions. 143 if obj := fn.Object(); obj != nil { 144 continue 145 } 146 var nolff lockFunctionFacts 147 pc.checkFunction(nil, fn, &nolff, nil, false /* force */) 148 } 149 150 // Check for expected failures. 151 pc.checkFailures() 152 153 return nil, nil 154 }