github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/cmd/snap-seccomp/main_test.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package main_test
    21  
    22  import (
    23  	"fmt"
    24  	"io/ioutil"
    25  	"math/rand"
    26  	"os"
    27  	"os/exec"
    28  	"path/filepath"
    29  	"strconv"
    30  	"strings"
    31  	"testing"
    32  
    33  	. "gopkg.in/check.v1"
    34  
    35  	"github.com/mvo5/libseccomp-golang"
    36  
    37  	"github.com/snapcore/snapd/arch"
    38  	main "github.com/snapcore/snapd/cmd/snap-seccomp"
    39  	"github.com/snapcore/snapd/osutil"
    40  	"github.com/snapcore/snapd/release"
    41  	"github.com/snapcore/snapd/testutil"
    42  )
    43  
    44  // Hook up check.v1 into the "go test" runner
    45  func Test(t *testing.T) { TestingT(t) }
    46  
    47  type snapSeccompSuite struct {
    48  	seccompBpfLoader     string
    49  	seccompSyscallRunner string
    50  	canCheckCompatArch   bool
    51  }
    52  
    53  var _ = Suite(&snapSeccompSuite{})
    54  
    55  const (
    56  	Deny = iota
    57  	Allow
    58  )
    59  
    60  var seccompBpfLoaderContent = []byte(`
    61  #include <fcntl.h>
    62  #include <inttypes.h>
    63  #include <stdint.h>
    64  #include <stdio.h>
    65  #include <stdlib.h>
    66  #include <string.h>
    67  #include <sys/prctl.h>
    68  #include <unistd.h>
    69  
    70  #include <linux/filter.h>
    71  #include <linux/seccomp.h>
    72  
    73  #define MAX_BPF_SIZE 32 * 1024
    74  
    75  int sc_apply_seccomp_bpf(const char* profile_path)
    76  {
    77      unsigned char bpf[MAX_BPF_SIZE + 1]; // account for EOF
    78      FILE* fp;
    79      fp = fopen(profile_path, "rb");
    80      if (fp == NULL) {
    81          fprintf(stderr, "cannot read %s\n", profile_path);
    82          return -1;
    83      }
    84  
    85      // set 'size' to 1; to get bytes transferred
    86      size_t num_read = fread(bpf, 1, sizeof(bpf), fp);
    87  
    88      if (ferror(fp) != 0) {
    89          perror("fread()");
    90          return -1;
    91      } else if (feof(fp) == 0) {
    92          fprintf(stderr, "file too big\n");
    93          return -1;
    94      }
    95      fclose(fp);
    96  
    97      struct sock_fprog prog = {
    98          .len = num_read / sizeof(struct sock_filter),
    99          .filter = (struct sock_filter*)bpf,
   100      };
   101  
   102      // Set NNP to allow loading seccomp policy into the kernel without
   103      // root
   104      if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
   105          perror("prctl(PR_NO_NEW_PRIVS, 1, 0, 0, 0)");
   106          return -1;
   107      }
   108  
   109      if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
   110          perror("prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, ...) failed");
   111          return -1;
   112      }
   113      return 0;
   114  }
   115  
   116  int main(int argc, char* argv[])
   117  {
   118      int rc = 0;
   119      if (argc < 2) {
   120          fprintf(stderr, "Usage: %s <bpf file> [prog ...]\n", argv[0]);
   121          return 1;
   122      }
   123  
   124      rc = sc_apply_seccomp_bpf(argv[1]);
   125      if (rc != 0)
   126          return -rc;
   127  
   128      execv(argv[2], (char* const*)&argv[2]);
   129      perror("execv failed");
   130      return 1;
   131  }
   132  `)
   133  
   134  var seccompSyscallRunnerContent = []byte(`
   135  #define _GNU_SOURCE
   136  #include <errno.h>
   137  #include <stdlib.h>
   138  #include <sys/syscall.h>
   139  #include <unistd.h>
   140  #include <inttypes.h>
   141  int main(int argc, char** argv)
   142  {
   143      uint32_t l[7];
   144      int syscall_ret, ret = 0;
   145      for (int i = 0; i < 7 && argv[i+1] != NULL; i++) {
   146          errno = 0;
   147          l[i] = strtoll(argv[i + 1], NULL, 10);
   148  	// exit '11' let's us know strtoll failed
   149          if (errno != 0)
   150              syscall(SYS_exit, 11, 0, 0, 0, 0, 0);
   151      }
   152      // There might be architecture-specific requirements. see "man syscall"
   153      // for details.
   154      syscall_ret = syscall(l[0], l[1], l[2], l[3], l[4], l[5], l[6]);
   155      // 911 is our mocked errno
   156      if (syscall_ret < 0 && errno == 911) {
   157          ret = 10;
   158      }
   159      syscall(SYS_exit, ret, 0, 0, 0, 0, 0);
   160      return 0;
   161  }
   162  `)
   163  
   164  func (s *snapSeccompSuite) SetUpSuite(c *C) {
   165  	main.MockErrnoOnDenial(911)
   166  
   167  	// build seccomp-load helper
   168  	s.seccompBpfLoader = filepath.Join(c.MkDir(), "seccomp_bpf_loader")
   169  	err := ioutil.WriteFile(s.seccompBpfLoader+".c", seccompBpfLoaderContent, 0644)
   170  	c.Assert(err, IsNil)
   171  	cmd := exec.Command("gcc", "-Werror", "-Wall", s.seccompBpfLoader+".c", "-o", s.seccompBpfLoader)
   172  	cmd.Stdout = os.Stdout
   173  	cmd.Stderr = os.Stderr
   174  	err = cmd.Run()
   175  	c.Assert(err, IsNil)
   176  
   177  	// build syscall-runner helper
   178  	s.seccompSyscallRunner = filepath.Join(c.MkDir(), "seccomp_syscall_runner")
   179  	err = ioutil.WriteFile(s.seccompSyscallRunner+".c", seccompSyscallRunnerContent, 0644)
   180  	c.Assert(err, IsNil)
   181  
   182  	cmd = exec.Command("gcc", "-std=c99", "-Werror", "-Wall", "-static", s.seccompSyscallRunner+".c", "-o", s.seccompSyscallRunner, "-Wl,-static", "-static-libgcc")
   183  	cmd.Stdout = os.Stdout
   184  	cmd.Stderr = os.Stderr
   185  	err = cmd.Run()
   186  	c.Assert(err, IsNil)
   187  
   188  	// Amazon Linux 2 is 64bit only and there is no multilib support
   189  	s.canCheckCompatArch = !release.DistroLike("amzn")
   190  
   191  	// Build 32bit runner on amd64 to test non-native syscall handling.
   192  	// Ideally we would build for ppc64el->powerpc and arm64->armhf but
   193  	// it seems tricky to find the right gcc-multilib for this.
   194  	if arch.DpkgArchitecture() == "amd64" && s.canCheckCompatArch {
   195  		cmd = exec.Command(cmd.Args[0], cmd.Args[1:]...)
   196  		cmd.Args = append(cmd.Args, "-m32")
   197  		for i, k := range cmd.Args {
   198  			if k == s.seccompSyscallRunner {
   199  				cmd.Args[i] = s.seccompSyscallRunner + ".m32"
   200  			}
   201  		}
   202  		if output, err := cmd.CombinedOutput(); err != nil {
   203  			fmt.Printf("cannot build multi-lib syscall runner: %v\n%s", err, output)
   204  		}
   205  	}
   206  }
   207  
   208  // Runs the policy through the kernel:
   209  //  1. runs main.Compile()
   210  //  2. the program in seccompBpfLoaderContent with the output file as an
   211  //     argument
   212  //  3. the program in seccompBpfLoaderContent loads the output file BPF into
   213  //     the kernel and executes the program in seccompBpfRunnerContent with the
   214  //     syscall and arguments specified by the test
   215  //
   216  // In this manner, in addition to verifying policy syntax we are able to
   217  // unit test the resulting bpf in several ways.
   218  //
   219  // Full testing of applied policy is done elsewhere via spread tests.
   220  //
   221  // Note that we skip testing prctl(PR_SET_ENDIAN) - it causes havoc when
   222  // it is run. We will also need to skip: fadvise64_64,
   223  //   ftruncate64, posix_fadvise, pread64, pwrite64, readahead,
   224  //   sync_file_range, and truncate64.
   225  // Once we start using those. See `man syscall`
   226  func (s *snapSeccompSuite) runBpf(c *C, seccompWhitelist, bpfInput string, expected int) {
   227  	// Common syscalls we need to allow for a minimal statically linked
   228  	// c program.
   229  	//
   230  	// If we compile a test program for each test we can get away with
   231  	// a even smaller set of syscalls: execve,exit essentially. But it
   232  	// means a much longer test run (30s vs 2s). Commit d288d89 contains
   233  	// the code for this.
   234  	common := `
   235  execve
   236  uname
   237  brk
   238  arch_prctl
   239  readlink
   240  access
   241  sysinfo
   242  exit
   243  # i386
   244  set_thread_area
   245  # armhf
   246  set_tls
   247  # arm64
   248  readlinkat
   249  faccessat
   250  # i386 from amd64
   251  restart_syscall
   252  # libc6 2.31/gcc-9.3
   253  mprotect
   254  `
   255  	bpfPath := filepath.Join(c.MkDir(), "bpf")
   256  	err := main.Compile([]byte(common+seccompWhitelist), bpfPath)
   257  	c.Assert(err, IsNil)
   258  
   259  	// default syscall runner
   260  	syscallRunner := s.seccompSyscallRunner
   261  
   262  	// syscallName;arch;arg1,arg2...
   263  	l := strings.Split(bpfInput, ";")
   264  	syscallName := l[0]
   265  	syscallArch := "native"
   266  	if len(l) > 1 {
   267  		syscallArch = l[1]
   268  	}
   269  
   270  	syscallNr, err := seccomp.GetSyscallFromName(syscallName)
   271  	c.Assert(err, IsNil)
   272  
   273  	// Check if we want to test non-native architecture
   274  	// handling. Doing this via the in-kernel tests is tricky as
   275  	// we need a kernel that can run the architecture and a
   276  	// compiler that can produce the required binaries. Currently
   277  	// we only test amd64 running i386 here.
   278  	if syscallArch != "native" {
   279  		syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.DpkgArchToScmpArch(syscallArch))
   280  		c.Assert(err, IsNil)
   281  
   282  		switch syscallArch {
   283  		case "amd64":
   284  			// default syscallRunner
   285  		case "i386":
   286  			syscallRunner = s.seccompSyscallRunner + ".m32"
   287  		default:
   288  			c.Errorf("unexpected non-native arch: %s", syscallArch)
   289  		}
   290  	}
   291  	switch {
   292  	case syscallNr == -101:
   293  		// "socket"
   294  		// see libseccomp: _s390x_sock_demux(), _x86_sock_demux()
   295  		// the -101 is translated to 359 (socket)
   296  		syscallNr = 359
   297  	case syscallNr == -10165:
   298  		// "mknod" on arm64 is not available at all on arm64
   299  		// only "mknodat" but libseccomp will not generate a
   300  		// "mknodat" whitelist, it geneates a whitelist with
   301  		// syscall -10165 (!?!) so we cannot test this.
   302  		c.Skip("skipping mknod tests on arm64")
   303  	case syscallNr < 0:
   304  		c.Errorf("failed to resolve %v: %v", l[0], syscallNr)
   305  		return
   306  	}
   307  
   308  	var syscallRunnerArgs [7]string
   309  	syscallRunnerArgs[0] = strconv.FormatInt(int64(syscallNr), 10)
   310  	if len(l) > 2 {
   311  		args := strings.Split(l[2], ",")
   312  		for i := range args {
   313  			// init with random number argument
   314  			syscallArg := (uint64)(rand.Uint32())
   315  			// override if the test specifies a specific number;
   316  			// this must match main.go:readNumber()
   317  			if nr, ok := main.SeccompResolver[args[i]]; ok {
   318  				syscallArg = nr
   319  			} else if nr, err := strconv.ParseUint(args[i], 10, 32); err == nil {
   320  				syscallArg = nr
   321  			} else if nr, err := strconv.ParseInt(args[i], 10, 32); err == nil {
   322  				syscallArg = uint64(uint32(nr))
   323  			}
   324  			syscallRunnerArgs[i+1] = strconv.FormatUint(syscallArg, 10)
   325  		}
   326  	}
   327  
   328  	cmd := exec.Command(s.seccompBpfLoader, bpfPath, syscallRunner, syscallRunnerArgs[0], syscallRunnerArgs[1], syscallRunnerArgs[2], syscallRunnerArgs[3], syscallRunnerArgs[4], syscallRunnerArgs[5], syscallRunnerArgs[6])
   329  	cmd.Stdin = os.Stdin
   330  	cmd.Stdout = os.Stdout
   331  	cmd.Stderr = os.Stderr
   332  	err = cmd.Run()
   333  	// the exit code of the test binary is either 0 or 10, everything
   334  	// else is unexpected (segv, strtoll failure, ...)
   335  	exitCode, e := osutil.ExitCode(err)
   336  	c.Assert(e, IsNil)
   337  	c.Assert(exitCode == 0 || exitCode == 10, Equals, true, Commentf("unexpected exit code: %v for %v - test setup broken", exitCode, seccompWhitelist))
   338  	switch expected {
   339  	case Allow:
   340  		if err != nil {
   341  			c.Fatalf("unexpected error for %q (failed to run %q)", seccompWhitelist, err)
   342  		}
   343  	case Deny:
   344  		if err == nil {
   345  			c.Fatalf("unexpected success for %q %q (ran but should have failed)", seccompWhitelist, bpfInput)
   346  		}
   347  	default:
   348  		c.Fatalf("unknown expected result %v", expected)
   349  	}
   350  }
   351  
   352  func (s *snapSeccompSuite) TestUnrestricted(c *C) {
   353  	inp := "@unrestricted\n"
   354  	outPath := filepath.Join(c.MkDir(), "bpf")
   355  	err := main.Compile([]byte(inp), outPath)
   356  	c.Assert(err, IsNil)
   357  
   358  	c.Check(outPath, testutil.FileEquals, inp)
   359  }
   360  
   361  // TestCompile iterates over a range of textual seccomp whitelist rules and
   362  // mocked kernel syscall input. For each rule, the test consists of compiling
   363  // the rule into a bpf program and then running that program on a virtual bpf
   364  // machine and comparing the bpf machine output to the specified expected
   365  // output and seccomp operation. Eg:
   366  //    {"<rule>", "<mocked kernel input>", <seccomp result>}
   367  //
   368  // Eg to test that the rule 'read >=2' is allowed with 'read(2)' and 'read(3)'
   369  // and denied with 'read(1)' and 'read(0)', add the following tests:
   370  //    {"read >=2", "read;native;2", Allow},
   371  //    {"read >=2", "read;native;3", Allow},
   372  //    {"read >=2", "read;native;1", main.SeccompRetKill},
   373  //    {"read >=2", "read;native;0", main.SeccompRetKill},
   374  func (s *snapSeccompSuite) TestCompile(c *C) {
   375  
   376  	for _, t := range []struct {
   377  		seccompWhitelist string
   378  		bpfInput         string
   379  		expected         int
   380  	}{
   381  		// special
   382  		{"@complain", "execve", Allow},
   383  
   384  		// trivial allow
   385  		{"read", "read", Allow},
   386  		{"read\nwrite\nexecve\n", "write", Allow},
   387  
   388  		// trivial denial
   389  		{"read", "ioctl", Deny},
   390  
   391  		// test argument filtering syntax, we currently support:
   392  		//   >=, <=, !, <, >, |
   393  		// modifiers.
   394  
   395  		// reads >= 2 are ok
   396  		{"read >=2", "read;native;2", Allow},
   397  		{"read >=2", "read;native;3", Allow},
   398  		// but not reads < 2, those get killed
   399  		{"read >=2", "read;native;1", Deny},
   400  		{"read >=2", "read;native;0", Deny},
   401  
   402  		// reads <= 2 are ok
   403  		{"read <=2", "read;native;0", Allow},
   404  		{"read <=2", "read;native;1", Allow},
   405  		{"read <=2", "read;native;2", Allow},
   406  		// but not reads >2, those get killed
   407  		{"read <=2", "read;native;3", Deny},
   408  		{"read <=2", "read;native;4", Deny},
   409  
   410  		// reads that are not 2 are ok
   411  		{"read !2", "read;native;1", Allow},
   412  		{"read !2", "read;native;3", Allow},
   413  		// but not 2, this gets killed
   414  		{"read !2", "read;native;2", Deny},
   415  
   416  		// reads > 2 are ok
   417  		{"read >2", "read;native;4", Allow},
   418  		{"read >2", "read;native;3", Allow},
   419  		// but not reads <= 2, those get killed
   420  		{"read >2", "read;native;2", Deny},
   421  		{"read >2", "read;native;1", Deny},
   422  
   423  		// reads < 2 are ok
   424  		{"read <2", "read;native;0", Allow},
   425  		{"read <2", "read;native;1", Allow},
   426  		// but not reads >= 2, those get killed
   427  		{"read <2", "read;native;2", Deny},
   428  		{"read <2", "read;native;3", Deny},
   429  
   430  		// FIXME: test maskedEqual better
   431  		{"read |1", "read;native;1", Allow},
   432  		{"read |1", "read;native;2", Deny},
   433  
   434  		// exact match, reads == 2 are ok
   435  		{"read 2", "read;native;2", Allow},
   436  		// but not those != 2
   437  		{"read 2", "read;native;3", Deny},
   438  		{"read 2", "read;native;1", Deny},
   439  
   440  		// test actual syscalls and their expected usage
   441  		{"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow},
   442  		{"ioctl - TIOCSTI", "ioctl;native;-,99", Deny},
   443  		{"ioctl - !TIOCSTI", "ioctl;native;-,TIOCSTI", Deny},
   444  
   445  		// test_bad_seccomp_filter_args_clone
   446  		{"setns - CLONE_NEWNET", "setns;native;-,99", Deny},
   447  		{"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow},
   448  
   449  		// test_bad_seccomp_filter_args_mknod
   450  		{"mknod - |S_IFIFO", "mknod;native;-,S_IFIFO", Allow},
   451  		{"mknod - |S_IFIFO", "mknod;native;-,99", Deny},
   452  
   453  		// test_bad_seccomp_filter_args_prctl
   454  		{"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;PR_CAP_AMBIENT_RAISE", Allow},
   455  		{"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;99", Deny},
   456  
   457  		// test_bad_seccomp_filter_args_prio
   458  		{"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;PRIO_PROCESS,0,19", Allow},
   459  		{"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;99", Deny},
   460  
   461  		// test_bad_seccomp_filter_args_quotactl
   462  		{"quotactl Q_GETQUOTA", "quotactl;native;Q_GETQUOTA", Allow},
   463  		{"quotactl Q_GETQUOTA", "quotactl;native;99", Deny},
   464  
   465  		// test_bad_seccomp_filter_args_termios
   466  		{"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow},
   467  		{"ioctl - TIOCSTI", "ioctl;native;-,99", Deny},
   468  
   469  		// u:root g:root
   470  		{"fchown - u:root g:root", "fchown;native;-,0,0", Allow},
   471  		{"fchown - u:root g:root", "fchown;native;-,99,0", Deny},
   472  		{"chown - u:root g:root", "chown;native;-,0,0", Allow},
   473  		{"chown - u:root g:root", "chown;native;-,99,0", Deny},
   474  
   475  		// u:root -1
   476  		{"chown - u:root -1", "chown;native;-,0,-1", Allow},
   477  		{"chown - u:root -1", "chown;native;-,99,-1", Deny},
   478  		{"chown - -1 u:root", "chown;native;-,-1,0", Allow},
   479  		{"chown - -1 u:root", "chown;native;-,99,0", Deny},
   480  		{"chown - -1 -1", "chown;native;-,-1,-1", Allow},
   481  		{"chown - -1 -1", "chown;native;-,99,-1", Deny},
   482  	} {
   483  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   484  	}
   485  }
   486  
   487  // TestCompileSocket runs in a separate tests so that only this part
   488  // can be skipped when "socketcall()" is used instead of "socket()".
   489  //
   490  // Some architectures (i386, s390x) use the "socketcall" syscall instead
   491  // of "socket". This is the case on Ubuntu 14.04, 17.04, 17.10
   492  func (s *snapSeccompSuite) TestCompileSocket(c *C) {
   493  	if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" {
   494  		c.Skip("14.04/i386 uses socketcall which cannot be tested here")
   495  	}
   496  
   497  	for _, t := range []struct {
   498  		seccompWhitelist string
   499  		bpfInput         string
   500  		expected         int
   501  	}{
   502  
   503  		// test_bad_seccomp_filter_args_socket
   504  		{"socket AF_UNIX", "socket;native;AF_UNIX", Allow},
   505  		{"socket AF_UNIX", "socket;native;99", Deny},
   506  		{"socket - SOCK_STREAM", "socket;native;-,SOCK_STREAM", Allow},
   507  		{"socket - SOCK_STREAM", "socket;native;-,99", Deny},
   508  		{"socket AF_CONN", "socket;native;AF_CONN", Allow},
   509  		{"socket AF_CONN", "socket;native;99", Deny},
   510  	} {
   511  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   512  	}
   513  
   514  }
   515  
   516  func (s *snapSeccompSuite) TestCompileBadInput(c *C) {
   517  	for _, t := range []struct {
   518  		inp    string
   519  		errMsg string
   520  	}{
   521  		// test_bad_seccomp_filter_args_clone (various typos in input)
   522  		{"setns - CLONE_NEWNE", `cannot parse line: cannot parse token "CLONE_NEWNE" \(line "setns - CLONE_NEWNE"\)`},
   523  		{"setns - CLONE_NEWNETT", `cannot parse line: cannot parse token "CLONE_NEWNETT" \(line "setns - CLONE_NEWNETT"\)`},
   524  		{"setns - CL0NE_NEWNET", `cannot parse line: cannot parse token "CL0NE_NEWNET" \(line "setns - CL0NE_NEWNET"\)`},
   525  
   526  		// test_bad_seccomp_filter_args_mknod (various typos in input)
   527  		{"mknod - |S_IFIF", `cannot parse line: cannot parse token "S_IFIF" \(line "mknod - |S_IFIF"\)`},
   528  		{"mknod - |S_IFIFOO", `cannot parse line: cannot parse token "S_IFIFOO" \(line "mknod - |S_IFIFOO"\)`},
   529  		{"mknod - |S_!FIFO", `cannot parse line: cannot parse token "S_IFIFO" \(line "mknod - |S_!FIFO"\)`},
   530  
   531  		// test_bad_seccomp_filter_args_null
   532  		{"socket S\x00CK_STREAM", `cannot parse line: cannot parse token .*`},
   533  		{"socket SOCK_STREAM\x00bad stuff", `cannot parse line: cannot parse token .*`},
   534  
   535  		// test_bad_seccomp_filter_args
   536  		{"setpriority bar", `cannot parse line: cannot parse token "bar" .*`},
   537  		{"setpriority -1", `cannot parse line: cannot parse token "-1" .*`},
   538  		{"setpriority 0 - -1 0", `cannot parse line: cannot parse token "-1" .*`},
   539  		{"setpriority --10", `cannot parse line: cannot parse token "--10" .*`},
   540  		{"setpriority 0:10", `cannot parse line: cannot parse token "0:10" .*`},
   541  		{"setpriority 0-10", `cannot parse line: cannot parse token "0-10" .*`},
   542  		{"setpriority 0,1", `cannot parse line: cannot parse token "0,1" .*`},
   543  		{"setpriority 0x0", `cannot parse line: cannot parse token "0x0" .*`},
   544  		{"setpriority a1", `cannot parse line: cannot parse token "a1" .*`},
   545  		{"setpriority 1a", `cannot parse line: cannot parse token "1a" .*`},
   546  		{"setpriority 1-", `cannot parse line: cannot parse token "1-" .*`},
   547  		{"setpriority 1\\ 2", `cannot parse line: cannot parse token "1\\\\" .*`},
   548  		{"setpriority 1\\n2", `cannot parse line: cannot parse token "1\\\\n2" .*`},
   549  		// 1 bigger than uint32
   550  		{"chown 0 4294967296", `cannot parse line: cannot parse token "4294967296" .*`},
   551  		// 1 smaller than int32
   552  		{"chown - 0 -2147483649", `cannot parse line: cannot parse token "-2147483649" .*`},
   553  		{"setpriority 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", `cannot parse line: cannot parse token "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" .*`},
   554  		{"mbind - - - - - - 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`},
   555  		{"mbind 1 2 3 4 5 6 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`},
   556  		// test_bad_seccomp_filter_args_prctl
   557  		{"prctl PR_GET_SECCOM", `cannot parse line: cannot parse token "PR_GET_SECCOM" .*`},
   558  		{"prctl PR_GET_SECCOMPP", `cannot parse line: cannot parse token "PR_GET_SECCOMPP" .*`},
   559  		{"prctl PR_GET_SECC0MP", `cannot parse line: cannot parse token "PR_GET_SECC0MP" .*`},
   560  		{"prctl PR_CAP_AMBIENT_RAIS", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAIS" .*`},
   561  		{"prctl PR_CAP_AMBIENT_RAISEE", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAISEE" .*`},
   562  		// test_bad_seccomp_filter_args_prio
   563  		{"setpriority PRIO_PROCES 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCES" .*`},
   564  		{"setpriority PRIO_PROCESSS 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCESSS" .*`},
   565  		{"setpriority PRIO_PR0CESS 0 >=0", `cannot parse line: cannot parse token "PRIO_PR0CESS" .*`},
   566  		// test_bad_seccomp_filter_args_quotactl
   567  		{"quotactl Q_GETQUOT", `cannot parse line: cannot parse token "Q_GETQUOT" .*`},
   568  		{"quotactl Q_GETQUOTAA", `cannot parse line: cannot parse token "Q_GETQUOTAA" .*`},
   569  		{"quotactl Q_GETQU0TA", `cannot parse line: cannot parse token "Q_GETQU0TA" .*`},
   570  		// test_bad_seccomp_filter_args_socket
   571  		{"socket AF_UNI", `cannot parse line: cannot parse token "AF_UNI" .*`},
   572  		{"socket AF_UNIXX", `cannot parse line: cannot parse token "AF_UNIXX" .*`},
   573  		{"socket AF_UN!X", `cannot parse line: cannot parse token "AF_UN!X" .*`},
   574  		{"socket - SOCK_STREA", `cannot parse line: cannot parse token "SOCK_STREA" .*`},
   575  		{"socket - SOCK_STREAMM", `cannot parse line: cannot parse token "SOCK_STREAMM" .*`},
   576  		{"socket - NETLINK_ROUT", `cannot parse line: cannot parse token "NETLINK_ROUT" .*`},
   577  		{"socket - NETLINK_ROUTEE", `cannot parse line: cannot parse token "NETLINK_ROUTEE" .*`},
   578  		{"socket - NETLINK_R0UTE", `cannot parse line: cannot parse token "NETLINK_R0UTE" .*`},
   579  		// test_bad_seccomp_filter_args_termios
   580  		{"ioctl - TIOCST", `cannot parse line: cannot parse token "TIOCST" .*`},
   581  		{"ioctl - TIOCSTII", `cannot parse line: cannot parse token "TIOCSTII" .*`},
   582  		{"ioctl - TIOCST1", `cannot parse line: cannot parse token "TIOCST1" .*`},
   583  		// ensure missing numbers are caught
   584  		{"setpriority >", `cannot parse line: cannot parse token ">" .*`},
   585  		{"setpriority >=", `cannot parse line: cannot parse token ">=" .*`},
   586  		{"setpriority <", `cannot parse line: cannot parse token "<" .*`},
   587  		{"setpriority <=", `cannot parse line: cannot parse token "<=" .*`},
   588  		{"setpriority |", `cannot parse line: cannot parse token "|" .*`},
   589  		{"setpriority !", `cannot parse line: cannot parse token "!" .*`},
   590  
   591  		// u:<username>
   592  		{"setuid :root", `cannot parse line: cannot parse token ":root" .*`},
   593  		{"setuid u:", `cannot parse line: cannot parse token "u:" \(line "setuid u:"\): "" must be a valid username`},
   594  		{"setuid u:!", `cannot parse line: cannot parse token "u:!" \(line "setuid u:!"\): "!" must be a valid username`},
   595  		{"setuid u:b@d|npu+", `cannot parse line: cannot parse token "u:b@d|npu+" \(line "setuid u:b@d|npu+"\): "b@d|npu+" must be a valid username`},
   596  		{"setuid u:snap|bad", `cannot parse line: cannot parse token "u:snap|bad" \(line "setuid u:snap|bad"\): "snap|bad" must be a valid username`},
   597  		{"setuid U:root", `cannot parse line: cannot parse token "U:root" .*`},
   598  		{"setuid u:nonexistent", `cannot parse line: cannot parse token "u:nonexistent" \(line "setuid u:nonexistent"\): user: unknown user nonexistent`},
   599  		// g:<groupname>
   600  		{"setgid g:", `cannot parse line: cannot parse token "g:" \(line "setgid g:"\): "" must be a valid group name`},
   601  		{"setgid g:!", `cannot parse line: cannot parse token "g:!" \(line "setgid g:!"\): "!" must be a valid group name`},
   602  		{"setgid g:b@d|npu+", `cannot parse line: cannot parse token "g:b@d|npu+" \(line "setgid g:b@d|npu+"\): "b@d|npu+" must be a valid group name`},
   603  		{"setgid g:snap|bad", `cannot parse line: cannot parse token "g:snap|bad" \(line "setgid g:snap|bad"\): "snap|bad" must be a valid group name`},
   604  		{"setgid G:root", `cannot parse line: cannot parse token "G:root" .*`},
   605  		{"setgid g:nonexistent", `cannot parse line: cannot parse token "g:nonexistent" \(line "setgid g:nonexistent"\): group: unknown group nonexistent`},
   606  	} {
   607  		outPath := filepath.Join(c.MkDir(), "bpf")
   608  		err := main.Compile([]byte(t.inp), outPath)
   609  		c.Check(err, ErrorMatches, t.errMsg, Commentf("%q errors in unexpected ways, got: %q expected %q", t.inp, err, t.errMsg))
   610  	}
   611  }
   612  
   613  // ported from test_restrictions_working_args_socket
   614  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsSocket(c *C) {
   615  	if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" {
   616  		c.Skip("14.04/i386 uses socketcall which cannot be tested here")
   617  	}
   618  
   619  	for _, pre := range []string{"AF", "PF"} {
   620  		for _, i := range []string{"UNIX", "LOCAL", "INET", "INET6", "IPX", "NETLINK", "X25", "AX25", "ATMPVC", "APPLETALK", "PACKET", "ALG", "CAN", "BRIDGE", "NETROM", "ROSE", "NETBEUI", "SECURITY", "KEY", "ASH", "ECONET", "SNA", "IRDA", "PPPOX", "WANPIPE", "BLUETOOTH", "RDS", "LLC", "TIPC", "IUCV", "RXRPC", "ISDN", "PHONET", "IEEE802154", "CAIF", "NFC", "VSOCK", "MPLS", "IB"} {
   621  			seccompWhitelist := fmt.Sprintf("socket %s_%s", pre, i)
   622  			bpfInputGood := fmt.Sprintf("socket;native;%s_%s", pre, i)
   623  			bpfInputBad := "socket;native;99999"
   624  			s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   625  			s.runBpf(c, seccompWhitelist, bpfInputBad, Deny)
   626  
   627  			for _, j := range []string{"SOCK_STREAM", "SOCK_DGRAM", "SOCK_SEQPACKET", "SOCK_RAW", "SOCK_RDM", "SOCK_PACKET"} {
   628  				seccompWhitelist := fmt.Sprintf("socket %s_%s %s", pre, i, j)
   629  				bpfInputGood := fmt.Sprintf("socket;native;%s_%s,%s", pre, i, j)
   630  				bpfInputBad := fmt.Sprintf("socket;native;%s_%s,9999", pre, i)
   631  				s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   632  				s.runBpf(c, seccompWhitelist, bpfInputBad, Deny)
   633  			}
   634  		}
   635  	}
   636  
   637  	for _, i := range []string{"NETLINK_ROUTE", "NETLINK_USERSOCK", "NETLINK_FIREWALL", "NETLINK_SOCK_DIAG", "NETLINK_NFLOG", "NETLINK_XFRM", "NETLINK_SELINUX", "NETLINK_ISCSI", "NETLINK_AUDIT", "NETLINK_FIB_LOOKUP", "NETLINK_CONNECTOR", "NETLINK_NETFILTER", "NETLINK_IP6_FW", "NETLINK_DNRTMSG", "NETLINK_KOBJECT_UEVENT", "NETLINK_GENERIC", "NETLINK_SCSITRANSPORT", "NETLINK_ECRYPTFS", "NETLINK_RDMA", "NETLINK_CRYPTO", "NETLINK_INET_DIAG"} {
   638  		for _, j := range []string{"AF_NETLINK", "PF_NETLINK"} {
   639  			seccompWhitelist := fmt.Sprintf("socket %s - %s", j, i)
   640  			bpfInputGood := fmt.Sprintf("socket;native;%s,0,%s", j, i)
   641  			bpfInputBad := fmt.Sprintf("socket;native;%s,0,99", j)
   642  			s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   643  			s.runBpf(c, seccompWhitelist, bpfInputBad, Deny)
   644  		}
   645  	}
   646  }
   647  
   648  // ported from test_restrictions_working_args_quotactl
   649  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsQuotactl(c *C) {
   650  	for _, arg := range []string{"Q_QUOTAON", "Q_QUOTAOFF", "Q_GETQUOTA", "Q_SETQUOTA", "Q_GETINFO", "Q_SETINFO", "Q_GETFMT", "Q_SYNC", "Q_XQUOTAON", "Q_XQUOTAOFF", "Q_XGETQUOTA", "Q_XSETQLIM", "Q_XGETQSTAT", "Q_XQUOTARM"} {
   651  		// good input
   652  		seccompWhitelist := fmt.Sprintf("quotactl %s", arg)
   653  		bpfInputGood := fmt.Sprintf("quotactl;native;%s", arg)
   654  		s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   655  		// bad input
   656  		for _, bad := range []string{"quotactl;native;99999", "read;native;"} {
   657  			s.runBpf(c, seccompWhitelist, bad, Deny)
   658  		}
   659  	}
   660  }
   661  
   662  // ported from test_restrictions_working_args_prctl
   663  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrctl(c *C) {
   664  	for _, arg := range []string{"PR_CAP_AMBIENT", "PR_CAP_AMBIENT_RAISE", "PR_CAP_AMBIENT_LOWER", "PR_CAP_AMBIENT_IS_SET", "PR_CAP_AMBIENT_CLEAR_ALL", "PR_CAPBSET_READ", "PR_CAPBSET_DROP", "PR_SET_CHILD_SUBREAPER", "PR_GET_CHILD_SUBREAPER", "PR_SET_DUMPABLE", "PR_GET_DUMPABLE", "PR_GET_ENDIAN", "PR_SET_FPEMU", "PR_GET_FPEMU", "PR_SET_FPEXC", "PR_GET_FPEXC", "PR_SET_KEEPCAPS", "PR_GET_KEEPCAPS", "PR_MCE_KILL", "PR_MCE_KILL_GET", "PR_SET_MM", "PR_SET_MM_START_CODE", "PR_SET_MM_END_CODE", "PR_SET_MM_START_DATA", "PR_SET_MM_END_DATA", "PR_SET_MM_START_STACK", "PR_SET_MM_START_BRK", "PR_SET_MM_BRK", "PR_SET_MM_ARG_START", "PR_SET_MM_ARG_END", "PR_SET_MM_ENV_START", "PR_SET_MM_ENV_END", "PR_SET_MM_AUXV", "PR_SET_MM_EXE_FILE", "PR_MPX_ENABLE_MANAGEMENT", "PR_MPX_DISABLE_MANAGEMENT", "PR_SET_NAME", "PR_GET_NAME", "PR_SET_NO_NEW_PRIVS", "PR_GET_NO_NEW_PRIVS", "PR_SET_PDEATHSIG", "PR_GET_PDEATHSIG", "PR_SET_PTRACER", "PR_SET_SECCOMP", "PR_GET_SECCOMP", "PR_SET_SECUREBITS", "PR_GET_SECUREBITS", "PR_SET_THP_DISABLE", "PR_TASK_PERF_EVENTS_DISABLE", "PR_TASK_PERF_EVENTS_ENABLE", "PR_GET_THP_DISABLE", "PR_GET_TID_ADDRESS", "PR_SET_TIMERSLACK", "PR_GET_TIMERSLACK", "PR_SET_TIMING", "PR_GET_TIMING", "PR_SET_TSC", "PR_GET_TSC", "PR_SET_UNALIGN", "PR_GET_UNALIGN"} {
   665  		// good input
   666  		seccompWhitelist := fmt.Sprintf("prctl %s", arg)
   667  		bpfInputGood := fmt.Sprintf("prctl;native;%s", arg)
   668  		s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   669  		// bad input
   670  		for _, bad := range []string{"prctl;native;99999", "setpriority;native;"} {
   671  			s.runBpf(c, seccompWhitelist, bad, Deny)
   672  		}
   673  
   674  		if arg == "PR_CAP_AMBIENT" {
   675  			for _, j := range []string{"PR_CAP_AMBIENT_RAISE", "PR_CAP_AMBIENT_LOWER", "PR_CAP_AMBIENT_IS_SET", "PR_CAP_AMBIENT_CLEAR_ALL"} {
   676  				seccompWhitelist := fmt.Sprintf("prctl %s %s", arg, j)
   677  				bpfInputGood := fmt.Sprintf("prctl;native;%s,%s", arg, j)
   678  				s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   679  				for _, bad := range []string{
   680  					fmt.Sprintf("prctl;native;%s,99999", arg),
   681  					"setpriority;native;",
   682  				} {
   683  					s.runBpf(c, seccompWhitelist, bad, Deny)
   684  				}
   685  			}
   686  		}
   687  	}
   688  }
   689  
   690  // ported from test_restrictions_working_args_clone
   691  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsClone(c *C) {
   692  	for _, t := range []struct {
   693  		seccompWhitelist string
   694  		bpfInput         string
   695  		expected         int
   696  	}{
   697  		// good input
   698  		{"setns - CLONE_NEWIPC", "setns;native;-,CLONE_NEWIPC", Allow},
   699  		{"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow},
   700  		{"setns - CLONE_NEWNS", "setns;native;-,CLONE_NEWNS", Allow},
   701  		{"setns - CLONE_NEWPID", "setns;native;-,CLONE_NEWPID", Allow},
   702  		{"setns - CLONE_NEWUSER", "setns;native;-,CLONE_NEWUSER", Allow},
   703  		{"setns - CLONE_NEWUTS", "setns;native;-,CLONE_NEWUTS", Allow},
   704  		// bad input
   705  		{"setns - CLONE_NEWIPC", "setns;native;-,99", Deny},
   706  		{"setns - CLONE_NEWNET", "setns;native;-,99", Deny},
   707  		{"setns - CLONE_NEWNS", "setns;native;-,99", Deny},
   708  		{"setns - CLONE_NEWPID", "setns;native;-,99", Deny},
   709  		{"setns - CLONE_NEWUSER", "setns;native;-,99", Deny},
   710  		{"setns - CLONE_NEWUTS", "setns;native;-,99", Deny},
   711  	} {
   712  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   713  	}
   714  }
   715  
   716  // ported from test_restrictions_working_args_mknod
   717  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsMknod(c *C) {
   718  	for _, t := range []struct {
   719  		seccompWhitelist string
   720  		bpfInput         string
   721  		expected         int
   722  	}{
   723  		// good input
   724  		{"mknod - S_IFREG", "mknod;native;-,S_IFREG", Allow},
   725  		{"mknod - S_IFCHR", "mknod;native;-,S_IFCHR", Allow},
   726  		{"mknod - S_IFBLK", "mknod;native;-,S_IFBLK", Allow},
   727  		{"mknod - S_IFIFO", "mknod;native;-,S_IFIFO", Allow},
   728  		{"mknod - S_IFSOCK", "mknod;native;-,S_IFSOCK", Allow},
   729  		// bad input
   730  		{"mknod - S_IFREG", "mknod;native;-,999", Deny},
   731  		{"mknod - S_IFCHR", "mknod;native;-,999", Deny},
   732  		{"mknod - S_IFBLK", "mknod;native;-,999", Deny},
   733  		{"mknod - S_IFIFO", "mknod;native;-,999", Deny},
   734  		{"mknod - S_IFSOCK", "mknod;native;-,999", Deny},
   735  	} {
   736  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   737  	}
   738  }
   739  
   740  // ported from test_restrictions_working_args_prio
   741  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrio(c *C) {
   742  	for _, t := range []struct {
   743  		seccompWhitelist string
   744  		bpfInput         string
   745  		expected         int
   746  	}{
   747  		// good input
   748  		{"setpriority PRIO_PROCESS", "setpriority;native;PRIO_PROCESS", Allow},
   749  		{"setpriority PRIO_PGRP", "setpriority;native;PRIO_PGRP", Allow},
   750  		{"setpriority PRIO_USER", "setpriority;native;PRIO_USER", Allow},
   751  		// bad input
   752  		{"setpriority PRIO_PROCESS", "setpriority;native;99", Deny},
   753  		{"setpriority PRIO_PGRP", "setpriority;native;99", Deny},
   754  		{"setpriority PRIO_USER", "setpriority;native;99", Deny},
   755  	} {
   756  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   757  	}
   758  }
   759  
   760  // ported from test_restrictions_working_args_termios
   761  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsTermios(c *C) {
   762  	for _, t := range []struct {
   763  		seccompWhitelist string
   764  		bpfInput         string
   765  		expected         int
   766  	}{
   767  		// good input
   768  		{"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow},
   769  		// bad input
   770  		{"ioctl - TIOCSTI", "quotactl;native;-,99", Deny},
   771  	} {
   772  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   773  	}
   774  }
   775  
   776  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsUidGid(c *C) {
   777  	// while 'root' user usually has uid 0, 'daemon' user uid may vary
   778  	// across distributions, best lookup the uid directly
   779  	daemonUid, err := osutil.FindUid("daemon")
   780  
   781  	if err != nil {
   782  		c.Skip("daemon user not available, perhaps we are in a buildroot jail")
   783  	}
   784  
   785  	for _, t := range []struct {
   786  		seccompWhitelist string
   787  		bpfInput         string
   788  		expected         int
   789  	}{
   790  		// good input. 'root' is guaranteed to be '0' and 'daemon' uid
   791  		// was determined at runtime
   792  		{"setuid u:root", "setuid;native;0", Allow},
   793  		{"setuid u:daemon", fmt.Sprintf("setuid;native;%v", daemonUid), Allow},
   794  		{"setgid g:root", "setgid;native;0", Allow},
   795  		{"setgid g:daemon", fmt.Sprintf("setgid;native;%v", daemonUid), Allow},
   796  		// bad input
   797  		{"setuid u:root", "setuid;native;99", Deny},
   798  		{"setuid u:daemon", "setuid;native;99", Deny},
   799  		{"setgid g:root", "setgid;native;99", Deny},
   800  		{"setgid g:daemon", "setgid;native;99", Deny},
   801  	} {
   802  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   803  	}
   804  }
   805  
   806  func (s *snapSeccompSuite) TestCompatArchWorks(c *C) {
   807  	if !s.canCheckCompatArch {
   808  		c.Skip("multi-lib syscall runner not supported by this host")
   809  	}
   810  	for _, t := range []struct {
   811  		arch             string
   812  		seccompWhitelist string
   813  		bpfInput         string
   814  		expected         int
   815  	}{
   816  		// on amd64 we add compat i386
   817  		{"amd64", "read", "read;i386", Allow},
   818  		{"amd64", "read", "read;amd64", Allow},
   819  		{"amd64", "chown - 0 -1", "chown;i386;-,0,-1", Allow},
   820  		{"amd64", "chown - 0 -1", "chown;amd64;-,0,-1", Allow},
   821  		{"amd64", "chown - 0 -1", "chown;i386;-,99,-1", Deny},
   822  		{"amd64", "chown - 0 -1", "chown;amd64;-,99,-1", Deny},
   823  		{"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,-1,-1", Allow},
   824  		{"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,-1,-1", Allow},
   825  		{"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,99,-1", Deny},
   826  		{"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,99,-1", Deny},
   827  	} {
   828  		// It is tricky to mock the architecture here because
   829  		// seccomp is always adding the native arch to the seccomp
   830  		// filter and it will silently discard arches that have
   831  		// an endian mismatch:
   832  		// https://github.com/seccomp/libseccomp/issues/86
   833  		//
   834  		// This means we can not just
   835  		//    main.MockArchDpkgArchitecture(t.arch)
   836  		// here because on endian mismatch the arch will *not* be
   837  		// added
   838  		if arch.DpkgArchitecture() == t.arch {
   839  			s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   840  		}
   841  	}
   842  }