github.com/apptainer/singularity@v3.1.1+incompatible/pkg/util/fs/proc/proc_linux_test.go (about)

     1  // Copyright (c) 2018, Sylabs Inc. All rights reserved.
     2  // This software is licensed under a 3-clause BSD license. Please consult the
     3  // LICENSE.md file distributed with the sources of this project regarding your
     4  // rights to use or distribute this software.
     5  
     6  package proc
     7  
     8  import (
     9  	"io/ioutil"
    10  	"os"
    11  	"os/exec"
    12  	"syscall"
    13  	"testing"
    14  
    15  	"github.com/sylabs/singularity/internal/pkg/test"
    16  )
    17  
    18  func TestHasFilesystem(t *testing.T) {
    19  	test.DropPrivilege(t)
    20  	defer test.ResetPrivilege(t)
    21  
    22  	p, err := HasFilesystem("proc")
    23  	if err != nil {
    24  		t.Error(err)
    25  	}
    26  	if !p {
    27  		t.Errorf("proc filesystem not present")
    28  	}
    29  
    30  	p, err = HasFilesystem("42fs")
    31  	if err != nil {
    32  		t.Error(err)
    33  	}
    34  	if p {
    35  		t.Errorf("42fs should not be in supported filesystems")
    36  	}
    37  }
    38  
    39  var mountInfoData = `22 28 0:21 / /sys rw,nosuid,nodev,noexec,relatime shared:7 - sysfs sysfs rw
    40  23 28 0:4 / /proc rw,nosuid,nodev,noexec,relatime shared:13 - proc proc rw
    41  24 28 0:6 / /dev rw,nosuid,relatime shared:2 - devtmpfs udev rw,size=8110616k,nr_inodes=2027654,mode=755
    42  25 24 0:22 / /dev/pts rw,nosuid,noexec,relatime shared:3 - devpts devpts rw,gid=5,mode=620,ptmxmode=000
    43  26 28 0:23 / /run rw,nosuid,noexec,relatime shared:5 - tmpfs tmpfs rw,size=1635872k,mode=755
    44  28 0 8:1 / / rw,noatime,nodiratime shared:1 - ext4 /dev/sda1 rw,discard,errors=remount-ro,data=ordered
    45  29 22 0:7 / /sys/kernel/security rw,nosuid,nodev,noexec,relatime shared:8 - securityfs securityfs rw
    46  30 24 0:25 / /dev/shm rw,nosuid,nodev shared:4 - tmpfs tmpfs rw
    47  31 26 0:26 / /run/lock rw,nosuid,nodev,noexec,relatime shared:6 - tmpfs tmpfs rw,size=5120k
    48  32 22 0:27 / /sys/fs/cgroup ro,nosuid,nodev,noexec shared:9 - tmpfs tmpfs ro,mode=755
    49  33 32 0:28 / /sys/fs/cgroup/unified rw,nosuid,nodev,noexec,relatime shared:10 - cgroup2 cgroup rw,nsdelegate
    50  34 32 0:29 / /sys/fs/cgroup/systemd rw,nosuid,nodev,noexec,relatime shared:11 - cgroup cgroup rw,xattr,name=systemd
    51  35 22 0:30 / /sys/fs/pstore rw,nosuid,nodev,noexec,relatime shared:12 - pstore pstore rw
    52  36 32 0:31 / /sys/fs/cgroup/devices rw,nosuid,nodev,noexec,relatime shared:14 - cgroup cgroup rw,devices
    53  37 32 0:32 / /sys/fs/cgroup/rdma rw,nosuid,nodev,noexec,relatime shared:15 - cgroup cgroup rw,rdma
    54  38 32 0:33 / /sys/fs/cgroup/cpuset rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,cpuset
    55  39 32 0:34 / /sys/fs/cgroup/cpu,cpuacct rw,nosuid,nodev,noexec,relatime shared:17 - cgroup cgroup rw,cpu,cpuacct
    56  40 32 0:35 / /sys/fs/cgroup/pids rw,nosuid,nodev,noexec,relatime shared:18 - cgroup cgroup rw,pids
    57  41 32 0:36 / /sys/fs/cgroup/hugetlb rw,nosuid,nodev,noexec,relatime shared:19 - cgroup cgroup rw,hugetlb
    58  42 32 0:37 / /sys/fs/cgroup/net_cls,net_prio rw,nosuid,nodev,noexec,relatime shared:20 - cgroup cgroup rw,net_cls,net_prio
    59  43 32 0:38 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,freezer
    60  44 32 0:39 / /sys/fs/cgroup/perf_event rw,nosuid,nodev,noexec,relatime shared:22 - cgroup cgroup rw,perf_event
    61  45 32 0:40 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:23 - cgroup cgroup rw,memory
    62  46 32 0:41 / /sys/fs/cgroup/blkio rw,nosuid,nodev,noexec,relatime shared:24 - cgroup cgroup rw,blkio
    63  47 23 0:42 / /proc/sys/fs/binfmt_misc rw,relatime shared:25 - autofs systemd-1 rw,fd=36,pgrp=1,timeout=0,minproto=5,maxproto=5,direct,pipe_ino=1423
    64  48 24 0:19 / /dev/mqueue rw,relatime shared:26 - mqueue mqueue rw
    65  49 24 0:43 / /dev/hugepages rw,relatime shared:27 - hugetlbfs hugetlbfs rw,pagesize=2M
    66  50 22 0:8 / /sys/kernel/debug rw,relatime shared:28 - debugfs debugfs rw
    67  51 26 0:44 / /run/rpc_pipefs rw,relatime shared:29 - rpc_pipefs sunrpc rw
    68  52 22 0:20 / /sys/kernel/config rw,relatime shared:30 - configfs configfs rw
    69  53 22 0:45 / /sys/fs/fuse/connections rw,relatime shared:31 - fusectl fusectl rw
    70  54 23 0:46 / /proc/fs/nfsd rw,relatime shared:32 - nfsd nfsd rw
    71  88 28 253:1 / /home rw,noatime,nodiratime shared:33 - ext4 /dev/mapper/pdc_egggdecf1 rw,errors=remount-ro,stripe=64
    72  90 47 0:47 / /proc/sys/fs/binfmt_misc rw,relatime shared:34 - binfmt_misc binfmt_misc rw
    73  381 26 0:54 / /run/user/1000 rw,nosuid,nodev,relatime shared:245 - tmpfs tmpfs rw,size=1635868k,mode=700,uid=1000,gid=1000
    74  363 381 0:52 / /run/user/1000/gvfs rw,nosuid,nodev,relatime shared:233 - fuse.gvfsd-fuse gvfsd-fuse rw,user_id=1000,group_id=1000`
    75  
    76  func TestParseMountInfo(t *testing.T) {
    77  	test.DropPrivilege(t)
    78  	defer test.ResetPrivilege(t)
    79  
    80  	if _, err := ParseMountInfo("/proc/self/fakemountinfo"); err == nil {
    81  		t.Errorf("should have failed with non existent path")
    82  	}
    83  	tmpfile, err := ioutil.TempFile("", "mountinfo")
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  	defer os.Remove(tmpfile.Name())
    88  
    89  	if _, err := tmpfile.Write([]byte(mountInfoData)); err != nil {
    90  		t.Fatal(err)
    91  	}
    92  	if err := tmpfile.Close(); err != nil {
    93  		t.Fatal(err)
    94  	}
    95  	m, err := ParseMountInfo(tmpfile.Name())
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	if len(m) != 7 {
   100  		t.Errorf("got %d main parent mount point instead of 7", len(m))
   101  	}
   102  	if len(m["/"]) != 5 {
   103  		t.Errorf("got %d child mount point for '/' instead of 5", len(m["/"]))
   104  	}
   105  	if len(m["/dev"]) != 4 {
   106  		t.Errorf("got %d child mount point for '/dev' instead of 4", len(m["/dev"]))
   107  	}
   108  	if len(m["/sys"]) != 6 {
   109  		t.Errorf("got %d child mount point for '/sys' instead of 6", len(m["/sys"]))
   110  	}
   111  	if len(m["/sys/fs/cgroup"]) != 13 {
   112  		t.Errorf("got %d child mount point for '/sys/fs/cgroup' instead of 13", len(m["/sys/fs/cgroup"]))
   113  	}
   114  	if len(m["/proc"]) != 2 {
   115  		t.Errorf("got %d child mount point for '/proc' instead of 2", len(m["/proc"]))
   116  	}
   117  	if len(m["/run"]) != 3 {
   118  		t.Errorf("got %d child mount point for '/run' instead of 3", len(m["/run"]))
   119  	}
   120  	if len(m["/run/user/1000"]) != 1 {
   121  		t.Errorf("got %d child mount point for '/run/user/1000' instead of 1", len(m["/run/user/1000"]))
   122  	}
   123  }
   124  
   125  func TestExtractPid(t *testing.T) {
   126  	procList := []struct {
   127  		path string
   128  		pid  uint
   129  		fail bool
   130  	}{
   131  		{"/proc/1/fd", 1, false},
   132  		{"/proc/self", 0, true},
   133  		{"/proc/123/ns/pid", 123, false},
   134  		{"/proc/-1", 0, true},
   135  		{"/etc/../proc/1/fd", 0, true},
   136  	}
   137  	for _, pl := range procList {
   138  		pid, err := ExtractPid(pl.path)
   139  		if err != nil && !pl.fail {
   140  			t.Fatal(err)
   141  		}
   142  		if !pl.fail && pid != pl.pid {
   143  			t.Fatalf("should have returned %d as PID instead of %d", pid, pl.pid)
   144  		}
   145  		if pl.fail && err == nil {
   146  			t.Fatalf("extract path %s should have failed", pl.path)
   147  		}
   148  	}
   149  }
   150  
   151  func TestCountChilds(t *testing.T) {
   152  	test.DropPrivilege(t)
   153  	defer test.ResetPrivilege(t)
   154  
   155  	childs, err := CountChilds(1)
   156  	if err != nil {
   157  		t.Fatal(err)
   158  	}
   159  	if childs == 0 {
   160  		t.Fatal("init have no child processes")
   161  	}
   162  	childs, err = CountChilds(0)
   163  	if err == nil {
   164  		t.Fatal("no error reported with PID 0")
   165  	}
   166  }
   167  
   168  func TestReadIDMap(t *testing.T) {
   169  	test.DropPrivilege(t)
   170  	defer test.ResetPrivilege(t)
   171  
   172  	// skip tests if uid_map doesn't exists
   173  	if _, err := os.Stat("/proc/self/uid_map"); os.IsNotExist(err) {
   174  		return
   175  	}
   176  	containerID, hostID, err := ReadIDMap("/proc/self/uid_map")
   177  	if err != nil {
   178  		t.Fatal(err)
   179  	}
   180  	if containerID != 0 || containerID != hostID {
   181  		t.Errorf("")
   182  	}
   183  	containerID, hostID, err = ReadIDMap("/proc/self/gid_map")
   184  	if err != nil {
   185  		t.Fatal(err)
   186  	}
   187  	if containerID != 0 || containerID != hostID {
   188  		t.Errorf("")
   189  	}
   190  }
   191  
   192  func TestParentMount(t *testing.T) {
   193  	test.DropPrivilege(t)
   194  	defer test.ResetPrivilege(t)
   195  
   196  	list := []struct {
   197  		path   string
   198  		parent string
   199  		fail   bool
   200  	}{
   201  		{"/proc_", "", true},
   202  		{"/proc", "/proc", false},
   203  		{"/dev/null", "/dev", false},
   204  		{"/proc/self", "/proc", false},
   205  		{"/proc/fake", "", true},
   206  	}
   207  
   208  	for _, l := range list {
   209  		parent, err := ParentMount(l.path)
   210  		if l.fail && err == nil {
   211  			t.Errorf("%s should fail", l.path)
   212  		} else if !l.fail {
   213  			if err != nil {
   214  				t.Error(err)
   215  			} else if parent != l.parent {
   216  				t.Errorf("mount parent of %s should be %s not %s", l.path, l.parent, parent)
   217  			}
   218  		}
   219  
   220  	}
   221  }
   222  
   223  func TestSetOOMScoreAdj(t *testing.T) {
   224  	test.EnsurePrivilege(t)
   225  
   226  	pid := os.Getpid()
   227  
   228  	list := []struct {
   229  		pid   int
   230  		score int
   231  		fail  bool
   232  	}{
   233  		{pid, 0, false},
   234  		{pid, 10, false},
   235  		{0, 0, true},
   236  	}
   237  
   238  	for _, l := range list {
   239  		err := SetOOMScoreAdj(l.pid, &l.score)
   240  		if l.fail && err == nil {
   241  			t.Errorf("writing %d in /proc/%d/oom_score_adj should have failed", l.score, l.pid)
   242  		} else if !l.fail && err != nil {
   243  			t.Error(err)
   244  		}
   245  	}
   246  }
   247  
   248  func TestHasNamespace(t *testing.T) {
   249  	test.EnsurePrivilege(t)
   250  
   251  	ppid := os.Getppid()
   252  	has, err := HasNamespace(ppid, "net")
   253  	if err != nil {
   254  		t.Error(err)
   255  	}
   256  	if has {
   257  		t.Errorf("namespaces should be identical")
   258  	}
   259  
   260  	cmd := exec.Command("/bin/cat")
   261  	pipe, err := cmd.StdinPipe()
   262  	if err != nil {
   263  		t.Fatal(err)
   264  	}
   265  
   266  	cmd.SysProcAttr = &syscall.SysProcAttr{}
   267  	cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWPID
   268  
   269  	if err := cmd.Start(); err != nil {
   270  		t.Fatal(err)
   271  	}
   272  
   273  	has, err = HasNamespace(cmd.Process.Pid, "pid")
   274  	if !has {
   275  		t.Errorf("pid namespace should be different")
   276  	}
   277  
   278  	pipe.Close()
   279  
   280  	cmd.Wait()
   281  }