github.com/ttpreport/gvisor-ligolo@v0.0.0-20240123134145-a858404967ba/pkg/sentry/fsimpl/proc/tasks_sys.go (about) 1 // Copyright 2019 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 proc 16 17 import ( 18 "bytes" 19 "fmt" 20 "math" 21 22 "github.com/ttpreport/gvisor-ligolo/pkg/abi/linux" 23 "github.com/ttpreport/gvisor-ligolo/pkg/context" 24 "github.com/ttpreport/gvisor-ligolo/pkg/errors/linuxerr" 25 "github.com/ttpreport/gvisor-ligolo/pkg/hostarch" 26 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/fsimpl/kernfs" 27 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/inet" 28 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel" 29 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/kernel/auth" 30 "github.com/ttpreport/gvisor-ligolo/pkg/sentry/vfs" 31 "github.com/ttpreport/gvisor-ligolo/pkg/sync" 32 "github.com/ttpreport/gvisor-ligolo/pkg/tcpip/network/ipv4" 33 "github.com/ttpreport/gvisor-ligolo/pkg/usermem" 34 ) 35 36 // +stateify savable 37 type tcpMemDir int 38 39 const ( 40 tcpRMem tcpMemDir = iota 41 tcpWMem 42 ) 43 44 // newSysDir returns the dentry corresponding to /proc/sys directory. 45 func (fs *filesystem) newSysDir(ctx context.Context, root *auth.Credentials, k *kernel.Kernel) kernfs.Inode { 46 return fs.newStaticDir(ctx, root, map[string]kernfs.Inode{ 47 "kernel": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{ 48 "hostname": fs.newInode(ctx, root, 0444, &hostnameData{}), 49 "sem": fs.newInode(ctx, root, 0444, newStaticFile(fmt.Sprintf("%d\t%d\t%d\t%d\n", linux.SEMMSL, linux.SEMMNS, linux.SEMOPM, linux.SEMMNI))), 50 "shmall": fs.newInode(ctx, root, 0444, ipcData(linux.SHMALL)), 51 "shmmax": fs.newInode(ctx, root, 0444, ipcData(linux.SHMMAX)), 52 "shmmni": fs.newInode(ctx, root, 0444, ipcData(linux.SHMMNI)), 53 "msgmni": fs.newInode(ctx, root, 0444, ipcData(linux.MSGMNI)), 54 "msgmax": fs.newInode(ctx, root, 0444, ipcData(linux.MSGMAX)), 55 "msgmnb": fs.newInode(ctx, root, 0444, ipcData(linux.MSGMNB)), 56 "yama": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{ 57 "ptrace_scope": fs.newYAMAPtraceScopeFile(ctx, k, root), 58 }), 59 }), 60 "vm": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{ 61 "max_map_count": fs.newInode(ctx, root, 0444, newStaticFile("2147483647\n")), 62 "mmap_min_addr": fs.newInode(ctx, root, 0444, &mmapMinAddrData{k: k}), 63 "overcommit_memory": fs.newInode(ctx, root, 0444, newStaticFile("0\n")), 64 }), 65 "net": fs.newSysNetDir(ctx, root, k), 66 }) 67 } 68 69 // newSysNetDir returns the dentry corresponding to /proc/sys/net directory. 70 func (fs *filesystem) newSysNetDir(ctx context.Context, root *auth.Credentials, k *kernel.Kernel) kernfs.Inode { 71 var contents map[string]kernfs.Inode 72 73 // TODO(gvisor.dev/issue/1833): Support for using the network stack in the 74 // network namespace of the calling process. 75 if stack := k.RootNetworkNamespace().Stack(); stack != nil { 76 contents = map[string]kernfs.Inode{ 77 "ipv4": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{ 78 "ip_forward": fs.newInode(ctx, root, 0444, &ipForwarding{stack: stack}), 79 "ip_local_port_range": fs.newInode(ctx, root, 0644, &portRange{stack: stack}), 80 "tcp_recovery": fs.newInode(ctx, root, 0644, &tcpRecoveryData{stack: stack}), 81 "tcp_rmem": fs.newInode(ctx, root, 0644, &tcpMemData{stack: stack, dir: tcpRMem}), 82 "tcp_sack": fs.newInode(ctx, root, 0644, &tcpSackData{stack: stack}), 83 "tcp_wmem": fs.newInode(ctx, root, 0644, &tcpMemData{stack: stack, dir: tcpWMem}), 84 85 // The following files are simple stubs until they are implemented in 86 // netstack, most of these files are configuration related. We use the 87 // value closest to the actual netstack behavior or any empty file, all 88 // of these files will have mode 0444 (read-only for all users). 89 "ip_local_reserved_ports": fs.newInode(ctx, root, 0444, newStaticFile("")), 90 "ipfrag_time": fs.newInode(ctx, root, 0444, newStaticFile("30")), 91 "ip_nonlocal_bind": fs.newInode(ctx, root, 0444, newStaticFile("0")), 92 "ip_no_pmtu_disc": fs.newInode(ctx, root, 0444, newStaticFile("1")), 93 94 // tcp_allowed_congestion_control tell the user what they are able to 95 // do as an unprivledged process so we leave it empty. 96 "tcp_allowed_congestion_control": fs.newInode(ctx, root, 0444, newStaticFile("")), 97 "tcp_available_congestion_control": fs.newInode(ctx, root, 0444, newStaticFile("reno")), 98 "tcp_congestion_control": fs.newInode(ctx, root, 0444, newStaticFile("reno")), 99 100 // Many of the following stub files are features netstack doesn't 101 // support. The unsupported features return "0" to indicate they are 102 // disabled. 103 "tcp_base_mss": fs.newInode(ctx, root, 0444, newStaticFile("1280")), 104 "tcp_dsack": fs.newInode(ctx, root, 0444, newStaticFile("0")), 105 "tcp_early_retrans": fs.newInode(ctx, root, 0444, newStaticFile("0")), 106 "tcp_fack": fs.newInode(ctx, root, 0444, newStaticFile("0")), 107 "tcp_fastopen": fs.newInode(ctx, root, 0444, newStaticFile("0")), 108 "tcp_fastopen_key": fs.newInode(ctx, root, 0444, newStaticFile("")), 109 "tcp_invalid_ratelimit": fs.newInode(ctx, root, 0444, newStaticFile("0")), 110 "tcp_keepalive_intvl": fs.newInode(ctx, root, 0444, newStaticFile("0")), 111 "tcp_keepalive_probes": fs.newInode(ctx, root, 0444, newStaticFile("0")), 112 "tcp_keepalive_time": fs.newInode(ctx, root, 0444, newStaticFile("7200")), 113 "tcp_mtu_probing": fs.newInode(ctx, root, 0444, newStaticFile("0")), 114 "tcp_no_metrics_save": fs.newInode(ctx, root, 0444, newStaticFile("1")), 115 "tcp_probe_interval": fs.newInode(ctx, root, 0444, newStaticFile("0")), 116 "tcp_probe_threshold": fs.newInode(ctx, root, 0444, newStaticFile("0")), 117 "tcp_retries1": fs.newInode(ctx, root, 0444, newStaticFile("3")), 118 "tcp_retries2": fs.newInode(ctx, root, 0444, newStaticFile("15")), 119 "tcp_rfc1337": fs.newInode(ctx, root, 0444, newStaticFile("1")), 120 "tcp_slow_start_after_idle": fs.newInode(ctx, root, 0444, newStaticFile("1")), 121 "tcp_synack_retries": fs.newInode(ctx, root, 0444, newStaticFile("5")), 122 "tcp_syn_retries": fs.newInode(ctx, root, 0444, newStaticFile("3")), 123 "tcp_timestamps": fs.newInode(ctx, root, 0444, newStaticFile("1")), 124 }), 125 "core": fs.newStaticDir(ctx, root, map[string]kernfs.Inode{ 126 "default_qdisc": fs.newInode(ctx, root, 0444, newStaticFile("pfifo_fast")), 127 "message_burst": fs.newInode(ctx, root, 0444, newStaticFile("10")), 128 "message_cost": fs.newInode(ctx, root, 0444, newStaticFile("5")), 129 "optmem_max": fs.newInode(ctx, root, 0444, newStaticFile("0")), 130 "rmem_default": fs.newInode(ctx, root, 0444, newStaticFile("212992")), 131 "rmem_max": fs.newInode(ctx, root, 0444, newStaticFile("212992")), 132 "somaxconn": fs.newInode(ctx, root, 0444, newStaticFile("128")), 133 "wmem_default": fs.newInode(ctx, root, 0444, newStaticFile("212992")), 134 "wmem_max": fs.newInode(ctx, root, 0444, newStaticFile("212992")), 135 }), 136 } 137 } 138 139 return fs.newStaticDir(ctx, root, contents) 140 } 141 142 // mmapMinAddrData implements vfs.DynamicBytesSource for 143 // /proc/sys/vm/mmap_min_addr. 144 // 145 // +stateify savable 146 type mmapMinAddrData struct { 147 kernfs.DynamicBytesFile 148 149 k *kernel.Kernel 150 } 151 152 var _ dynamicInode = (*mmapMinAddrData)(nil) 153 154 // Generate implements vfs.DynamicBytesSource.Generate. 155 func (d *mmapMinAddrData) Generate(ctx context.Context, buf *bytes.Buffer) error { 156 fmt.Fprintf(buf, "%d\n", d.k.Platform.MinUserAddress()) 157 return nil 158 } 159 160 // hostnameData implements vfs.DynamicBytesSource for /proc/sys/kernel/hostname. 161 // 162 // +stateify savable 163 type hostnameData struct { 164 kernfs.DynamicBytesFile 165 } 166 167 var _ dynamicInode = (*hostnameData)(nil) 168 169 // Generate implements vfs.DynamicBytesSource.Generate. 170 func (*hostnameData) Generate(ctx context.Context, buf *bytes.Buffer) error { 171 utsns := kernel.UTSNamespaceFromContext(ctx) 172 buf.WriteString(utsns.HostName()) 173 buf.WriteString("\n") 174 return nil 175 } 176 177 // tcpSackData implements vfs.WritableDynamicBytesSource for 178 // /proc/sys/net/tcp_sack. 179 // 180 // +stateify savable 181 type tcpSackData struct { 182 kernfs.DynamicBytesFile 183 184 stack inet.Stack `state:"wait"` 185 enabled *bool 186 } 187 188 var _ vfs.WritableDynamicBytesSource = (*tcpSackData)(nil) 189 190 // Generate implements vfs.DynamicBytesSource.Generate. 191 func (d *tcpSackData) Generate(ctx context.Context, buf *bytes.Buffer) error { 192 if d.enabled == nil { 193 sack, err := d.stack.TCPSACKEnabled() 194 if err != nil { 195 return err 196 } 197 d.enabled = &sack 198 } 199 200 val := "0\n" 201 if *d.enabled { 202 // Technically, this is not quite compatible with Linux. Linux stores these 203 // as an integer, so if you write "2" into tcp_sack, you should get 2 back. 204 // Tough luck. 205 val = "1\n" 206 } 207 _, err := buf.WriteString(val) 208 return err 209 } 210 211 // Write implements vfs.WritableDynamicBytesSource.Write. 212 func (d *tcpSackData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) { 213 if offset != 0 { 214 // No need to handle partial writes thus far. 215 return 0, linuxerr.EINVAL 216 } 217 if src.NumBytes() == 0 { 218 return 0, nil 219 } 220 221 // Limit the amount of memory allocated. 222 src = src.TakeFirst(hostarch.PageSize - 1) 223 224 var v int32 225 n, err := usermem.CopyInt32StringInVec(ctx, src.IO, src.Addrs, &v, src.Opts) 226 if err != nil { 227 return 0, err 228 } 229 if d.enabled == nil { 230 d.enabled = new(bool) 231 } 232 *d.enabled = v != 0 233 return n, d.stack.SetTCPSACKEnabled(*d.enabled) 234 } 235 236 // tcpRecoveryData implements vfs.WritableDynamicBytesSource for 237 // /proc/sys/net/ipv4/tcp_recovery. 238 // 239 // +stateify savable 240 type tcpRecoveryData struct { 241 kernfs.DynamicBytesFile 242 243 stack inet.Stack `state:"wait"` 244 } 245 246 var _ vfs.WritableDynamicBytesSource = (*tcpRecoveryData)(nil) 247 248 // Generate implements vfs.DynamicBytesSource.Generate. 249 func (d *tcpRecoveryData) Generate(ctx context.Context, buf *bytes.Buffer) error { 250 recovery, err := d.stack.TCPRecovery() 251 if err != nil { 252 return err 253 } 254 255 _, err = buf.WriteString(fmt.Sprintf("%d\n", recovery)) 256 return err 257 } 258 259 // Write implements vfs.WritableDynamicBytesSource.Write. 260 func (d *tcpRecoveryData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) { 261 if offset != 0 { 262 // No need to handle partial writes thus far. 263 return 0, linuxerr.EINVAL 264 } 265 if src.NumBytes() == 0 { 266 return 0, nil 267 } 268 269 // Limit the amount of memory allocated. 270 src = src.TakeFirst(hostarch.PageSize - 1) 271 272 var v int32 273 n, err := usermem.CopyInt32StringInVec(ctx, src.IO, src.Addrs, &v, src.Opts) 274 if err != nil { 275 return 0, err 276 } 277 if err := d.stack.SetTCPRecovery(inet.TCPLossRecovery(v)); err != nil { 278 return 0, err 279 } 280 return n, nil 281 } 282 283 // tcpMemData implements vfs.WritableDynamicBytesSource for 284 // /proc/sys/net/ipv4/tcp_rmem and /proc/sys/net/ipv4/tcp_wmem. 285 // 286 // +stateify savable 287 type tcpMemData struct { 288 kernfs.DynamicBytesFile 289 290 dir tcpMemDir 291 stack inet.Stack `state:"wait"` 292 293 // mu protects against concurrent reads/writes to FDs based on the dentry 294 // backing this byte source. 295 mu sync.Mutex `state:"nosave"` 296 } 297 298 var _ vfs.WritableDynamicBytesSource = (*tcpMemData)(nil) 299 300 // Generate implements vfs.DynamicBytesSource.Generate. 301 func (d *tcpMemData) Generate(ctx context.Context, buf *bytes.Buffer) error { 302 d.mu.Lock() 303 defer d.mu.Unlock() 304 305 size, err := d.readSizeLocked() 306 if err != nil { 307 return err 308 } 309 _, err = buf.WriteString(fmt.Sprintf("%d\t%d\t%d\n", size.Min, size.Default, size.Max)) 310 return err 311 } 312 313 // Write implements vfs.WritableDynamicBytesSource.Write. 314 func (d *tcpMemData) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) { 315 if offset != 0 { 316 // No need to handle partial writes thus far. 317 return 0, linuxerr.EINVAL 318 } 319 if src.NumBytes() == 0 { 320 return 0, nil 321 } 322 d.mu.Lock() 323 defer d.mu.Unlock() 324 325 // Limit the amount of memory allocated. 326 src = src.TakeFirst(hostarch.PageSize - 1) 327 size, err := d.readSizeLocked() 328 if err != nil { 329 return 0, err 330 } 331 buf := []int32{int32(size.Min), int32(size.Default), int32(size.Max)} 332 n, err := usermem.CopyInt32StringsInVec(ctx, src.IO, src.Addrs, buf, src.Opts) 333 if err != nil { 334 return 0, err 335 } 336 newSize := inet.TCPBufferSize{ 337 Min: int(buf[0]), 338 Default: int(buf[1]), 339 Max: int(buf[2]), 340 } 341 if err := d.writeSizeLocked(newSize); err != nil { 342 return 0, err 343 } 344 return n, nil 345 } 346 347 // Precondition: d.mu must be locked. 348 func (d *tcpMemData) readSizeLocked() (inet.TCPBufferSize, error) { 349 switch d.dir { 350 case tcpRMem: 351 return d.stack.TCPReceiveBufferSize() 352 case tcpWMem: 353 return d.stack.TCPSendBufferSize() 354 default: 355 panic(fmt.Sprintf("unknown tcpMemFile type: %v", d.dir)) 356 } 357 } 358 359 // Precondition: d.mu must be locked. 360 func (d *tcpMemData) writeSizeLocked(size inet.TCPBufferSize) error { 361 switch d.dir { 362 case tcpRMem: 363 return d.stack.SetTCPReceiveBufferSize(size) 364 case tcpWMem: 365 return d.stack.SetTCPSendBufferSize(size) 366 default: 367 panic(fmt.Sprintf("unknown tcpMemFile type: %v", d.dir)) 368 } 369 } 370 371 // ipForwarding implements vfs.WritableDynamicBytesSource for 372 // /proc/sys/net/ipv4/ip_forward. 373 // 374 // +stateify savable 375 type ipForwarding struct { 376 kernfs.DynamicBytesFile 377 378 stack inet.Stack `state:"wait"` 379 enabled bool 380 } 381 382 var _ vfs.WritableDynamicBytesSource = (*ipForwarding)(nil) 383 384 // Generate implements vfs.DynamicBytesSource.Generate. 385 func (ipf *ipForwarding) Generate(ctx context.Context, buf *bytes.Buffer) error { 386 val := "0\n" 387 if ipf.enabled { 388 // Technically, this is not quite compatible with Linux. Linux stores these 389 // as an integer, so if you write "2" into tcp_sack, you should get 2 back. 390 // Tough luck. 391 val = "1\n" 392 } 393 buf.WriteString(val) 394 395 return nil 396 } 397 398 // Write implements vfs.WritableDynamicBytesSource.Write. 399 func (ipf *ipForwarding) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) { 400 if offset != 0 { 401 // No need to handle partial writes thus far. 402 return 0, linuxerr.EINVAL 403 } 404 if src.NumBytes() == 0 { 405 return 0, nil 406 } 407 408 // Limit input size so as not to impact performance if input size is large. 409 src = src.TakeFirst(hostarch.PageSize - 1) 410 411 var v int32 412 n, err := usermem.CopyInt32StringInVec(ctx, src.IO, src.Addrs, &v, src.Opts) 413 if err != nil { 414 return 0, err 415 } 416 ipf.enabled = v != 0 417 if err := ipf.stack.SetForwarding(ipv4.ProtocolNumber, ipf.enabled); err != nil { 418 return 0, err 419 } 420 return n, nil 421 } 422 423 // portRange implements vfs.WritableDynamicBytesSource for 424 // /proc/sys/net/ipv4/ip_local_port_range. 425 // 426 // +stateify savable 427 type portRange struct { 428 kernfs.DynamicBytesFile 429 430 stack inet.Stack `state:"wait"` 431 432 // start and end store the port range. We must save/restore this here, 433 // since a netstack instance is created on restore. 434 start *uint16 435 end *uint16 436 } 437 438 var _ vfs.WritableDynamicBytesSource = (*portRange)(nil) 439 440 // Generate implements vfs.DynamicBytesSource.Generate. 441 func (pr *portRange) Generate(ctx context.Context, buf *bytes.Buffer) error { 442 if pr.start == nil { 443 start, end := pr.stack.PortRange() 444 pr.start = &start 445 pr.end = &end 446 } 447 _, err := fmt.Fprintf(buf, "%d %d\n", *pr.start, *pr.end) 448 return err 449 } 450 451 // Write implements vfs.WritableDynamicBytesSource.Write. 452 func (pr *portRange) Write(ctx context.Context, _ *vfs.FileDescription, src usermem.IOSequence, offset int64) (int64, error) { 453 if offset != 0 { 454 // No need to handle partial writes thus far. 455 return 0, linuxerr.EINVAL 456 } 457 if src.NumBytes() == 0 { 458 return 0, nil 459 } 460 461 // Limit input size so as not to impact performance if input size is 462 // large. 463 src = src.TakeFirst(hostarch.PageSize - 1) 464 465 ports := make([]int32, 2) 466 n, err := usermem.CopyInt32StringsInVec(ctx, src.IO, src.Addrs, ports, src.Opts) 467 if err != nil { 468 return 0, err 469 } 470 471 // Port numbers must be uint16s. 472 if ports[0] < 0 || ports[1] < 0 || ports[0] > math.MaxUint16 || ports[1] > math.MaxUint16 { 473 return 0, linuxerr.EINVAL 474 } 475 476 if err := pr.stack.SetPortRange(uint16(ports[0]), uint16(ports[1])); err != nil { 477 return 0, err 478 } 479 if pr.start == nil { 480 pr.start = new(uint16) 481 pr.end = new(uint16) 482 } 483 *pr.start = uint16(ports[0]) 484 *pr.end = uint16(ports[1]) 485 return n, nil 486 }