github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/pipe/node_test.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 pipe 16 17 import ( 18 "testing" 19 "time" 20 21 "github.com/SagerNet/gvisor/pkg/context" 22 "github.com/SagerNet/gvisor/pkg/errors/linuxerr" 23 "github.com/SagerNet/gvisor/pkg/sentry/contexttest" 24 "github.com/SagerNet/gvisor/pkg/sentry/fs" 25 "github.com/SagerNet/gvisor/pkg/syserror" 26 ) 27 28 type sleeper struct { 29 context.Context 30 ch chan struct{} 31 } 32 33 func newSleeperContext(t *testing.T) context.Context { 34 return &sleeper{ 35 Context: contexttest.Context(t), 36 ch: make(chan struct{}), 37 } 38 } 39 40 func (s *sleeper) SleepStart() <-chan struct{} { 41 return s.ch 42 } 43 44 func (s *sleeper) SleepFinish(bool) { 45 } 46 47 func (s *sleeper) Cancel() { 48 s.ch <- struct{}{} 49 } 50 51 func (s *sleeper) Interrupted() bool { 52 return len(s.ch) != 0 53 } 54 55 type openResult struct { 56 *fs.File 57 error 58 } 59 60 var perms fs.FilePermissions = fs.FilePermissions{ 61 User: fs.PermMask{Read: true, Write: true}, 62 } 63 64 func testOpenOrDie(ctx context.Context, t *testing.T, n fs.InodeOperations, flags fs.FileFlags, doneChan chan<- struct{}) (*fs.File, error) { 65 inode := fs.NewMockInode(ctx, fs.NewMockMountSource(nil), fs.StableAttr{Type: fs.Pipe}) 66 d := fs.NewDirent(ctx, inode, "pipe") 67 file, err := n.GetFile(ctx, d, flags) 68 if err != nil { 69 t.Errorf("open with flags %+v failed: %v", flags, err) 70 return nil, err 71 } 72 if doneChan != nil { 73 doneChan <- struct{}{} 74 } 75 return file, err 76 } 77 78 func testOpen(ctx context.Context, t *testing.T, n fs.InodeOperations, flags fs.FileFlags, resChan chan<- openResult) (*fs.File, error) { 79 inode := fs.NewMockInode(ctx, fs.NewMockMountSource(nil), fs.StableAttr{Type: fs.Pipe}) 80 d := fs.NewDirent(ctx, inode, "pipe") 81 file, err := n.GetFile(ctx, d, flags) 82 if resChan != nil { 83 resChan <- openResult{file, err} 84 } 85 return file, err 86 } 87 88 func newNamedPipe(t *testing.T) *Pipe { 89 return NewPipe(true, DefaultPipeSize) 90 } 91 92 func newAnonPipe(t *testing.T) *Pipe { 93 return NewPipe(false, DefaultPipeSize) 94 } 95 96 // assertRecvBlocks ensures that a recv attempt on c blocks for at least 97 // blockDuration. This is useful for checking that a goroutine that is supposed 98 // to be executing a blocking operation is actually blocking. 99 func assertRecvBlocks(t *testing.T, c <-chan struct{}, blockDuration time.Duration, failMsg string) { 100 select { 101 case <-c: 102 t.Fatalf(failMsg) 103 case <-time.After(blockDuration): 104 // Ok, blocked for the required duration. 105 } 106 } 107 108 func TestReadOpenBlocksForWriteOpen(t *testing.T) { 109 ctx := newSleeperContext(t) 110 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 111 112 rDone := make(chan struct{}) 113 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone) 114 115 // Verify that the open for read is blocking. 116 assertRecvBlocks(t, rDone, time.Millisecond*100, 117 "open for read not blocking with no writers") 118 119 wDone := make(chan struct{}) 120 go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone) 121 122 <-wDone 123 <-rDone 124 } 125 126 func TestWriteOpenBlocksForReadOpen(t *testing.T) { 127 ctx := newSleeperContext(t) 128 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 129 130 wDone := make(chan struct{}) 131 go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone) 132 133 // Verify that the open for write is blocking 134 assertRecvBlocks(t, wDone, time.Millisecond*100, 135 "open for write not blocking with no readers") 136 137 rDone := make(chan struct{}) 138 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone) 139 140 <-rDone 141 <-wDone 142 } 143 144 func TestMultipleWriteOpenDoesntCountAsReadOpen(t *testing.T) { 145 ctx := newSleeperContext(t) 146 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 147 148 rDone1 := make(chan struct{}) 149 rDone2 := make(chan struct{}) 150 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone1) 151 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone2) 152 153 assertRecvBlocks(t, rDone1, time.Millisecond*100, 154 "open for read didn't block with no writers") 155 assertRecvBlocks(t, rDone2, time.Millisecond*100, 156 "open for read didn't block with no writers") 157 158 wDone := make(chan struct{}) 159 go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone) 160 161 <-wDone 162 <-rDone2 163 <-rDone1 164 } 165 166 func TestClosedReaderBlocksWriteOpen(t *testing.T) { 167 ctx := newSleeperContext(t) 168 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 169 170 rFile, _ := testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, NonBlocking: true}, nil) 171 rFile.DecRef(ctx) 172 173 wDone := make(chan struct{}) 174 // This open for write should block because the reader is now gone. 175 go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone) 176 assertRecvBlocks(t, wDone, time.Millisecond*100, 177 "open for write didn't block with no concurrent readers") 178 179 // Open for read again. This should unblock the open for write. 180 rDone := make(chan struct{}) 181 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone) 182 183 <-rDone 184 <-wDone 185 } 186 187 func TestReadWriteOpenNeverBlocks(t *testing.T) { 188 ctx := newSleeperContext(t) 189 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 190 191 rwDone := make(chan struct{}) 192 // Open for read-write never wait for a reader or writer, even if the 193 // nonblocking flag is not set. 194 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, Write: true, NonBlocking: false}, rwDone) 195 <-rwDone 196 } 197 198 func TestReadWriteOpenUnblocksReadOpen(t *testing.T) { 199 ctx := newSleeperContext(t) 200 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 201 202 rDone := make(chan struct{}) 203 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone) 204 205 rwDone := make(chan struct{}) 206 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, Write: true}, rwDone) 207 208 <-rwDone 209 <-rDone 210 } 211 212 func TestReadWriteOpenUnblocksWriteOpen(t *testing.T) { 213 ctx := newSleeperContext(t) 214 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 215 216 wDone := make(chan struct{}) 217 go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone) 218 219 rwDone := make(chan struct{}) 220 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true, Write: true}, rwDone) 221 222 <-rwDone 223 <-wDone 224 } 225 226 func TestBlockedOpenIsCancellable(t *testing.T) { 227 ctx := newSleeperContext(t) 228 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 229 230 done := make(chan openResult) 231 go testOpen(ctx, t, f, fs.FileFlags{Read: true}, done) 232 select { 233 case <-done: 234 t.Fatalf("open for read didn't block with no writers") 235 case <-time.After(time.Millisecond * 100): 236 // Ok. 237 } 238 239 ctx.(*sleeper).Cancel() 240 // If the cancel on the sleeper didn't work, the open for read would never 241 // return. 242 res := <-done 243 if res.error != syserror.ErrInterrupted { 244 t.Fatalf("Cancellation didn't cause GetFile to return fs.ErrInterrupted, got %v.", 245 res.error) 246 } 247 } 248 249 func TestNonblockingReadOpenFileNoWriters(t *testing.T) { 250 ctx := newSleeperContext(t) 251 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 252 253 if _, err := testOpen(ctx, t, f, fs.FileFlags{Read: true, NonBlocking: true}, nil); err != nil { 254 t.Fatalf("Nonblocking open for read failed with error %v.", err) 255 } 256 } 257 258 func TestNonblockingWriteOpenFileNoReaders(t *testing.T) { 259 ctx := newSleeperContext(t) 260 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 261 262 if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true, NonBlocking: true}, nil); !linuxerr.Equals(linuxerr.ENXIO, err) { 263 t.Fatalf("Nonblocking open for write failed unexpected error %v.", err) 264 } 265 } 266 267 func TestNonBlockingReadOpenWithWriter(t *testing.T) { 268 ctx := newSleeperContext(t) 269 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 270 271 wDone := make(chan struct{}) 272 go testOpenOrDie(ctx, t, f, fs.FileFlags{Write: true}, wDone) 273 274 // Open for write blocks since there are no readers yet. 275 assertRecvBlocks(t, wDone, time.Millisecond*100, 276 "Open for write didn't block with no reader.") 277 278 if _, err := testOpen(ctx, t, f, fs.FileFlags{Read: true, NonBlocking: true}, nil); err != nil { 279 t.Fatalf("Nonblocking open for read failed with error %v.", err) 280 } 281 282 // Open for write should now be unblocked. 283 <-wDone 284 } 285 286 func TestNonBlockingWriteOpenWithReader(t *testing.T) { 287 ctx := newSleeperContext(t) 288 f := NewInodeOperations(ctx, perms, newNamedPipe(t)) 289 290 rDone := make(chan struct{}) 291 go testOpenOrDie(ctx, t, f, fs.FileFlags{Read: true}, rDone) 292 293 // Open for write blocked, since no reader yet. 294 assertRecvBlocks(t, rDone, time.Millisecond*100, 295 "Open for reader didn't block with no writer.") 296 297 if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true, NonBlocking: true}, nil); err != nil { 298 t.Fatalf("Nonblocking open for write failed with error %v.", err) 299 } 300 301 // Open for write should now be unblocked. 302 <-rDone 303 } 304 305 func TestAnonReadOpen(t *testing.T) { 306 ctx := newSleeperContext(t) 307 f := NewInodeOperations(ctx, perms, newAnonPipe(t)) 308 309 if _, err := testOpen(ctx, t, f, fs.FileFlags{Read: true}, nil); err != nil { 310 t.Fatalf("open anon pipe for read failed: %v", err) 311 } 312 } 313 314 func TestAnonWriteOpen(t *testing.T) { 315 ctx := newSleeperContext(t) 316 f := NewInodeOperations(ctx, perms, newAnonPipe(t)) 317 318 if _, err := testOpen(ctx, t, f, fs.FileFlags{Write: true}, nil); err != nil { 319 t.Fatalf("open anon pipe for write failed: %v", err) 320 } 321 }