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