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