github.com/google/syzkaller@v0.0.0-20240517125934-c0f1611a36d6/pkg/osutil/osutil_linux.go (about)

     1  // Copyright 2017 syzkaller project authors. All rights reserved.
     2  // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.
     3  
     4  package osutil
     5  
     6  import (
     7  	"fmt"
     8  	"os"
     9  	"os/exec"
    10  	"path/filepath"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  	"syscall"
    15  	"time"
    16  
    17  	"golang.org/x/sys/unix"
    18  )
    19  
    20  // RemoveAll is similar to os.RemoveAll, but can handle more cases.
    21  func RemoveAll(dir string) error {
    22  	files, _ := os.ReadDir(dir)
    23  	for _, f := range files {
    24  		name := filepath.Join(dir, f.Name())
    25  		if f.IsDir() {
    26  			RemoveAll(name)
    27  		}
    28  		unix.Unmount(name, unix.MNT_FORCE)
    29  	}
    30  	if err := os.RemoveAll(dir); err != nil {
    31  		removeImmutable(dir)
    32  		return os.RemoveAll(dir)
    33  	}
    34  	return nil
    35  }
    36  
    37  func SystemMemorySize() uint64 {
    38  	var info syscall.Sysinfo_t
    39  	syscall.Sysinfo(&info)
    40  	return uint64(info.Totalram) // nolint:unconvert
    41  }
    42  
    43  func removeImmutable(fname string) error {
    44  	// Reset FS_XFLAG_IMMUTABLE/FS_XFLAG_APPEND.
    45  	fd, err := syscall.Open(fname, syscall.O_RDONLY, 0)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	defer syscall.Close(fd)
    50  	return unix.IoctlSetPointerInt(fd, unix.FS_IOC_SETFLAGS, 0)
    51  }
    52  
    53  func Sandbox(cmd *exec.Cmd, user, net bool) error {
    54  	enabled, uid, gid, err := initSandbox()
    55  	if err != nil || !enabled {
    56  		return err
    57  	}
    58  	if cmd.SysProcAttr == nil {
    59  		cmd.SysProcAttr = new(syscall.SysProcAttr)
    60  	}
    61  	if net {
    62  		cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC |
    63  			syscall.CLONE_NEWNS | syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID
    64  	}
    65  	if user {
    66  		cmd.SysProcAttr.Credential = &syscall.Credential{
    67  			Uid: uid,
    68  			Gid: gid,
    69  		}
    70  	}
    71  	return nil
    72  }
    73  
    74  func SandboxChown(file string) error {
    75  	enabled, uid, gid, err := initSandbox()
    76  	if err != nil || !enabled {
    77  		return err
    78  	}
    79  	return os.Chown(file, int(uid), int(gid))
    80  }
    81  
    82  var (
    83  	sandboxOnce     sync.Once
    84  	sandboxEnabled  = true
    85  	sandboxUsername = "syzkaller"
    86  	sandboxUID      = ^uint32(0)
    87  	sandboxGID      = ^uint32(0)
    88  )
    89  
    90  func initSandbox() (bool, uint32, uint32, error) {
    91  	sandboxOnce.Do(func() {
    92  		if syscall.Getuid() != 0 || os.Getenv("CI") != "" || os.Getenv("SYZ_DISABLE_SANDBOXING") == "yes" {
    93  			sandboxEnabled = false
    94  			return
    95  		}
    96  		uid, err := usernameToID("-u")
    97  		if err != nil {
    98  			return
    99  		}
   100  		gid, err := usernameToID("-g")
   101  		if err != nil {
   102  			return
   103  		}
   104  		sandboxUID = uid
   105  		sandboxGID = gid
   106  	})
   107  	if sandboxEnabled && sandboxUID == ^uint32(0) {
   108  		return false, 0, 0, fmt.Errorf("user %q is not found, can't sandbox command", sandboxUsername)
   109  	}
   110  	return sandboxEnabled, sandboxUID, sandboxGID, nil
   111  }
   112  
   113  func usernameToID(what string) (uint32, error) {
   114  	out, err := RunCmd(time.Minute, "", "id", what, sandboxUsername)
   115  	if err != nil {
   116  		return 0, err
   117  	}
   118  	str := strings.Trim(string(out), " \t\n")
   119  	id, err := strconv.ParseUint(str, 10, 32)
   120  	if err != nil {
   121  		return 0, err
   122  	}
   123  	return uint32(id), nil
   124  }
   125  
   126  func setPdeathsig(cmd *exec.Cmd, hardKill bool) {
   127  	if cmd.SysProcAttr == nil {
   128  		cmd.SysProcAttr = new(syscall.SysProcAttr)
   129  	}
   130  	if hardKill {
   131  		cmd.SysProcAttr.Pdeathsig = syscall.SIGKILL
   132  	} else {
   133  		cmd.SysProcAttr.Pdeathsig = syscall.SIGTERM
   134  	}
   135  	// We will kill the whole process group.
   136  	cmd.SysProcAttr.Setpgid = true
   137  }
   138  
   139  func killPgroup(cmd *exec.Cmd) {
   140  	syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
   141  }
   142  
   143  func prolongPipe(r, w *os.File) {
   144  	for sz := 128 << 10; sz <= 2<<20; sz *= 2 {
   145  		syscall.Syscall(syscall.SYS_FCNTL, w.Fd(), syscall.F_SETPIPE_SZ, uintptr(sz))
   146  	}
   147  }