github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/platform/systrap/subprocess_refs.go (about) 1 package systrap 2 3 import ( 4 "fmt" 5 6 "github.com/ttpreport/gvisor-ligolo/pkg/atomicbitops" 7 "github.com/ttpreport/gvisor-ligolo/pkg/refs" 8 ) 9 10 // enableLogging indicates whether reference-related events should be logged (with 11 // stack traces). This is false by default and should only be set to true for 12 // debugging purposes, as it can generate an extremely large amount of output 13 // and drastically degrade performance. 14 const subprocessenableLogging = false 15 16 // obj is used to customize logging. Note that we use a pointer to T so that 17 // we do not copy the entire object when passed as a format parameter. 18 var subprocessobj *subprocess 19 20 // Refs implements refs.RefCounter. It keeps a reference count using atomic 21 // operations and calls the destructor when the count reaches zero. 22 // 23 // NOTE: Do not introduce additional fields to the Refs struct. It is used by 24 // many filesystem objects, and we want to keep it as small as possible (i.e., 25 // the same size as using an int64 directly) to avoid taking up extra cache 26 // space. In general, this template should not be extended at the cost of 27 // performance. If it does not offer enough flexibility for a particular object 28 // (example: b/187877947), we should implement the RefCounter/CheckedObject 29 // interfaces manually. 30 // 31 // +stateify savable 32 type subprocessRefs struct { 33 // refCount is composed of two fields: 34 // 35 // [32-bit speculative references]:[32-bit real references] 36 // 37 // Speculative references are used for TryIncRef, to avoid a CompareAndSwap 38 // loop. See IncRef, DecRef and TryIncRef for details of how these fields are 39 // used. 40 refCount atomicbitops.Int64 41 } 42 43 // InitRefs initializes r with one reference and, if enabled, activates leak 44 // checking. 45 func (r *subprocessRefs) InitRefs() { 46 47 r.refCount.RacyStore(1) 48 refs.Register(r) 49 } 50 51 // RefType implements refs.CheckedObject.RefType. 52 func (r *subprocessRefs) RefType() string { 53 return fmt.Sprintf("%T", subprocessobj)[1:] 54 } 55 56 // LeakMessage implements refs.CheckedObject.LeakMessage. 57 func (r *subprocessRefs) LeakMessage() string { 58 return fmt.Sprintf("[%s %p] reference count of %d instead of 0", r.RefType(), r, r.ReadRefs()) 59 } 60 61 // LogRefs implements refs.CheckedObject.LogRefs. 62 func (r *subprocessRefs) LogRefs() bool { 63 return subprocessenableLogging 64 } 65 66 // ReadRefs returns the current number of references. The returned count is 67 // inherently racy and is unsafe to use without external synchronization. 68 func (r *subprocessRefs) ReadRefs() int64 { 69 return r.refCount.Load() 70 } 71 72 // IncRef implements refs.RefCounter.IncRef. 73 // 74 //go:nosplit 75 func (r *subprocessRefs) IncRef() { 76 v := r.refCount.Add(1) 77 if subprocessenableLogging { 78 refs.LogIncRef(r, v) 79 } 80 if v <= 1 { 81 panic(fmt.Sprintf("Incrementing non-positive count %p on %s", r, r.RefType())) 82 } 83 } 84 85 // TryIncRef implements refs.TryRefCounter.TryIncRef. 86 // 87 // To do this safely without a loop, a speculative reference is first acquired 88 // on the object. This allows multiple concurrent TryIncRef calls to distinguish 89 // other TryIncRef calls from genuine references held. 90 // 91 //go:nosplit 92 func (r *subprocessRefs) TryIncRef() bool { 93 const speculativeRef = 1 << 32 94 if v := r.refCount.Add(speculativeRef); int32(v) == 0 { 95 96 r.refCount.Add(-speculativeRef) 97 return false 98 } 99 100 v := r.refCount.Add(-speculativeRef + 1) 101 if subprocessenableLogging { 102 refs.LogTryIncRef(r, v) 103 } 104 return true 105 } 106 107 // DecRef implements refs.RefCounter.DecRef. 108 // 109 // Note that speculative references are counted here. Since they were added 110 // prior to real references reaching zero, they will successfully convert to 111 // real references. In other words, we see speculative references only in the 112 // following case: 113 // 114 // A: TryIncRef [speculative increase => sees non-negative references] 115 // B: DecRef [real decrease] 116 // A: TryIncRef [transform speculative to real] 117 // 118 //go:nosplit 119 func (r *subprocessRefs) DecRef(destroy func()) { 120 v := r.refCount.Add(-1) 121 if subprocessenableLogging { 122 refs.LogDecRef(r, v) 123 } 124 switch { 125 case v < 0: 126 panic(fmt.Sprintf("Decrementing non-positive ref count %p, owned by %s", r, r.RefType())) 127 128 case v == 0: 129 refs.Unregister(r) 130 131 if destroy != nil { 132 destroy() 133 } 134 } 135 } 136 137 func (r *subprocessRefs) afterLoad() { 138 if r.ReadRefs() > 0 { 139 refs.Register(r) 140 } 141 }