github.com/nicocha30/gvisor-ligolo@v0.0.0-20230726075806-989fa2c0a413/pkg/sentry/fsimpl/kernfs/mmap_util.go (about)

     1  // Copyright 2020 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 kernfs
    16  
    17  import (
    18  	"github.com/nicocha30/gvisor-ligolo/pkg/context"
    19  	"github.com/nicocha30/gvisor-ligolo/pkg/hostarch"
    20  	"github.com/nicocha30/gvisor-ligolo/pkg/safemem"
    21  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/fsutil"
    22  	"github.com/nicocha30/gvisor-ligolo/pkg/sentry/memmap"
    23  	"github.com/nicocha30/gvisor-ligolo/pkg/sync"
    24  )
    25  
    26  // inodePlatformFile implements memmap.File. It exists solely because inode
    27  // cannot implement both kernfs.Inode.IncRef and memmap.File.IncRef.
    28  //
    29  // +stateify savable
    30  type inodePlatformFile struct {
    31  	// hostFD contains the host fd that this file was originally created from,
    32  	// which must be available at time of restore.
    33  	//
    34  	// This field is initialized at creation time and is immutable.
    35  	// inodePlatformFile does not own hostFD and hence should not close it.
    36  	hostFD int
    37  
    38  	// fdRefsMu protects fdRefs.
    39  	fdRefsMu sync.Mutex `state:"nosave"`
    40  
    41  	// fdRefs counts references on memmap.File offsets. It is used solely for
    42  	// memory accounting.
    43  	fdRefs fsutil.FrameRefSet
    44  
    45  	// fileMapper caches mappings of the host file represented by this inode.
    46  	fileMapper fsutil.HostFileMapper
    47  
    48  	// fileMapperInitOnce is used to lazily initialize fileMapper.
    49  	fileMapperInitOnce sync.Once `state:"nosave"`
    50  }
    51  
    52  var _ memmap.File = (*inodePlatformFile)(nil)
    53  
    54  // IncRef implements memmap.File.IncRef.
    55  func (i *inodePlatformFile) IncRef(fr memmap.FileRange, memCgID uint32) {
    56  	i.fdRefsMu.Lock()
    57  	i.fdRefs.IncRefAndAccount(fr, memCgID)
    58  	i.fdRefsMu.Unlock()
    59  }
    60  
    61  // DecRef implements memmap.File.DecRef.
    62  func (i *inodePlatformFile) DecRef(fr memmap.FileRange) {
    63  	i.fdRefsMu.Lock()
    64  	i.fdRefs.DecRefAndAccount(fr)
    65  	i.fdRefsMu.Unlock()
    66  }
    67  
    68  // MapInternal implements memmap.File.MapInternal.
    69  func (i *inodePlatformFile) MapInternal(fr memmap.FileRange, at hostarch.AccessType) (safemem.BlockSeq, error) {
    70  	return i.fileMapper.MapInternal(fr, i.hostFD, at.Write)
    71  }
    72  
    73  // FD implements memmap.File.FD.
    74  func (i *inodePlatformFile) FD() int {
    75  	return i.hostFD
    76  }
    77  
    78  // CachedMappable implements memmap.Mappable. This utility can be embedded in a
    79  // kernfs.Inode that represents a host file  to make the inode mappable.
    80  // CachedMappable caches the mappings of the host file. CachedMappable must be
    81  // initialized (via Init) with a hostFD before use.
    82  //
    83  // +stateify savable
    84  type CachedMappable struct {
    85  	// mapsMu protects mappings.
    86  	mapsMu sync.Mutex `state:"nosave"`
    87  
    88  	// mappings tracks mappings of hostFD into memmap.MappingSpaces.
    89  	mappings memmap.MappingSet
    90  
    91  	// pf implements memmap.File for mappings backed by a host fd.
    92  	pf inodePlatformFile
    93  }
    94  
    95  var _ memmap.Mappable = (*CachedMappable)(nil)
    96  
    97  // Init initializes i.pf. This must be called before using CachedMappable.
    98  func (i *CachedMappable) Init(hostFD int) {
    99  	i.pf.hostFD = hostFD
   100  }
   101  
   102  // AddMapping implements memmap.Mappable.AddMapping.
   103  func (i *CachedMappable) AddMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) error {
   104  	i.mapsMu.Lock()
   105  	mapped := i.mappings.AddMapping(ms, ar, offset, writable)
   106  	for _, r := range mapped {
   107  		i.pf.fileMapper.IncRefOn(r)
   108  	}
   109  	i.mapsMu.Unlock()
   110  	return nil
   111  }
   112  
   113  // RemoveMapping implements memmap.Mappable.RemoveMapping.
   114  func (i *CachedMappable) RemoveMapping(ctx context.Context, ms memmap.MappingSpace, ar hostarch.AddrRange, offset uint64, writable bool) {
   115  	i.mapsMu.Lock()
   116  	unmapped := i.mappings.RemoveMapping(ms, ar, offset, writable)
   117  	for _, r := range unmapped {
   118  		i.pf.fileMapper.DecRefOn(r)
   119  	}
   120  	i.mapsMu.Unlock()
   121  }
   122  
   123  // CopyMapping implements memmap.Mappable.CopyMapping.
   124  func (i *CachedMappable) CopyMapping(ctx context.Context, ms memmap.MappingSpace, srcAR, dstAR hostarch.AddrRange, offset uint64, writable bool) error {
   125  	return i.AddMapping(ctx, ms, dstAR, offset, writable)
   126  }
   127  
   128  // Translate implements memmap.Mappable.Translate.
   129  func (i *CachedMappable) Translate(ctx context.Context, required, optional memmap.MappableRange, at hostarch.AccessType) ([]memmap.Translation, error) {
   130  	mr := optional
   131  	return []memmap.Translation{
   132  		{
   133  			Source: mr,
   134  			File:   &i.pf,
   135  			Offset: mr.Start,
   136  			Perms:  hostarch.AnyAccess,
   137  		},
   138  	}, nil
   139  }
   140  
   141  // InvalidateUnsavable implements memmap.Mappable.InvalidateUnsavable.
   142  func (i *CachedMappable) InvalidateUnsavable(ctx context.Context) error {
   143  	// We expect the same host fd across save/restore, so all translations
   144  	// should be valid.
   145  	return nil
   146  }
   147  
   148  // InvalidateRange invalidates the passed range on i.mappings.
   149  func (i *CachedMappable) InvalidateRange(r memmap.MappableRange) {
   150  	i.mapsMu.Lock()
   151  	i.mappings.Invalidate(r, memmap.InvalidateOpts{
   152  		// Compare Linux's mm/truncate.c:truncate_setsize() =>
   153  		// truncate_pagecache() =>
   154  		// mm/memory.c:unmap_mapping_range(evencows=1).
   155  		InvalidatePrivate: true,
   156  	})
   157  	i.mapsMu.Unlock()
   158  }
   159  
   160  // InitFileMapperOnce initializes the host file mapper. It ensures that the
   161  // file mapper is initialized just once.
   162  func (i *CachedMappable) InitFileMapperOnce() {
   163  	i.pf.fileMapperInitOnce.Do(i.pf.fileMapper.Init)
   164  }