gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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  	"gvisor.dev/gvisor/pkg/abi/linux"
    20  	"gvisor.dev/gvisor/pkg/errors/linuxerr"
    21  	"gvisor.dev/gvisor/pkg/sentry/kernel"
    22  	"gvisor.dev/gvisor/pkg/sentry/kernel/auth"
    23  	"gvisor.dev/gvisor/pkg/sentry/vfs"
    24  	"gvisor.dev/gvisor/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 registration 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  }