github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/tools/checklocks/state.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/token"
    20  	"go/types"
    21  	"strings"
    22  	"sync/atomic"
    23  
    24  	"golang.org/x/tools/go/ssa"
    25  )
    26  
    27  // lockState tracks the locking state and aliases.
    28  type lockState struct {
    29  	// lockedMutexes is used to track which mutexes in a given struct are
    30  	// currently locked. Note that most of the heavy lifting is done by
    31  	// valueAsString below, which maps to specific structure fields, etc.
    32  	lockedMutexes []string
    33  
    34  	// stored stores values that have been stored in memory, bound to
    35  	// FreeVars or passed as Parameterse.
    36  	stored map[ssa.Value]ssa.Value
    37  
    38  	// used is a temporary map, used only for valueAsString. It prevents
    39  	// multiple use of the same memory location.
    40  	used map[ssa.Value]struct{}
    41  
    42  	// defers are the stack of defers that have been pushed.
    43  	defers []*ssa.Defer
    44  
    45  	// refs indicates the number of references on this structure. If it's
    46  	// greater than one, we will do copy-on-write.
    47  	refs *int32
    48  }
    49  
    50  // newLockState makes a new lockState.
    51  func newLockState() *lockState {
    52  	refs := int32(1) // Not shared.
    53  	return &lockState{
    54  		lockedMutexes: make([]string, 0),
    55  		used:          make(map[ssa.Value]struct{}),
    56  		stored:        make(map[ssa.Value]ssa.Value),
    57  		defers:        make([]*ssa.Defer, 0),
    58  		refs:          &refs,
    59  	}
    60  }
    61  
    62  // fork forks the locking state. When a lockState is forked, any modifications
    63  // will cause maps to be copied.
    64  func (l *lockState) fork() *lockState {
    65  	if l == nil {
    66  		return newLockState()
    67  	}
    68  	atomic.AddInt32(l.refs, 1)
    69  	return &lockState{
    70  		lockedMutexes: l.lockedMutexes,
    71  		used:          make(map[ssa.Value]struct{}),
    72  		stored:        l.stored,
    73  		defers:        l.defers,
    74  		refs:          l.refs,
    75  	}
    76  }
    77  
    78  // modify indicates that this state will be modified.
    79  func (l *lockState) modify() {
    80  	if atomic.LoadInt32(l.refs) > 1 {
    81  		// Copy the lockedMutexes.
    82  		lm := make([]string, len(l.lockedMutexes))
    83  		copy(lm, l.lockedMutexes)
    84  		l.lockedMutexes = lm
    85  
    86  		// Copy the stored values.
    87  		s := make(map[ssa.Value]ssa.Value)
    88  		for k, v := range l.stored {
    89  			s[k] = v
    90  		}
    91  		l.stored = s
    92  
    93  		// Reset the used values.
    94  		l.used = make(map[ssa.Value]struct{})
    95  
    96  		// Copy the defers.
    97  		ds := make([]*ssa.Defer, len(l.defers))
    98  		copy(ds, l.defers)
    99  		l.defers = ds
   100  
   101  		// Drop our reference.
   102  		atomic.AddInt32(l.refs, -1)
   103  		newRefs := int32(1) // Not shared.
   104  		l.refs = &newRefs
   105  	}
   106  }
   107  
   108  // isHeld indicates whether the field is held is not.
   109  func (l *lockState) isHeld(rv resolvedValue) (string, bool) {
   110  	if !rv.valid {
   111  		return rv.valueAsString(l), false
   112  	}
   113  	s := rv.valueAsString(l)
   114  	for _, k := range l.lockedMutexes {
   115  		if k == s {
   116  			return s, true
   117  		}
   118  	}
   119  	return s, false
   120  }
   121  
   122  // lockField locks the given field.
   123  //
   124  // If false is returned, the field was already locked.
   125  func (l *lockState) lockField(rv resolvedValue) (string, bool) {
   126  	if !rv.valid {
   127  		return rv.valueAsString(l), false
   128  	}
   129  	s := rv.valueAsString(l)
   130  	for _, k := range l.lockedMutexes {
   131  		if k == s {
   132  			return s, false
   133  		}
   134  	}
   135  	l.modify()
   136  	l.lockedMutexes = append(l.lockedMutexes, s)
   137  	return s, true
   138  }
   139  
   140  // unlockField unlocks the given field.
   141  //
   142  // If false is returned, the field was not locked.
   143  func (l *lockState) unlockField(rv resolvedValue) (string, bool) {
   144  	if !rv.valid {
   145  		return rv.valueAsString(l), false
   146  	}
   147  	s := rv.valueAsString(l)
   148  	for i, k := range l.lockedMutexes {
   149  		if k == s {
   150  			// Copy the last lock in and truncate.
   151  			l.modify()
   152  			l.lockedMutexes[i] = l.lockedMutexes[len(l.lockedMutexes)-1]
   153  			l.lockedMutexes = l.lockedMutexes[:len(l.lockedMutexes)-1]
   154  			return s, true
   155  		}
   156  	}
   157  	return s, false
   158  }
   159  
   160  // store records an alias.
   161  func (l *lockState) store(addr ssa.Value, v ssa.Value) {
   162  	l.modify()
   163  	l.stored[addr] = v
   164  }
   165  
   166  // isSubset indicates other holds all the locks held by l.
   167  func (l *lockState) isSubset(other *lockState) bool {
   168  	held := 0 // Number in l, held by other.
   169  	for _, k := range l.lockedMutexes {
   170  		for _, ok := range other.lockedMutexes {
   171  			if k == ok {
   172  				held++
   173  				break
   174  			}
   175  		}
   176  	}
   177  	return held >= len(l.lockedMutexes)
   178  }
   179  
   180  // count indicates the number of locks held.
   181  func (l *lockState) count() int {
   182  	return len(l.lockedMutexes)
   183  }
   184  
   185  // isCompatible returns true if the states are compatible.
   186  func (l *lockState) isCompatible(other *lockState) bool {
   187  	return l.isSubset(other) && other.isSubset(l)
   188  }
   189  
   190  // elemType is a type that implements the Elem function.
   191  type elemType interface {
   192  	Elem() types.Type
   193  }
   194  
   195  // valueAsString returns a string for a given value.
   196  //
   197  // This decomposes the value into the simplest possible representation in terms
   198  // of parameters, free variables and globals. During resolution, stored values
   199  // may be transferred, as well as bound free variables.
   200  //
   201  // Nil may not be passed here.
   202  func (l *lockState) valueAsString(v ssa.Value) string {
   203  	switch x := v.(type) {
   204  	case *ssa.Parameter:
   205  		// Was this provided as a paramter for a local anonymous
   206  		// function invocation?
   207  		v, ok := l.stored[x]
   208  		if ok {
   209  			return l.valueAsString(v)
   210  		}
   211  		return fmt.Sprintf("{param:%s}", x.Name())
   212  	case *ssa.Global:
   213  		return fmt.Sprintf("{global:%s}", x.Name())
   214  	case *ssa.FreeVar:
   215  		// Attempt to resolve this, in case we are being invoked in a
   216  		// scope where all the variables are bound.
   217  		v, ok := l.stored[x]
   218  		if ok {
   219  			// The FreeVar is typically bound to a location, so we
   220  			// check what's been stored there. Note that the second
   221  			// may map to the same FreeVar, which we can check.
   222  			stored, ok := l.stored[v]
   223  			if ok {
   224  				return l.valueAsString(stored)
   225  			}
   226  		}
   227  		return fmt.Sprintf("{freevar:%s}", x.Name())
   228  	case *ssa.Convert:
   229  		// Just disregard conversion.
   230  		return l.valueAsString(x.X)
   231  	case *ssa.ChangeType:
   232  		// Ditto, disregard.
   233  		return l.valueAsString(x.X)
   234  	case *ssa.UnOp:
   235  		if x.Op != token.MUL {
   236  			break
   237  		}
   238  		// Is this loading a free variable? If yes, then this can be
   239  		// resolved in the original isAlias function.
   240  		if fv, ok := x.X.(*ssa.FreeVar); ok {
   241  			return l.valueAsString(fv)
   242  		}
   243  		// Should be try to resolve via a memory address? This needs to
   244  		// be done since a memory location can hold its own value.
   245  		if _, ok := l.used[x.X]; !ok {
   246  			// Check if we know what the accessed location holds.
   247  			// This is used to disambiguate memory locations.
   248  			v, ok := l.stored[x.X]
   249  			if ok {
   250  				l.used[x.X] = struct{}{}
   251  				defer func() { delete(l.used, x.X) }()
   252  				return l.valueAsString(v)
   253  			}
   254  		}
   255  		// x.X.Type is pointer. We must construct this type
   256  		// dynamically, since the ssa.Value could be synthetic.
   257  		return fmt.Sprintf("*(%s)", l.valueAsString(x.X))
   258  	case *ssa.Field:
   259  		structType, ok := resolveStruct(x.X.Type())
   260  		if !ok {
   261  			// This should not happen.
   262  			panic(fmt.Sprintf("structType not available for struct: %#v", x.X))
   263  		}
   264  		fieldObj := structType.Field(x.Field)
   265  		return fmt.Sprintf("%s.%s", l.valueAsString(x.X), fieldObj.Name())
   266  	case *ssa.FieldAddr:
   267  		structType, ok := resolveStruct(x.X.Type())
   268  		if !ok {
   269  			// This should not happen.
   270  			panic(fmt.Sprintf("structType not available for struct: %#v", x.X))
   271  		}
   272  		fieldObj := structType.Field(x.Field)
   273  		return fmt.Sprintf("&(%s.%s)", l.valueAsString(x.X), fieldObj.Name())
   274  	case *ssa.Index:
   275  		return fmt.Sprintf("%s[%s]", l.valueAsString(x.X), l.valueAsString(x.Index))
   276  	case *ssa.IndexAddr:
   277  		return fmt.Sprintf("&(%s[%s])", l.valueAsString(x.X), l.valueAsString(x.Index))
   278  	case *ssa.Lookup:
   279  		return fmt.Sprintf("%s[%s]", l.valueAsString(x.X), l.valueAsString(x.Index))
   280  	case *ssa.Extract:
   281  		return fmt.Sprintf("%s[%d]", l.valueAsString(x.Tuple), x.Index)
   282  	}
   283  
   284  	// In the case of any other type (e.g. this may be an alloc, a return
   285  	// value, etc.), just return the literal pointer value to the Value.
   286  	// This will be unique within the ssa graph, and so if two values are
   287  	// equal, they are from the same type.
   288  	return fmt.Sprintf("{%T:%p}", v, v)
   289  }
   290  
   291  // String returns the full lock state.
   292  func (l *lockState) String() string {
   293  	if l.count() == 0 {
   294  		return "no locks held"
   295  	}
   296  	return strings.Join(l.lockedMutexes, ",")
   297  }
   298  
   299  // pushDefer pushes a defer onto the stack.
   300  func (l *lockState) pushDefer(d *ssa.Defer) {
   301  	l.modify()
   302  	l.defers = append(l.defers, d)
   303  }
   304  
   305  // popDefer pops a defer from the stack.
   306  func (l *lockState) popDefer() *ssa.Defer {
   307  	// Does not technically modify the underlying slice.
   308  	count := len(l.defers)
   309  	if count == 0 {
   310  		return nil
   311  	}
   312  	d := l.defers[count-1]
   313  	l.defers = l.defers[:count-1]
   314  	return d
   315  }