github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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 "fmt" 19 "go/ast" 20 "go/token" 21 "go/types" 22 "regexp" 23 "strings" 24 25 "golang.org/x/tools/go/ssa" 26 ) 27 28 // atomicAlignment is saved per type. 29 // 30 // This represents the alignment required for the type, which may 31 // be implied and imposed by other types within the aggregate type. 32 type atomicAlignment int 33 34 // AFact implements analysis.Fact.AFact. 35 func (*atomicAlignment) AFact() {} 36 37 // atomicDisposition is saved per field. 38 // 39 // This represents how the field must be accessed. It must either 40 // be non-atomic (default), atomic or ignored. 41 type atomicDisposition int 42 43 const ( 44 atomicDisallow atomicDisposition = iota 45 atomicIgnore 46 atomicRequired 47 ) 48 49 // fieldList is a simple list of fields, used in two types below. 50 // 51 // Note that the integers in this list refer to one of two things: 52 // - A positive integer refers to a field index in a struct. 53 // - A negative integer refers to a field index in a struct, where 54 // that field is a pointer and must be subsequently resolved. 55 type fieldList []int 56 57 // resolvedValue is an ssa.Value with additional fields. 58 // 59 // This can be resolved to a string as part of a lock state. 60 type resolvedValue struct { 61 value ssa.Value 62 valid bool 63 fieldList []int 64 } 65 66 // findExtract finds a relevant extract. This must exist within the referrers 67 // to the call object. If this doesn't then the object which is locked is never 68 // consumed, and we should consider this a bug. 69 func findExtract(v ssa.Value, index int) (ssa.Value, bool) { 70 if refs := v.Referrers(); refs != nil { 71 for _, inst := range *refs { 72 if x, ok := inst.(*ssa.Extract); ok && x.Tuple == v && x.Index == index { 73 return inst.(ssa.Value), true 74 } 75 } 76 } 77 return nil, false 78 } 79 80 // resolve resolves the given field list. 81 func (fl fieldList) resolve(v ssa.Value) (rv resolvedValue) { 82 return resolvedValue{ 83 value: v, 84 fieldList: fl, 85 valid: true, 86 } 87 } 88 89 // valueAsString returns a string representing this value. 90 // 91 // This must align with how the string is generated in valueAsString. 92 func (rv resolvedValue) valueAsString(ls *lockState) string { 93 typ := rv.value.Type() 94 s := ls.valueAsString(rv.value) 95 for i, fieldNumber := range rv.fieldList { 96 switch { 97 case fieldNumber > 0: 98 field, ok := findField(typ, fieldNumber-1) 99 if !ok { 100 // This can't be resolved, return for debugging. 101 return fmt.Sprintf("{%s+%v}", s, rv.fieldList[i:]) 102 } 103 s = fmt.Sprintf("&(%s.%s)", s, field.Name()) 104 typ = field.Type() 105 case fieldNumber < 1: 106 field, ok := findField(typ, (-fieldNumber)-1) 107 if !ok { 108 // See above. 109 return fmt.Sprintf("{%s+%v}", s, rv.fieldList[i:]) 110 } 111 s = fmt.Sprintf("*(&(%s.%s))", s, field.Name()) 112 typ = field.Type() 113 } 114 } 115 return s 116 } 117 118 // lockFieldFacts apply on every struct field. 119 type lockFieldFacts struct { 120 // IsMutex is true if the field is of type sync.Mutex. 121 IsMutex bool 122 123 // IsRWMutex is true if the field is of type sync.RWMutex. 124 IsRWMutex bool 125 126 // IsPointer indicates if the field is a pointer. 127 IsPointer bool 128 129 // FieldNumber is the number of this field in the struct. 130 FieldNumber int 131 } 132 133 // AFact implements analysis.Fact.AFact. 134 func (*lockFieldFacts) AFact() {} 135 136 // lockGuardFacts contains guard information. 137 type lockGuardFacts struct { 138 // GuardedBy is the set of locks that are guarding this field. The key 139 // is the original annotation value, and the field list is the object 140 // traversal path. 141 GuardedBy map[string]fieldList 142 143 // AtomicDisposition is the disposition for this field. Note that this 144 // can affect the interpretation of the GuardedBy field above, see the 145 // relevant comment. 146 AtomicDisposition atomicDisposition 147 } 148 149 // AFact implements analysis.Fact.AFact. 150 func (*lockGuardFacts) AFact() {} 151 152 // functionGuard is used by lockFunctionFacts, below. 153 type functionGuard struct { 154 // ParameterNumber is the index of the object that contains the 155 // guarding mutex. From this parameter, a walk is performed 156 // subsequently using the resolve method. 157 // 158 // Note that is ParameterNumber is beyond the size of parameters, then 159 // it may return to a return value. This applies only for the Acquires 160 // relation below. 161 ParameterNumber int 162 163 // NeedsExtract is used in the case of a return value, and indicates 164 // that the field must be extracted from a tuple. 165 NeedsExtract bool 166 167 // FieldList is the traversal path to the object. 168 FieldList fieldList 169 } 170 171 // resolveReturn resolves a return value. 172 // 173 // Precondition: rv is either an ssa.Value, or an *ssa.Return. 174 func (fg *functionGuard) resolveReturn(rv interface{}, args int) resolvedValue { 175 if rv == nil { 176 // For defers and other objects, this may be nil. This is 177 // handled in state.go in the actual lock checking logic. 178 return resolvedValue{ 179 value: nil, 180 valid: false, 181 } 182 } 183 index := fg.ParameterNumber - args 184 // If this is a *ssa.Return object, i.e. we are analyzing the function 185 // and not the call site, then we can just pull the result directly. 186 if r, ok := rv.(*ssa.Return); ok { 187 return fg.FieldList.resolve(r.Results[index]) 188 } 189 if fg.NeedsExtract { 190 // Resolve on the extracted field, this is necessary if the 191 // type here is not an explicit return. Note that rv must be an 192 // ssa.Value, since it is not an *ssa.Return. 193 v, ok := findExtract(rv.(ssa.Value), index) 194 if !ok { 195 return resolvedValue{ 196 value: v, 197 valid: false, 198 } 199 } 200 return fg.FieldList.resolve(v) 201 } 202 if index != 0 { 203 // This should not happen, NeedsExtract should always be set. 204 panic("NeedsExtract is false, but return value index is non-zero") 205 } 206 // Resolve on the single return. 207 return fg.FieldList.resolve(rv.(ssa.Value)) 208 } 209 210 // resolveStatic returns an ssa.Value representing the given field. 211 // 212 // Precondition: per resolveReturn. 213 func (fg *functionGuard) resolveStatic(fn *ssa.Function, rv interface{}) resolvedValue { 214 if fg.ParameterNumber >= len(fn.Params) { 215 return fg.resolveReturn(rv, len(fn.Params)) 216 } 217 return fg.FieldList.resolve(fn.Params[fg.ParameterNumber]) 218 } 219 220 // resolveCall returns an ssa.Value representing the given field. 221 func (fg *functionGuard) resolveCall(args []ssa.Value, rv ssa.Value) resolvedValue { 222 if fg.ParameterNumber >= len(args) { 223 return fg.resolveReturn(rv, len(args)) 224 } 225 return fg.FieldList.resolve(args[fg.ParameterNumber]) 226 } 227 228 // lockFunctionFacts apply on every method. 229 type lockFunctionFacts struct { 230 // HeldOnEntry tracks the names and number of parameter (including receiver) 231 // lockFuncfields that guard calls to this function. 232 // 233 // The key is the name specified in the checklocks annotation. e.g given 234 // the following code: 235 // 236 // ``` 237 // type A struct { 238 // mu sync.Mutex 239 // a int 240 // } 241 // 242 // // +checklocks:a.mu 243 // func xyz(a *A) {..} 244 // ``` 245 // 246 // '`+checklocks:a.mu' will result in an entry in this map as shown below. 247 // HeldOnEntry: {"a.mu" => {ParameterNumber: 0, FieldNumbers: {0}} 248 // 249 // Unlikely lockFieldFacts, there is no atomic interpretation. 250 HeldOnEntry map[string]functionGuard 251 252 // HeldOnExit tracks the locks that are expected to be held on exit. 253 HeldOnExit map[string]functionGuard 254 255 // Ignore means this function has local analysis ignores. 256 // 257 // This is not used outside the local package. 258 Ignore bool 259 } 260 261 // AFact implements analysis.Fact.AFact. 262 func (*lockFunctionFacts) AFact() {} 263 264 // checkGuard validates the guardName. 265 func (lff *lockFunctionFacts) checkGuard(pc *passContext, d *ast.FuncDecl, guardName string, allowReturn bool) (functionGuard, bool) { 266 if _, ok := lff.HeldOnEntry[guardName]; ok { 267 pc.maybeFail(d.Pos(), "annotation %s specified more than once, already required", guardName) 268 return functionGuard{}, false 269 } 270 if _, ok := lff.HeldOnExit[guardName]; ok { 271 pc.maybeFail(d.Pos(), "annotation %s specified more than once, already acquired", guardName) 272 return functionGuard{}, false 273 } 274 fg, ok := pc.findFunctionGuard(d, guardName, allowReturn) 275 return fg, ok 276 } 277 278 // addGuardedBy adds a field to both HeldOnEntry and HeldOnExit. 279 func (lff *lockFunctionFacts) addGuardedBy(pc *passContext, d *ast.FuncDecl, guardName string) { 280 if fg, ok := lff.checkGuard(pc, d, guardName, false /* allowReturn */); ok { 281 if lff.HeldOnEntry == nil { 282 lff.HeldOnEntry = make(map[string]functionGuard) 283 } 284 if lff.HeldOnExit == nil { 285 lff.HeldOnExit = make(map[string]functionGuard) 286 } 287 lff.HeldOnEntry[guardName] = fg 288 lff.HeldOnExit[guardName] = fg 289 } 290 } 291 292 // addAcquires adds a field to HeldOnExit. 293 func (lff *lockFunctionFacts) addAcquires(pc *passContext, d *ast.FuncDecl, guardName string) { 294 if fg, ok := lff.checkGuard(pc, d, guardName, true /* allowReturn */); ok { 295 if lff.HeldOnExit == nil { 296 lff.HeldOnExit = make(map[string]functionGuard) 297 } 298 lff.HeldOnExit[guardName] = fg 299 } 300 } 301 302 // addReleases adds a field to HeldOnEntry. 303 func (lff *lockFunctionFacts) addReleases(pc *passContext, d *ast.FuncDecl, guardName string) { 304 if fg, ok := lff.checkGuard(pc, d, guardName, false /* allowReturn */); ok { 305 if lff.HeldOnEntry == nil { 306 lff.HeldOnEntry = make(map[string]functionGuard) 307 } 308 lff.HeldOnEntry[guardName] = fg 309 } 310 } 311 312 // fieldListFor returns the fieldList for the given object. 313 func (pc *passContext) fieldListFor(pos token.Pos, fieldObj types.Object, index int, fieldName string, checkMutex bool) (int, bool) { 314 var lff lockFieldFacts 315 if !pc.pass.ImportObjectFact(fieldObj, &lff) { 316 // This should not happen: we export facts for all fields. 317 panic(fmt.Sprintf("no lockFieldFacts available for field %s", fieldName)) 318 } 319 // Check that it is indeed a mutex. 320 if checkMutex && !lff.IsMutex && !lff.IsRWMutex { 321 pc.maybeFail(pos, "field %s is not a mutex or an rwmutex", fieldName) 322 return 0, false 323 } 324 // Return the resolution path. 325 if lff.IsPointer { 326 return -(index + 1), true 327 } 328 return (index + 1), true 329 } 330 331 // resolveOneField resolves a field in a single struct. 332 func (pc *passContext) resolveOneField(pos token.Pos, structType *types.Struct, fieldName string, checkMutex bool) (fl fieldList, fieldObj types.Object, ok bool) { 333 // Scan to match the next field. 334 for i := 0; i < structType.NumFields(); i++ { 335 fieldObj := structType.Field(i) 336 if fieldObj.Name() != fieldName { 337 continue 338 } 339 flOne, ok := pc.fieldListFor(pos, fieldObj, i, fieldName, checkMutex) 340 if !ok { 341 return nil, nil, false 342 } 343 fl = append(fl, flOne) 344 return fl, fieldObj, true 345 } 346 // Is this an embed? 347 for i := 0; i < structType.NumFields(); i++ { 348 fieldObj := structType.Field(i) 349 if !fieldObj.Embedded() { 350 continue 351 } 352 // Is this an embedded struct? 353 structType, ok := resolveStruct(fieldObj.Type()) 354 if !ok { 355 continue 356 } 357 // Need to check that there is a resolution path. If there is 358 // no resolution path that's not a failure: we just continue 359 // scanning the next embed to find a match. 360 flEmbed, okEmbed := pc.fieldListFor(pos, fieldObj, i, fieldName, false) 361 flCont, fieldObjCont, okCont := pc.resolveOneField(pos, structType, fieldName, checkMutex) 362 if okEmbed && okCont { 363 fl = append(fl, flEmbed) 364 fl = append(fl, flCont...) 365 return fl, fieldObjCont, true 366 } 367 } 368 pc.maybeFail(pos, "field %s does not exist", fieldName) 369 return nil, nil, false 370 } 371 372 // resolveField resolves a set of fields given a string, such a 'a.b.c'. 373 // 374 // Note that this checks that the final element is a mutex of some kind, and 375 // will fail appropriately. 376 func (pc *passContext) resolveField(pos token.Pos, structType *types.Struct, parts []string) (fl fieldList, ok bool) { 377 for partNumber, fieldName := range parts { 378 flOne, fieldObj, ok := pc.resolveOneField(pos, structType, fieldName, partNumber >= len(parts)-1 /* checkMutex */) 379 if !ok { 380 // Error already reported. 381 return nil, false 382 } 383 fl = append(fl, flOne...) 384 if partNumber < len(parts)-1 { 385 // Traverse to the next type. 386 structType, ok = resolveStruct(fieldObj.Type()) 387 if !ok { 388 pc.maybeFail(pos, "invalid intermediate field %s", fieldName) 389 return fl, false 390 } 391 } 392 } 393 return fl, true 394 } 395 396 var ( 397 mutexRE = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineMutex|Mutex)") 398 rwMutexRE = regexp.MustCompile("((.*/)|^)sync.(CrossGoroutineRWMutex|RWMutex)") 399 ) 400 401 // exportLockFieldFacts finds all struct fields that are mutexes, and ensures 402 // that they are annotated approperly. 403 // 404 // This information is consumed subsequently by exportLockGuardFacts, and this 405 // function must be called first on all structures. 406 func (pc *passContext) exportLockFieldFacts(ts *ast.TypeSpec, ss *ast.StructType) { 407 structType := pc.pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct) 408 for i := range ss.Fields.List { 409 lff := &lockFieldFacts{ 410 FieldNumber: i, 411 } 412 // We use HasSuffix below because fieldType can be fully 413 // qualified with the package name eg for the gvisor sync 414 // package mutex fields have the type: 415 // "<package path>/sync/sync.Mutex" 416 fieldObj := structType.Field(i) 417 s := fieldObj.Type().String() 418 switch { 419 case mutexRE.MatchString(s): 420 lff.IsMutex = true 421 case rwMutexRE.MatchString(s): 422 lff.IsRWMutex = true 423 } 424 // Save whether this is a pointer. 425 _, lff.IsPointer = fieldObj.Type().Underlying().(*types.Pointer) 426 // We must always export the lockFieldFacts, since traversal 427 // can take place along any object in the struct. 428 pc.pass.ExportObjectFact(fieldObj, lff) 429 } 430 } 431 432 // exportLockGuardFacts finds all relevant guard information for structures. 433 // 434 // This function requires exportLockFieldFacts be called first on all 435 // structures. 436 func (pc *passContext) exportLockGuardFacts(ts *ast.TypeSpec, ss *ast.StructType) { 437 structType := pc.pass.TypesInfo.TypeOf(ts.Name).Underlying().(*types.Struct) 438 for i, field := range ss.Fields.List { 439 if field.Doc == nil { 440 continue 441 } 442 var ( 443 lff lockFieldFacts 444 lgf lockGuardFacts 445 ) 446 pc.pass.ImportObjectFact(structType.Field(i), &lff) 447 fieldObj := structType.Field(i) 448 for _, l := range field.Doc.List { 449 pc.extractAnnotations(l.Text, map[string]func(string){ 450 checkAtomicAnnotation: func(string) { 451 switch lgf.AtomicDisposition { 452 case atomicRequired: 453 pc.maybeFail(fieldObj.Pos(), "annotation is redundant, already atomic required") 454 case atomicIgnore: 455 pc.maybeFail(fieldObj.Pos(), "annotation is contradictory, already atomic ignored") 456 } 457 lgf.AtomicDisposition = atomicRequired 458 }, 459 checkLocksIgnore: func(string) { 460 switch lgf.AtomicDisposition { 461 case atomicIgnore: 462 pc.maybeFail(fieldObj.Pos(), "annotation is redundant, already atomic ignored") 463 case atomicRequired: 464 pc.maybeFail(fieldObj.Pos(), "annotation is contradictory, already atomic required") 465 } 466 lgf.AtomicDisposition = atomicIgnore 467 }, 468 checkLocksAnnotation: func(guardName string) { 469 // Check for a duplicate annotation. 470 if _, ok := lgf.GuardedBy[guardName]; ok { 471 pc.maybeFail(fieldObj.Pos(), "annotation %s specified more than once", guardName) 472 return 473 } 474 fl, ok := pc.resolveField(fieldObj.Pos(), structType, strings.Split(guardName, ".")) 475 if ok { 476 // If we successfully resolved 477 // the field, then save it. 478 if lgf.GuardedBy == nil { 479 lgf.GuardedBy = make(map[string]fieldList) 480 } 481 lgf.GuardedBy[guardName] = fl 482 } 483 }, 484 }) 485 } 486 // Save only if there is something meaningful. 487 if len(lgf.GuardedBy) > 0 || lgf.AtomicDisposition != atomicDisallow { 488 pc.pass.ExportObjectFact(structType.Field(i), &lgf) 489 } 490 } 491 } 492 493 // countFields gives an accurate field count, according for unnamed arguments 494 // and return values and the compact identifier format. 495 func countFields(fl []*ast.Field) (count int) { 496 for _, field := range fl { 497 if len(field.Names) == 0 { 498 count++ 499 continue 500 } 501 count += len(field.Names) 502 } 503 return 504 } 505 506 // matchFieldList attempts to match the given field. 507 func (pc *passContext) matchFieldList(pos token.Pos, fl []*ast.Field, guardName string) (functionGuard, bool) { 508 parts := strings.Split(guardName, ".") 509 parameterName := parts[0] 510 parameterNumber := 0 511 for _, field := range fl { 512 // See countFields, above. 513 if len(field.Names) == 0 { 514 parameterNumber++ 515 continue 516 } 517 for _, name := range field.Names { 518 if name.Name != parameterName { 519 parameterNumber++ 520 continue 521 } 522 ptrType, ok := pc.pass.TypesInfo.TypeOf(field.Type).Underlying().(*types.Pointer) 523 if !ok { 524 // Since mutexes cannot be copied we only care 525 // about parameters that are pointer types when 526 // checking for guards. 527 pc.maybeFail(pos, "parameter name %s does not refer to a pointer type", parameterName) 528 return functionGuard{}, false 529 } 530 structType, ok := ptrType.Elem().Underlying().(*types.Struct) 531 if !ok { 532 // Fields can only be in named structures. 533 pc.maybeFail(pos, "parameter name %s does not refer to a pointer to a struct", parameterName) 534 return functionGuard{}, false 535 } 536 fg := functionGuard{ 537 ParameterNumber: parameterNumber, 538 } 539 fl, ok := pc.resolveField(pos, structType, parts[1:]) 540 fg.FieldList = fl 541 return fg, ok // If ok is false, already failed. 542 } 543 } 544 return functionGuard{}, false 545 } 546 547 // findFunctionGuard identifies the parameter number and field number for a 548 // particular string of the 'a.b'. 549 // 550 // This function will report any errors directly. 551 func (pc *passContext) findFunctionGuard(d *ast.FuncDecl, guardName string, allowReturn bool) (functionGuard, bool) { 552 var ( 553 parameterList []*ast.Field 554 returnList []*ast.Field 555 ) 556 if d.Recv != nil { 557 parameterList = append(parameterList, d.Recv.List...) 558 } 559 if d.Type.Params != nil { 560 parameterList = append(parameterList, d.Type.Params.List...) 561 } 562 if fg, ok := pc.matchFieldList(d.Pos(), parameterList, guardName); ok { 563 return fg, ok 564 } 565 if allowReturn { 566 if d.Type.Results != nil { 567 returnList = append(returnList, d.Type.Results.List...) 568 } 569 if fg, ok := pc.matchFieldList(d.Pos(), returnList, guardName); ok { 570 // Fix this up to apply to the return value, as noted 571 // in fg.ParameterNumber. For the ssa analysis, we must 572 // record whether this has multiple results, since 573 // *ssa.Call indicates: "The Call instruction yields 574 // the function result if there is exactly one. 575 // Otherwise it returns a tuple, the components of 576 // which are accessed via Extract." 577 fg.ParameterNumber += countFields(parameterList) 578 fg.NeedsExtract = countFields(returnList) > 1 579 return fg, ok 580 } 581 } 582 // We never saw a matching parameter. 583 pc.maybeFail(d.Pos(), "annotation %s does not have a matching parameter", guardName) 584 return functionGuard{}, false 585 } 586 587 // exportFunctionFacts exports relevant function findings. 588 func (pc *passContext) exportFunctionFacts(d *ast.FuncDecl) { 589 if d.Doc == nil || d.Doc.List == nil { 590 return 591 } 592 var lff lockFunctionFacts 593 for _, l := range d.Doc.List { 594 pc.extractAnnotations(l.Text, map[string]func(string){ 595 checkLocksIgnore: func(string) { 596 // Note that this applies to all atomic 597 // analysis as well. There is no provided way 598 // to selectively ignore only lock analysis or 599 // atomic analysis, as we expect this use to be 600 // extremely rare. 601 lff.Ignore = true 602 }, 603 checkLocksAnnotation: func(guardName string) { lff.addGuardedBy(pc, d, guardName) }, 604 checkLocksAcquires: func(guardName string) { lff.addAcquires(pc, d, guardName) }, 605 checkLocksReleases: func(guardName string) { lff.addReleases(pc, d, guardName) }, 606 }) 607 } 608 609 // Export the function facts if there is anything to save. 610 if lff.Ignore || len(lff.HeldOnEntry) > 0 || len(lff.HeldOnExit) > 0 { 611 funcObj := pc.pass.TypesInfo.Defs[d.Name].(*types.Func) 612 pc.pass.ExportObjectFact(funcObj, &lff) 613 } 614 }