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  }