github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/kernel/fasync/fasync.go (about) 1 // Copyright 2018 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 fasync provides FIOASYNC related functionality. 16 package fasync 17 18 import ( 19 "github.com/nicocha30/gvisor-ligolo/pkg/abi/linux" 20 "github.com/nicocha30/gvisor-ligolo/pkg/errors/linuxerr" 21 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel" 22 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/kernel/auth" 23 "github.com/nicocha30/gvisor-ligolo/pkg/sentry/vfs" 24 "github.com/nicocha30/gvisor-ligolo/pkg/waiter" 25 ) 26 27 // Table to convert waiter event masks into si_band siginfo codes. 28 // Taken from fs/fcntl.c:band_table. 29 var bandTable = map[waiter.EventMask]int64{ 30 // POLL_IN 31 waiter.EventIn: linux.EPOLLIN | linux.EPOLLRDNORM, 32 // POLL_OUT 33 waiter.EventOut: linux.EPOLLOUT | linux.EPOLLWRNORM | linux.EPOLLWRBAND, 34 // POLL_ERR 35 waiter.EventErr: linux.EPOLLERR, 36 // POLL_PRI 37 waiter.EventPri: linux.EPOLLPRI | linux.EPOLLRDBAND, 38 // POLL_HUP 39 waiter.EventHUp: linux.EPOLLHUP | linux.EPOLLERR, 40 } 41 42 // New returns a function that creates a new vfs.FileAsync with the given 43 // file descriptor. 44 func New(fd int) func() vfs.FileAsync { 45 return func() vfs.FileAsync { 46 return &FileAsync{fd: fd} 47 } 48 } 49 50 // FileAsync sends signals when the registered file is ready for IO. 51 // 52 // +stateify savable 53 type FileAsync struct { 54 // e is immutable after first use (which is protected by mu below). 55 e waiter.Entry 56 57 // fd is the file descriptor to notify about. 58 // It is immutable, set at allocation time. This matches Linux semantics in 59 // fs/fcntl.c:fasync_helper. 60 // The fd value is passed to the signal recipient in siginfo.si_fd. 61 fd int 62 63 // regMu protects registeration and unregistration actions on e. 64 // 65 // regMu must be held while registration decisions are being made 66 // through the registration action itself. 67 // 68 // Lock ordering: regMu, mu. 69 regMu regMutex `state:"nosave"` 70 71 // mu protects all following fields. 72 // 73 // Lock ordering: e.mu, mu. 74 mu fileMutex `state:"nosave"` 75 requester *auth.Credentials 76 registered bool 77 // signal is the signal to deliver upon I/O being available. 78 // The default value ("zero signal") means the default SIGIO signal will be 79 // delivered. 80 signal linux.Signal 81 82 // Only one of the following is allowed to be non-nil. 83 recipientPG *kernel.ProcessGroup 84 recipientTG *kernel.ThreadGroup 85 recipientT *kernel.Task 86 } 87 88 // NotifyEvent implements waiter.EventListener.NotifyEvent. 89 func (a *FileAsync) NotifyEvent(mask waiter.EventMask) { 90 a.mu.Lock() 91 if !a.registered { 92 a.mu.Unlock() 93 return 94 } 95 // Read all the required fields which are lock protected from FileAsync 96 // and release the lock. 97 t := a.recipientT 98 tg := a.recipientTG 99 creds := a.requester 100 sig := a.signal 101 if a.recipientPG != nil { 102 tg = a.recipientPG.Originator() 103 } 104 a.mu.Unlock() 105 106 if tg != nil { 107 t = tg.Leader() 108 } 109 if t == nil { 110 // No recipient has been registered. 111 return 112 } 113 tCreds := t.Credentials() 114 // Logic from sigio_perm in fs/fcntl.c. 115 permCheck := (creds.EffectiveKUID == 0 || 116 creds.EffectiveKUID == tCreds.SavedKUID || 117 creds.EffectiveKUID == tCreds.RealKUID || 118 creds.RealKUID == tCreds.SavedKUID || 119 creds.RealKUID == tCreds.RealKUID) 120 if !permCheck { 121 return 122 } 123 signalInfo := &linux.SignalInfo{ 124 Signo: int32(linux.SIGIO), 125 Code: linux.SI_KERNEL, 126 } 127 if sig != 0 { 128 signalInfo.Signo = int32(sig) 129 signalInfo.SetFD(uint32(a.fd)) 130 var band int64 131 for m, bandCode := range bandTable { 132 if m&mask != 0 { 133 band |= bandCode 134 } 135 } 136 signalInfo.SetBand(band) 137 } 138 if tg != nil { 139 t.SendGroupSignal(signalInfo) 140 } else { 141 t.SendSignal(signalInfo) 142 } 143 } 144 145 // Register sets the file which will be monitored for IO events. 146 // 147 // The file must not be currently registered. 148 func (a *FileAsync) Register(w waiter.Waitable) error { 149 a.regMu.Lock() 150 defer a.regMu.Unlock() 151 a.mu.Lock() 152 if a.registered { 153 a.mu.Unlock() 154 panic("registering already registered file") 155 } 156 a.e.Init(a, waiter.ReadableEvents|waiter.WritableEvents|waiter.EventErr|waiter.EventHUp) 157 a.registered = true 158 a.mu.Unlock() 159 return w.EventRegister(&a.e) 160 } 161 162 // Unregister stops monitoring a file. 163 // 164 // The file must be currently registered. 165 func (a *FileAsync) Unregister(w waiter.Waitable) { 166 a.regMu.Lock() 167 defer a.regMu.Unlock() 168 a.mu.Lock() 169 170 if !a.registered { 171 a.mu.Unlock() 172 panic("unregistering unregistered file") 173 } 174 175 a.registered = false 176 177 a.mu.Unlock() 178 w.EventUnregister(&a.e) 179 } 180 181 // Owner returns who is currently getting signals. All return values will be 182 // nil if no one is set to receive signals. 183 func (a *FileAsync) Owner() (*kernel.Task, *kernel.ThreadGroup, *kernel.ProcessGroup) { 184 a.mu.Lock() 185 defer a.mu.Unlock() 186 return a.recipientT, a.recipientTG, a.recipientPG 187 } 188 189 // SetOwnerTask sets the owner (who will receive signals) to a specified task. 190 // Only this owner will receive signals. 191 func (a *FileAsync) SetOwnerTask(requester *kernel.Task, recipient *kernel.Task) { 192 a.mu.Lock() 193 defer a.mu.Unlock() 194 a.requester = requester.Credentials() 195 a.recipientT = recipient 196 a.recipientTG = nil 197 a.recipientPG = nil 198 } 199 200 // SetOwnerThreadGroup sets the owner (who will receive signals) to a specified 201 // thread group. Only this owner will receive signals. 202 func (a *FileAsync) SetOwnerThreadGroup(requester *kernel.Task, recipient *kernel.ThreadGroup) { 203 a.mu.Lock() 204 defer a.mu.Unlock() 205 a.requester = requester.Credentials() 206 a.recipientT = nil 207 a.recipientTG = recipient 208 a.recipientPG = nil 209 } 210 211 // SetOwnerProcessGroup sets the owner (who will receive signals) to a 212 // specified process group. Only this owner will receive signals. 213 func (a *FileAsync) SetOwnerProcessGroup(requester *kernel.Task, recipient *kernel.ProcessGroup) { 214 a.mu.Lock() 215 defer a.mu.Unlock() 216 a.requester = requester.Credentials() 217 a.recipientT = nil 218 a.recipientTG = nil 219 a.recipientPG = recipient 220 } 221 222 // ClearOwner unsets the current signal recipient. 223 func (a *FileAsync) ClearOwner() { 224 a.mu.Lock() 225 defer a.mu.Unlock() 226 a.requester = nil 227 a.recipientT = nil 228 a.recipientTG = nil 229 a.recipientPG = nil 230 } 231 232 // Signal returns which signal will be sent to the signal recipient. 233 // A value of zero means the signal to deliver wasn't customized, which means 234 // the default signal (SIGIO) will be delivered. 235 func (a *FileAsync) Signal() linux.Signal { 236 a.mu.Lock() 237 defer a.mu.Unlock() 238 return a.signal 239 } 240 241 // SetSignal overrides which signal to send when I/O is available. 242 // The default behavior can be reset by specifying signal zero, which means 243 // to send SIGIO. 244 func (a *FileAsync) SetSignal(signal linux.Signal) error { 245 if signal != 0 && !signal.IsValid() { 246 return linuxerr.EINVAL 247 } 248 a.mu.Lock() 249 defer a.mu.Unlock() 250 a.signal = signal 251 return nil 252 }