github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/tools/checklocks/facts.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 16 17 import ( 18 "encoding/gob" 19 "fmt" 20 "go/ast" 21 "go/token" 22 "go/types" 23 "regexp" 24 "strings" 25 26 "golang.org/x/tools/go/analysis/passes/buildssa" 27 "golang.org/x/tools/go/ssa" 28 ) 29 30 // atomicAlignment is saved per type. 31 // 32 // This represents the alignment required for the type, which may 33 // be implied and imposed by other types within the aggregate type. 34 type atomicAlignment int 35 36 // AFact implements analysis.Fact.AFact. 37 func (*atomicAlignment) AFact() {} 38 39 // atomicDisposition is saved per field. 40 // 41 // This represents how the field must be accessed. It must either 42 // be non-atomic (default), atomic or ignored. 43 type atomicDisposition int 44 45 const ( 46 atomicDisallow atomicDisposition = iota 47 atomicIgnore 48 atomicRequired 49 ) 50 51 // fieldEntry is a single field type. 52 type fieldEntry interface { 53 // synthesize produces a string that is compatible with valueAndObject, 54 // along with the same object that should be produced in that case. 55 // 56 // Note that it is called synthesize because this is produced only the 57 // type information, and not with any ssa.Value objects. 58 synthesize(s string, typ types.Type) (string, types.Object) 59 } 60 61 // fieldStruct is a non-pointer struct element. 62 type fieldStruct int 63 64 // synthesize implements fieldEntry.synthesize. 65 func (f fieldStruct) synthesize(s string, typ types.Type) (string, types.Object) { 66 field, ok := findField(typ, int(f)) 67 if !ok { 68 // Should not happen as long as fieldList construction is correct. 69 panic(fmt.Sprintf("unable to resolve field %d in %s", int(f), typ.String())) 70 } 71 return fmt.Sprintf("&(%s.%s)", s, field.Name()), field 72 } 73 74 // fieldStructPtr is a pointer struct element. 75 type fieldStructPtr int 76 77 // synthesize implements fieldEntry.synthesize. 78 func (f fieldStructPtr) synthesize(s string, typ types.Type) (string, types.Object) { 79 field, ok := findField(typ, int(f)) 80 if !ok { 81 // See above, this should not happen. 82 panic(fmt.Sprintf("unable to resolve ptr field %d in %s", int(f), typ.String())) 83 } 84 return fmt.Sprintf("*(&(%s.%s))", s, field.Name()), field 85 } 86 87 // fieldList is a simple list of fields, used in two types below. 88 type fieldList []fieldEntry 89 90 // resolvedValue is an ssa.Value with additional fields. 91 // 92 // This can be resolved to a string as part of a lock state. 93 type resolvedValue struct { 94 value ssa.Value 95 fieldList fieldList 96 } 97 98 // makeResolvedValue makes a new resolvedValue. 99 func makeResolvedValue(v ssa.Value, fl fieldList) resolvedValue { 100 return resolvedValue{ 101 value: v, 102 fieldList: fl, 103 } 104 } 105 106 // valid indicates whether this is a valid resolvedValue. 107 func (rv *resolvedValue) valid() bool { 108 return rv.value != nil 109 } 110 111 // valueAndObject returns a string and object. 112 // 113 // This uses the lockState valueAndObject in order to produce a string and 114 // object for the base ssa.Value, then synthesizes a string representation 115 // based on the fieldList. 116 func (rv *resolvedValue) valueAndObject(ls *lockState) (string, types.Object) { 117 // N.B. obj.Type() and typ should be equal, but a check is omitted 118 // since, 1) we automatically chase through pointers during field 119 // resolution, and 2) obj may be nil if there is no source object. 120 s, obj := ls.valueAndObject(rv.value) 121 typ := rv.value.Type() 122 for _, entry := range rv.fieldList { 123 s, obj = entry.synthesize(s, typ) 124 typ = obj.Type() 125 } 126 return s, obj 127 } 128 129 // fieldGuardResolver details a guard for a field. 130 type fieldGuardResolver interface { 131 // resolveField is used to resolve a guard during a field access. The 132 // parent structure is available, as well as the current lock state. 133 resolveField(pc *passContext, ls *lockState, parent ssa.Value) resolvedValue 134 } 135 136 // functionGuardResolver details a guard for a function. 137 type functionGuardResolver interface { 138 // resolveStatic is used to resolve a guard during static analysis, 139 // e.g. based on static annotations applied to a method. The function's 140 // ssa object is available, as well as the return value. 141 resolveStatic(pc *passContext, ls *lockState, fn *ssa.Function, rv any) resolvedValue 142 143 // resolveCall is used to resolve a guard during a call. The ssa 144 // return value is available from the instruction context where the 145 // call occurs, but the target's ssa representation is not available. 146 resolveCall(pc *passContext, ls *lockState, args []ssa.Value, rv ssa.Value) resolvedValue 147 } 148 149 // lockGuardFacts contains guard information. 150 type lockGuardFacts struct { 151 // GuardedBy is the set of locks that are guarding this field. The key 152 // is the original annotation value, and the field list is the object 153 // traversal path. 154 GuardedBy map[string]fieldGuardResolver 155 156 // AtomicDisposition is the disposition for this field. Note that this 157 // can affect the interpretation of the GuardedBy field above, see the 158 // relevant comment. 159 AtomicDisposition atomicDisposition 160 } 161 162 // AFact implements analysis.Fact.AFact. 163 func (*lockGuardFacts) AFact() {} 164 165 // globalGuard is a global value. 166 type globalGuard struct { 167 // ObjectName indicates the object from which resolution should occur. 168 ObjectName string 169 170 // PackageName is the package where the object lives. 171 PackageName string 172 173 // FieldList is the traversal path from object. 174 FieldList fieldList 175 } 176 177 // ssaPackager returns the ssa package. 178 type ssaPackager interface { 179 Package() *ssa.Package 180 } 181 182 // resolveCommon implements resolution for all cases. 183 func (g *globalGuard) resolveCommon(pc *passContext, ls *lockState) resolvedValue { 184 state := pc.pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 185 pkg := state.Pkg 186 if g.PackageName != "" && g.PackageName != state.Pkg.Pkg.Path() { 187 pkg = state.Pkg.Prog.ImportedPackage(g.PackageName) 188 } 189 v := pkg.Members[g.ObjectName].(ssa.Value) 190 return makeResolvedValue(v, g.FieldList) 191 } 192 193 // resolveStatic implements functionGuardResolver.resolveStatic. 194 func (g *globalGuard) resolveStatic(pc *passContext, ls *lockState, _ *ssa.Function, v any) resolvedValue { 195 return g.resolveCommon(pc, ls) 196 } 197 198 // resolveCall implements functionGuardResolver.resolveCall. 199 func (g *globalGuard) resolveCall(pc *passContext, ls *lockState, _ []ssa.Value, v ssa.Value) resolvedValue { 200 return g.resolveCommon(pc, ls) 201 } 202 203 // resolveField implements fieldGuardResolver.resolveField. 204 func (g *globalGuard) resolveField(pc *passContext, ls *lockState, parent ssa.Value) resolvedValue { 205 return g.resolveCommon(pc, ls) 206 } 207 208 // fieldGuard is a field-based guard. 209 type fieldGuard struct { 210 // FieldList is the traversal path from the parent. 211 FieldList fieldList 212 } 213 214 // resolveField implements fieldGuardResolver.resolveField. 215 func (f *fieldGuard) resolveField(_ *passContext, _ *lockState, parent ssa.Value) resolvedValue { 216 return makeResolvedValue(parent, f.FieldList) 217 } 218 219 // parameterGuard is a parameter-based guard. 220 type parameterGuard struct { 221 // Index is the parameter index of the object that contains the 222 // guarding mutex. 223 Index int 224 225 // fieldList is the traversal path from the parameter. 226 FieldList fieldList 227 } 228 229 // resolveStatic implements functionGuardResolver.resolveStatic. 230 func (p *parameterGuard) resolveStatic(_ *passContext, _ *lockState, fn *ssa.Function, _ any) resolvedValue { 231 return makeResolvedValue(fn.Params[p.Index], p.FieldList) 232 } 233 234 // resolveCall implements functionGuardResolver.resolveCall. 235 func (p *parameterGuard) resolveCall(_ *passContext, _ *lockState, args []ssa.Value, _ ssa.Value) resolvedValue { 236 return makeResolvedValue(args[p.Index], p.FieldList) 237 } 238 239 // returnGuard is a return-based guard. 240 type returnGuard struct { 241 // Index is the index of the return value. 242 Index int 243 244 // NeedsExtract is used in the case of a return value, and indicates 245 // that the field must be extracted from a tuple. 246 NeedsExtract bool 247 248 // FieldList is the traversal path from the return value. 249 FieldList fieldList 250 } 251 252 // resolveCommon implements resolution for both cases. 253 func (r *returnGuard) resolveCommon(rv any) resolvedValue { 254 if rv == nil { 255 // For defers and other objects, this may be nil. This is 256 // handled in state.go in the actual lock checking logic. This 257 // means that there is no resolvedValue available. 258 return resolvedValue{} 259 } 260 // If this is a *ssa.Return object, i.e. we are analyzing the function 261 // and not the call site, then we can just pull the result directly. 262 if ret, ok := rv.(*ssa.Return); ok { 263 return makeResolvedValue(ret.Results[r.Index], r.FieldList) 264 } 265 if r.NeedsExtract { 266 // Resolve on the extracted field, this is necessary if the 267 // type here is not an explicit return. Note that rv must be an 268 // ssa.Value, since it is not an *ssa.Return. 269 v := rv.(ssa.Value) 270 if refs := v.Referrers(); refs != nil { 271 for _, inst := range *refs { 272 if x, ok := inst.(*ssa.Extract); ok && x.Tuple == v && x.Index == r.Index { 273 return makeResolvedValue(x, r.FieldList) 274 } 275 } 276 } 277 // Nothing resolved. 278 return resolvedValue{} 279 } 280 if r.Index != 0 { 281 // This should not happen, NeedsExtract should always be set. 282 panic("NeedsExtract is false, but return value index is non-zero") 283 } 284 // Resolve on the single return. 285 return makeResolvedValue(rv.(ssa.Value), r.FieldList) 286 } 287 288 // resolveStatic implements functionGuardResolver.resolveStatic. 289 func (r *returnGuard) resolveStatic(_ *passContext, _ *lockState, _ *ssa.Function, rv any) resolvedValue { 290 return r.resolveCommon(rv) 291 } 292 293 // resolveCall implements functionGuardResolver.resolveCall. 294 func (r *returnGuard) resolveCall(_ *passContext, _ *lockState, _ []ssa.Value, rv ssa.Value) resolvedValue { 295 return r.resolveCommon(rv) 296 } 297 298 // functionGuardInfo is information about a method guard. 299 type functionGuardInfo struct { 300 // Resolver is the resolver for this guard. 301 Resolver functionGuardResolver 302 303 // IsAlias indicates that this guard is an alias. 304 IsAlias bool 305 306 // Exclusive indicates an exclusive lock is required. 307 Exclusive bool 308 } 309 310 // lockFunctionFacts apply on every method. 311 type lockFunctionFacts struct { 312 // HeldOnEntry tracks the names and number of parameter (including receiver) 313 // lockFuncfields that guard calls to this function. 314 // 315 // The key is the name specified in the checklocks annotation. e.g given 316 // the following code: 317 // 318 // ``` 319 // type A struct { 320 // mu sync.Mutex 321 // a int 322 // } 323 // 324 // // +checklocks:a.mu 325 // func xyz(a *A) {..} 326 // ``` 327 // 328 // '`+checklocks:a.mu' will result in an entry in this map as shown below. 329 // HeldOnEntry: {"a.mu" => {Resolver: ¶meterGuard{Index: 0}} 330 HeldOnEntry map[string]functionGuardInfo 331 332 // HeldOnExit tracks the locks that are expected to be held on exit. 333 HeldOnExit map[string]functionGuardInfo 334 335 // Ignore means this function has local analysis ignores. 336 // 337 // This is not used outside the local package. 338 Ignore bool 339 } 340 341 // AFact implements analysis.Fact.AFact. 342 func (*lockFunctionFacts) AFact() {} 343 344 // checkGuard validates the guardName. 345 func (lff *lockFunctionFacts) checkGuard(pc *passContext, d *ast.FuncDecl, guardName string, exclusive bool, allowReturn bool) (functionGuardInfo, bool) { 346 if _, ok := lff.HeldOnEntry[guardName]; ok { 347 pc.maybeFail(d.Pos(), "annotation %s specified more than once, already required", guardName) 348 return functionGuardInfo{}, false 349 } 350 if _, ok := lff.HeldOnExit[guardName]; ok { 351 pc.maybeFail(d.Pos(), "annotation %s specified more than once, already acquired", guardName) 352 return functionGuardInfo{}, false 353 } 354 fg, ok := pc.findFunctionGuard(d, guardName, exclusive, allowReturn) 355 return fg, ok 356 } 357 358 // addGuardedBy adds a field to both HeldOnEntry and HeldOnExit. 359 func (lff *lockFunctionFacts) addGuardedBy(pc *passContext, d *ast.FuncDecl, guardName string, exclusive bool) { 360 if fg, ok := lff.checkGuard(pc, d, guardName, exclusive, false /* allowReturn */); ok { 361 if lff.HeldOnEntry == nil { 362 lff.HeldOnEntry = make(map[string]functionGuardInfo) 363 } 364 if lff.HeldOnExit == nil { 365 lff.HeldOnExit = make(map[string]functionGuardInfo) 366 } 367 lff.HeldOnEntry[guardName] = fg 368 lff.HeldOnExit[guardName] = fg 369 } 370 } 371 372 // addAcquires adds a field to HeldOnExit. 373 func (lff *lockFunctionFacts) addAcquires(pc *passContext, d *ast.FuncDecl, guardName string, exclusive bool) { 374 if fg, ok := lff.checkGuard(pc, d, guardName, exclusive, true /* allowReturn */); ok { 375 if lff.HeldOnExit == nil { 376 lff.HeldOnExit = make(map[string]functionGuardInfo) 377 } 378 lff.HeldOnExit[guardName] = fg 379 } 380 } 381 382 // addReleases adds a field to HeldOnEntry. 383 func (lff *lockFunctionFacts) addReleases(pc *passContext, d *ast.FuncDecl, guardName string, exclusive bool) { 384 if fg, ok := lff.checkGuard(pc, d, guardName, exclusive, false /* allowReturn */); ok { 385 if lff.HeldOnEntry == nil { 386 lff.HeldOnEntry = make(map[string]functionGuardInfo) 387 } 388 lff.HeldOnEntry[guardName] = fg 389 } 390 } 391 392 // addAlias adds an alias. 393 func (lff *lockFunctionFacts) addAlias(pc *passContext, d *ast.FuncDecl, guardName string) { 394 // Parse the alias. 395 parts := strings.Split(guardName, "=") 396 if len(parts) != 2 { 397 pc.maybeFail(d.Pos(), "invalid annotation %s for alias", guardName) 398 return 399 } 400 401 // Parse the actual guard. 402 fg, ok := lff.checkGuard(pc, d, parts[0], true /* exclusive */, true /* allowReturn */) 403 if !ok { 404 return 405 } 406 fg.IsAlias = true 407 408 // Find the existing specification. 409 _, entryOk := lff.HeldOnEntry[parts[1]] 410 if entryOk { 411 lff.HeldOnEntry[guardName] = fg 412 } 413 _, exitOk := lff.HeldOnExit[parts[1]] 414 if exitOk { 415 lff.HeldOnExit[guardName] = fg 416 } 417 if !entryOk && !exitOk { 418 pc.maybeFail(d.Pos(), "alias annotation %s does not refer to an existing guard", guardName) 419 } 420 } 421 422 // fieldEntryFor returns the fieldList value for the given object. 423 func (pc *passContext) fieldEntryFor(fieldObj types.Object, index int) fieldEntry { 424 425 // Return the resolution path. 426 if _, ok := fieldObj.Type().Underlying().(*types.Pointer); ok { 427 return fieldStructPtr(index) 428 } 429 if _, ok := fieldObj.Type().Underlying().(*types.Interface); ok { 430 return fieldStructPtr(index) 431 } 432 return fieldStruct(index) 433 } 434 435 // findField resolves a field in a single struct. 436 func (pc *passContext) findField(structType *types.Struct, fieldName string) (fl fieldList, fieldObj types.Object, ok bool) { 437 // Scan to match the next field. 438 for i := 0; i < structType.NumFields(); i++ { 439 fieldObj := structType.Field(i) 440 if fieldObj.Name() != fieldName { 441 continue 442 } 443 fl = append(fl, pc.fieldEntryFor(fieldObj, i)) 444 return fl, fieldObj, true 445 } 446 447 // Is this an embed? 448 for i := 0; i < structType.NumFields(); i++ { 449 fieldObj := structType.Field(i) 450 if !fieldObj.Embedded() { 451 continue 452 } 453 454 // Is this an embedded struct? 455 structType, ok := resolveStruct(fieldObj.Type()) 456 if !ok { 457 continue 458 } 459 460 // Need to check that there is a resolution path. If there is 461 // no resolution path that's not a failure: we just continue 462 // scanning the next embed to find a match. 463 flEmbed := pc.fieldEntryFor(fieldObj, i) 464 flNext, fieldObjNext, ok := pc.findField(structType, fieldName) 465 if !ok { 466 continue 467 } 468 469 // Found an embedded chain. 470 fl = append(fl, flEmbed) 471 fl = append(fl, flNext...) 472 return fl, fieldObjNext, true 473 } 474 475 return nil, nil, false 476 } 477 478 var ( 479 mutexRE = regexp.MustCompile(".*Mutex") 480 rwMutexRE = regexp.MustCompile(".*RWMutex") 481 lockerRE = regexp.MustCompile(".*sync.Locker") 482 ) 483 484 // validateMutex validates the mutex type. 485 // 486 // This function returns true iff the object is a valid mutex with an error 487 // reported at the given position if necessary. 488 func (pc *passContext) validateMutex(pos token.Pos, obj types.Object, exclusive bool) bool { 489 // Check that it is indeed a mutex. 490 s := obj.Type().String() 491 switch { 492 case rwMutexRE.MatchString(s): 493 // Safe for all cases. 494 return true 495 case mutexRE.MatchString(s), lockerRE.MatchString(s): 496 // Safe for exclusive cases. 497 if !exclusive { 498 pc.maybeFail(pos, "field %s must be a RWMutex", obj.Name()) 499 return false 500 } 501 return true 502 default: 503 // Not a mutex at all? 504 pc.maybeFail(pos, "field %s is not a Mutex or an RWMutex", obj.Name()) 505 return false 506 } 507 } 508 509 // findFieldList resolves a set of fields given a string, such a 'a.b.c'. 510 // 511 // Note that parts must be non-zero in length. If it may be zero, then 512 // maybeFindFieldList should be used instead with an appropriate object. 513 func (pc *passContext) findFieldList(pos token.Pos, structType *types.Struct, parts []string, exclusive bool) (fl fieldList, ok bool) { 514 var obj types.Object 515 516 // This loop requires at least one iteration in order to ensure that 517 // obj above is non-nil, and the type can be validated. 518 for i, fieldName := range parts { 519 flOne, fieldObj, ok := pc.findField(structType, fieldName) 520 if !ok { 521 return nil, false 522 } 523 fl = append(fl, flOne...) 524 obj = fieldObj 525 if i < len(parts)-1 { 526 structType, ok = resolveStruct(obj.Type()) 527 if !ok { 528 // N.B. This is associated with the original position. 529 pc.maybeFail(pos, "field %s expected to be struct", fieldName) 530 return nil, false 531 } 532 } 533 } 534 535 // Validate the final field. This reports the field to the caller 536 // anyways, since the error will be reported only once. 537 _ = pc.validateMutex(pos, obj, exclusive) 538 return fl, true 539 } 540 541 // maybeFindFieldList resolves the given object. 542 // 543 // Parts may be the empty list, unlike findFieldList. 544 func (pc *passContext) maybeFindFieldList(pos token.Pos, obj types.Object, parts []string, exclusive bool) (fl fieldList, ok bool) { 545 if len(parts) > 0 { 546 structType, ok := resolveStruct(obj.Type()) 547 if !ok { 548 // This does not have any fields; the access is not allowed. 549 pc.maybeFail(pos, "attempted field access on non-struct") 550 return nil, false 551 } 552 return pc.findFieldList(pos, structType, parts, exclusive) 553 } 554 555 // See above. 556 _ = pc.validateMutex(pos, obj, exclusive) 557 return nil, true 558 } 559 560 // findFieldGuardResolver finds a symbol resolver. 561 type findFieldGuardResolver func(pos token.Pos, guardName string) (fieldGuardResolver, bool) 562 563 // findFunctionGuardResolver finds a symbol resolver. 564 type findFunctionGuardResolver func(pos token.Pos, guardName string) (functionGuardResolver, bool) 565 566 // fillLockGuardFacts fills the facts with guard information. 567 func (pc *passContext) fillLockGuardFacts(obj types.Object, cg *ast.CommentGroup, find findFieldGuardResolver, lgf *lockGuardFacts) { 568 if cg == nil { 569 return 570 } 571 for _, l := range cg.List { 572 pc.extractAnnotations(l.Text, map[string]func(string){ 573 checkAtomicAnnotation: func(string) { 574 switch lgf.AtomicDisposition { 575 case atomicRequired: 576 pc.maybeFail(obj.Pos(), "annotation is redundant, already atomic required") 577 case atomicIgnore: 578 pc.maybeFail(obj.Pos(), "annotation is contradictory, already atomic ignored") 579 } 580 lgf.AtomicDisposition = atomicRequired 581 }, 582 checkLocksIgnore: func(string) { 583 switch lgf.AtomicDisposition { 584 case atomicIgnore: 585 pc.maybeFail(obj.Pos(), "annotation is redundant, already atomic ignored") 586 case atomicRequired: 587 pc.maybeFail(obj.Pos(), "annotation is contradictory, already atomic required") 588 } 589 lgf.AtomicDisposition = atomicIgnore 590 }, 591 checkLocksAnnotation: func(guardName string) { 592 // Check for a duplicate annotation. 593 if _, ok := lgf.GuardedBy[guardName]; ok { 594 pc.maybeFail(obj.Pos(), "annotation %s specified more than once", guardName) 595 return 596 } 597 // Add the item. 598 if lgf.GuardedBy == nil { 599 lgf.GuardedBy = make(map[string]fieldGuardResolver) 600 } 601 fr, ok := find(obj.Pos(), guardName) 602 if !ok { 603 pc.maybeFail(obj.Pos(), "annotation %s cannot be resolved", guardName) 604 return 605 } 606 lgf.GuardedBy[guardName] = fr 607 }, 608 // N.B. We support only the vanilla annotation on 609 // individual fields. If the field is a read lock, then 610 // we will allow read access by default. 611 checkLocksAnnotationRead: func(guardName string) { 612 pc.maybeFail(obj.Pos(), "annotation %s not legal on fields", guardName) 613 }, 614 }) 615 } 616 // Save only if there is something meaningful. 617 if len(lgf.GuardedBy) > 0 || lgf.AtomicDisposition != atomicDisallow { 618 pc.pass.ExportObjectFact(obj, lgf) 619 } 620 } 621 622 // findGlobalGuard attempts to resolve a name globally. 623 func (pc *passContext) findGlobalGuard(pos token.Pos, guardName string) (*globalGuard, bool) { 624 // Attempt to resolve the object. 625 parts := strings.Split(guardName, ".") 626 globalObj := pc.pass.Pkg.Scope().Lookup(parts[0]) 627 if globalObj == nil { 628 // No global object. 629 return nil, false 630 } 631 fl, ok := pc.maybeFindFieldList(pos, globalObj, parts[1:], true /* exclusive */) 632 if !ok { 633 // Invalid fields. 634 return nil, false 635 } 636 return &globalGuard{ 637 ObjectName: parts[0], 638 PackageName: pc.pass.Pkg.Path(), 639 FieldList: fl, 640 }, true 641 } 642 643 // findGlobalFieldGuard is compatible with findFieldGuardResolver. 644 func (pc *passContext) findGlobalFieldGuard(pos token.Pos, guardName string) (fieldGuardResolver, bool) { 645 g, ok := pc.findGlobalGuard(pos, guardName) 646 return g, ok 647 } 648 649 // findGlobalFunctionGuard is compatible with findFunctionGuardResolver. 650 func (pc *passContext) findGlobalFunctionGuard(pos token.Pos, guardName string) (functionGuardResolver, bool) { 651 g, ok := pc.findGlobalGuard(pos, guardName) 652 return g, ok 653 } 654 655 // structLockGuardFacts finds all relevant guard information for structures. 656 func (pc *passContext) structLockGuardFacts(structType *types.Struct, ss *ast.StructType) { 657 var fieldObj *types.Var 658 findLocal := func(pos token.Pos, guardName string) (fieldGuardResolver, bool) { 659 // Try to resolve from the local structure first. 660 fl, ok := pc.findFieldList(pos, structType, strings.Split(guardName, "."), true /* exclusive */) 661 if ok { 662 // Found a valid resolution. 663 return &fieldGuard{ 664 FieldList: fl, 665 }, true 666 } 667 // Attempt a global resolution. 668 return pc.findGlobalFieldGuard(pos, guardName) 669 } 670 for i, field := range ss.Fields.List { 671 var lgf lockGuardFacts 672 fieldObj = structType.Field(i) // N.B. Captured above. 673 pc.fillLockGuardFacts(fieldObj, field.Doc, findLocal, &lgf) 674 675 // See above, for anonymous structure fields. 676 if ss, ok := field.Type.(*ast.StructType); ok { 677 if st, ok := fieldObj.Type().(*types.Struct); ok { 678 pc.structLockGuardFacts(st, ss) 679 } 680 } 681 } 682 } 683 684 // globalLockGuardFacts finds all relevant guard information for globals. 685 // 686 // Note that the Type is checked in checklocks.go at the top-level. 687 func (pc *passContext) globalLockGuardFacts(vs *ast.ValueSpec) { 688 var lgf lockGuardFacts 689 globalObj := pc.pass.TypesInfo.ObjectOf(vs.Names[0]) 690 pc.fillLockGuardFacts(globalObj, vs.Doc, pc.findGlobalFieldGuard, &lgf) 691 } 692 693 // countFields gives an accurate field count, according for unnamed arguments 694 // and return values and the compact identifier format. 695 func countFields(fl []*ast.Field) (count int) { 696 for _, field := range fl { 697 if len(field.Names) == 0 { 698 count++ 699 continue 700 } 701 count += len(field.Names) 702 } 703 return 704 } 705 706 // matchFieldList attempts to match the given field. 707 // 708 // This function may or may not report an error. This is indicated in the 709 // reported return value. If reported is true, then the specification is 710 // ambiguous or not valid, and should be propagated. 711 func (pc *passContext) matchFieldList(pos token.Pos, fields []*ast.Field, guardName string, exclusive bool) (number int, fl fieldList, reported, ok bool) { 712 parts := strings.Split(guardName, ".") 713 firstName := parts[0] 714 index := 0 715 for _, field := range fields { 716 // See countFields, above. 717 if len(field.Names) == 0 { 718 index++ 719 continue 720 } 721 for _, name := range field.Names { 722 if name.Name != firstName { 723 index++ 724 continue 725 } 726 obj := pc.pass.TypesInfo.ObjectOf(name) 727 fl, ok := pc.maybeFindFieldList(pos, obj, parts[1:], exclusive) 728 if !ok { 729 // Some intermediate name does not match. The 730 // resolveField function will not report. 731 pc.maybeFail(pos, "name %s does not resolve to a field", guardName) 732 return 0, nil, true, false 733 } 734 // Successfully found a field. 735 return index, fl, false, true 736 } 737 } 738 739 // Nothing matching. 740 return 0, nil, false, false 741 } 742 743 // findFunctionGuard identifies the parameter number and field number for a 744 // particular string of the 'a.b'. 745 // 746 // This function will report any errors directly. 747 func (pc *passContext) findFunctionGuard(d *ast.FuncDecl, guardName string, exclusive bool, allowReturn bool) (functionGuardInfo, bool) { 748 // Match against receiver & parameters. 749 var parameterList []*ast.Field 750 if d.Recv != nil { 751 parameterList = append(parameterList, d.Recv.List...) 752 } 753 if d.Type.Params != nil { 754 parameterList = append(parameterList, d.Type.Params.List...) 755 } 756 if index, fl, reported, ok := pc.matchFieldList(d.Pos(), parameterList, guardName, exclusive); reported || ok { 757 if !ok { 758 return functionGuardInfo{}, false 759 } 760 return functionGuardInfo{ 761 Resolver: ¶meterGuard{ 762 Index: index, 763 FieldList: fl, 764 }, 765 Exclusive: exclusive, 766 }, true 767 } 768 769 // Match against return values, if allowed. 770 if allowReturn { 771 var returnList []*ast.Field 772 if d.Type.Results != nil { 773 returnList = append(returnList, d.Type.Results.List...) 774 } 775 if index, fl, reported, ok := pc.matchFieldList(d.Pos(), returnList, guardName, exclusive); reported || ok { 776 if !ok { 777 return functionGuardInfo{}, false 778 } 779 return functionGuardInfo{ 780 Resolver: &returnGuard{ 781 Index: index, 782 FieldList: fl, 783 NeedsExtract: countFields(returnList) > 1, 784 }, 785 Exclusive: exclusive, 786 }, true 787 } 788 } 789 790 // Match against globals. 791 if g, ok := pc.findGlobalFunctionGuard(d.Pos(), guardName); ok { 792 return functionGuardInfo{ 793 Resolver: g, 794 Exclusive: exclusive, 795 }, true 796 } 797 798 // No match found. 799 pc.maybeFail(d.Pos(), "annotation %s does not have a match any parameter, return value or global", guardName) 800 return functionGuardInfo{}, false 801 } 802 803 // functionFacts exports relevant function findings. 804 func (pc *passContext) functionFacts(d *ast.FuncDecl) { 805 // Extract guard information. 806 if d.Doc == nil || d.Doc.List == nil { 807 return 808 } 809 var lff lockFunctionFacts 810 for _, l := range d.Doc.List { 811 pc.extractAnnotations(l.Text, map[string]func(string){ 812 checkLocksIgnore: func(string) { 813 // Note that this applies to all atomic 814 // analysis as well. There is no provided way 815 // to selectively ignore only lock analysis or 816 // atomic analysis, as we expect this use to be 817 // extremely rare. 818 lff.Ignore = true 819 }, 820 checkLocksAnnotation: func(guardName string) { lff.addGuardedBy(pc, d, guardName, true /* exclusive */) }, 821 checkLocksAnnotationRead: func(guardName string) { lff.addGuardedBy(pc, d, guardName, false /* exclusive */) }, 822 checkLocksAcquires: func(guardName string) { lff.addAcquires(pc, d, guardName, true /* exclusive */) }, 823 checkLocksAcquiresRead: func(guardName string) { lff.addAcquires(pc, d, guardName, false /* exclusive */) }, 824 checkLocksReleases: func(guardName string) { lff.addReleases(pc, d, guardName, true /* exclusive */) }, 825 checkLocksReleasesRead: func(guardName string) { lff.addReleases(pc, d, guardName, false /* exclusive */) }, 826 checkLocksAlias: func(guardName string) { lff.addAlias(pc, d, guardName) }, 827 }) 828 } 829 830 // Export the function facts if there is anything to save. 831 if lff.Ignore || len(lff.HeldOnEntry) > 0 || len(lff.HeldOnExit) > 0 { 832 funcObj := pc.pass.TypesInfo.Defs[d.Name].(*types.Func) 833 pc.pass.ExportObjectFact(funcObj, &lff) 834 } 835 } 836 837 func init() { 838 gob.Register((*returnGuard)(nil)) 839 gob.Register((*globalGuard)(nil)) 840 gob.Register((*parameterGuard)(nil)) 841 gob.Register((*fieldGuard)(nil)) 842 gob.Register((*fieldStructPtr)(nil)) 843 gob.Register((*fieldStruct)(nil)) 844 }