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