github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/platform/ptrace/ptrace.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 ptrace provides a ptrace-based implementation of the platform
    16  // interface. This is useful for development and testing purposes primarily,
    17  // and runs on stock kernels without special permissions.
    18  //
    19  // In a nutshell, it works as follows:
    20  //
    21  // The creation of a new address space creates a new child process with a single
    22  // thread which is traced by a single goroutine.
    23  //
    24  // A context is just a collection of temporary variables. Calling Switch on a
    25  // context does the following:
    26  //
    27  //	Locks the runtime thread.
    28  //
    29  //	Looks up a traced subprocess thread for the current runtime thread. If
    30  //	none exists, the dedicated goroutine is asked to create a new stopped
    31  //	thread in the subprocess. This stopped subprocess thread is then traced
    32  //	by the current thread and this information is stored for subsequent
    33  //	switches.
    34  //
    35  //	The context is then bound with information about the subprocess thread
    36  //	so that the context may be appropriately interrupted via a signal.
    37  //
    38  //	The requested operation is performed in the traced subprocess thread
    39  //	(e.g. set registers, execute, return).
    40  //
    41  // Lock order:
    42  //
    43  // subprocess.mu
    44  //   context.mu
    45  package ptrace
    46  
    47  import (
    48  	"os"
    49  
    50  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    51  	pkgcontext "github.com/SagerNet/gvisor/pkg/context"
    52  	"github.com/SagerNet/gvisor/pkg/hostarch"
    53  	"github.com/SagerNet/gvisor/pkg/sentry/arch"
    54  	"github.com/SagerNet/gvisor/pkg/sentry/platform"
    55  	"github.com/SagerNet/gvisor/pkg/sentry/platform/interrupt"
    56  	"github.com/SagerNet/gvisor/pkg/sync"
    57  )
    58  
    59  var (
    60  	// stubStart is the link address for our stub, and determines the
    61  	// maximum user address. This is valid only after a call to stubInit.
    62  	//
    63  	// We attempt to link the stub here, and adjust downward as needed.
    64  	stubStart uintptr = stubInitAddress
    65  
    66  	// stubEnd is the first byte past the end of the stub, as with
    67  	// stubStart this is valid only after a call to stubInit.
    68  	stubEnd uintptr
    69  
    70  	// stubInitialized controls one-time stub initialization.
    71  	stubInitialized sync.Once
    72  )
    73  
    74  type context struct {
    75  	// signalInfo is the signal info, if and when a signal is received.
    76  	signalInfo linux.SignalInfo
    77  
    78  	// interrupt is the interrupt context.
    79  	interrupt interrupt.Forwarder
    80  
    81  	// mu protects the following fields.
    82  	mu sync.Mutex
    83  
    84  	// If lastFaultSP is non-nil, the last context switch was due to a fault
    85  	// received while executing lastFaultSP. Only context.Switch may set
    86  	// lastFaultSP to a non-nil value.
    87  	lastFaultSP *subprocess
    88  
    89  	// lastFaultAddr is the last faulting address; this is only meaningful if
    90  	// lastFaultSP is non-nil.
    91  	lastFaultAddr hostarch.Addr
    92  
    93  	// lastFaultIP is the address of the last faulting instruction;
    94  	// this is also only meaningful if lastFaultSP is non-nil.
    95  	lastFaultIP hostarch.Addr
    96  }
    97  
    98  // Switch runs the provided context in the given address space.
    99  func (c *context) Switch(ctx pkgcontext.Context, mm platform.MemoryManager, ac arch.Context, cpu int32) (*linux.SignalInfo, hostarch.AccessType, error) {
   100  	as := mm.AddressSpace()
   101  	s := as.(*subprocess)
   102  	isSyscall := s.switchToApp(c, ac)
   103  
   104  	var (
   105  		faultSP   *subprocess
   106  		faultAddr hostarch.Addr
   107  		faultIP   hostarch.Addr
   108  	)
   109  	if !isSyscall && linux.Signal(c.signalInfo.Signo) == linux.SIGSEGV {
   110  		faultSP = s
   111  		faultAddr = hostarch.Addr(c.signalInfo.Addr())
   112  		faultIP = hostarch.Addr(ac.IP())
   113  	}
   114  
   115  	// Update the context to reflect the outcome of this context switch.
   116  	c.mu.Lock()
   117  	lastFaultSP := c.lastFaultSP
   118  	lastFaultAddr := c.lastFaultAddr
   119  	lastFaultIP := c.lastFaultIP
   120  	// At this point, c may not yet be in s.contexts, so c.lastFaultSP won't be
   121  	// updated by s.Unmap(). This is fine; we only need to synchronize with
   122  	// calls to s.Unmap() that occur after the handling of this fault.
   123  	c.lastFaultSP = faultSP
   124  	c.lastFaultAddr = faultAddr
   125  	c.lastFaultIP = faultIP
   126  	c.mu.Unlock()
   127  
   128  	// Update subprocesses to reflect the outcome of this context switch.
   129  	if lastFaultSP != faultSP {
   130  		if lastFaultSP != nil {
   131  			lastFaultSP.mu.Lock()
   132  			delete(lastFaultSP.contexts, c)
   133  			lastFaultSP.mu.Unlock()
   134  		}
   135  		if faultSP != nil {
   136  			faultSP.mu.Lock()
   137  			faultSP.contexts[c] = struct{}{}
   138  			faultSP.mu.Unlock()
   139  		}
   140  	}
   141  
   142  	if isSyscall {
   143  		return nil, hostarch.NoAccess, nil
   144  	}
   145  
   146  	si := c.signalInfo
   147  
   148  	if faultSP == nil {
   149  		// Non-fault signal.
   150  		return &si, hostarch.NoAccess, platform.ErrContextSignal
   151  	}
   152  
   153  	// Got a page fault. Ideally, we'd get real fault type here, but ptrace
   154  	// doesn't expose this information. Instead, we use a simple heuristic:
   155  	//
   156  	// It was an instruction fault iff the faulting addr == instruction
   157  	// pointer.
   158  	//
   159  	// It was a write fault if the fault is immediately repeated.
   160  	at := hostarch.Read
   161  	if faultAddr == faultIP {
   162  		at.Execute = true
   163  	}
   164  	if lastFaultSP == faultSP &&
   165  		lastFaultAddr == faultAddr &&
   166  		lastFaultIP == faultIP {
   167  		at.Write = true
   168  	}
   169  
   170  	// Unfortunately, we have to unilaterally return ErrContextSignalCPUID
   171  	// here, in case this fault was generated by a CPUID exception. There
   172  	// is no way to distinguish between CPUID-generated faults and regular
   173  	// page faults.
   174  	return &si, at, platform.ErrContextSignalCPUID
   175  }
   176  
   177  // Interrupt interrupts the running guest application associated with this context.
   178  func (c *context) Interrupt() {
   179  	c.interrupt.NotifyInterrupt()
   180  }
   181  
   182  // Release implements platform.Context.Release().
   183  func (c *context) Release() {}
   184  
   185  // FullStateChanged implements platform.Context.FullStateChanged.
   186  func (c *context) FullStateChanged() {}
   187  
   188  // PullFullState implements platform.Context.PullFullState.
   189  func (c *context) PullFullState(as platform.AddressSpace, ac arch.Context) {}
   190  
   191  // PTrace represents a collection of ptrace subprocesses.
   192  type PTrace struct {
   193  	platform.MMapMinAddr
   194  	platform.NoCPUPreemptionDetection
   195  	platform.UseHostGlobalMemoryBarrier
   196  }
   197  
   198  // New returns a new ptrace-based implementation of the platform interface.
   199  func New() (*PTrace, error) {
   200  	stubInitialized.Do(func() {
   201  		// Initialize the stub.
   202  		stubInit()
   203  
   204  		// Create the master process for the global pool. This must be
   205  		// done before initializing any other processes.
   206  		master, err := newSubprocess(createStub)
   207  		if err != nil {
   208  			// Should never happen.
   209  			panic("unable to initialize ptrace master: " + err.Error())
   210  		}
   211  
   212  		// Set the master on the globalPool.
   213  		globalPool.master = master
   214  	})
   215  
   216  	return &PTrace{}, nil
   217  }
   218  
   219  // SupportsAddressSpaceIO implements platform.Platform.SupportsAddressSpaceIO.
   220  func (*PTrace) SupportsAddressSpaceIO() bool {
   221  	return false
   222  }
   223  
   224  // CooperativelySchedulesAddressSpace implements platform.Platform.CooperativelySchedulesAddressSpace.
   225  func (*PTrace) CooperativelySchedulesAddressSpace() bool {
   226  	return false
   227  }
   228  
   229  // MapUnit implements platform.Platform.MapUnit.
   230  func (*PTrace) MapUnit() uint64 {
   231  	// The host kernel manages page tables and arbitrary-sized mappings
   232  	// have effectively the same cost.
   233  	return 0
   234  }
   235  
   236  // MaxUserAddress returns the first address that may not be used by user
   237  // applications.
   238  func (*PTrace) MaxUserAddress() hostarch.Addr {
   239  	return hostarch.Addr(stubStart)
   240  }
   241  
   242  // NewAddressSpace returns a new subprocess.
   243  func (p *PTrace) NewAddressSpace(_ interface{}) (platform.AddressSpace, <-chan struct{}, error) {
   244  	as, err := newSubprocess(globalPool.master.createStub)
   245  	return as, nil, err
   246  }
   247  
   248  // NewContext returns an interruptible context.
   249  func (*PTrace) NewContext() platform.Context {
   250  	return &context{}
   251  }
   252  
   253  type constructor struct{}
   254  
   255  func (*constructor) New(*os.File) (platform.Platform, error) {
   256  	return New()
   257  }
   258  
   259  func (*constructor) OpenDevice() (*os.File, error) {
   260  	return nil, nil
   261  }
   262  
   263  // Flags implements platform.Constructor.Flags().
   264  func (*constructor) Requirements() platform.Requirements {
   265  	// TODO(b/75837838): Also set a new PID namespace so that we limit
   266  	// access to other host processes.
   267  	return platform.Requirements{
   268  		RequiresCapSysPtrace: true,
   269  		RequiresCurrentPIDNS: true,
   270  	}
   271  }
   272  
   273  func init() {
   274  	platform.Register("ptrace", &constructor{})
   275  }