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 }