github.com/rigado/snapd@v2.42.5-go-mod+incompatible/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; 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  `
   253  	bpfPath := filepath.Join(c.MkDir(), "bpf")
   254  	err := main.Compile([]byte(common+seccompWhitelist), bpfPath)
   255  	c.Assert(err, IsNil)
   256  
   257  	// default syscall runner
   258  	syscallRunner := s.seccompSyscallRunner
   259  
   260  	// syscallName;arch;arg1,arg2...
   261  	l := strings.Split(bpfInput, ";")
   262  	syscallName := l[0]
   263  	syscallArch := "native"
   264  	if len(l) > 1 {
   265  		syscallArch = l[1]
   266  	}
   267  
   268  	syscallNr, err := seccomp.GetSyscallFromName(syscallName)
   269  	c.Assert(err, IsNil)
   270  
   271  	// Check if we want to test non-native architecture
   272  	// handling. Doing this via the in-kernel tests is tricky as
   273  	// we need a kernel that can run the architecture and a
   274  	// compiler that can produce the required binaries. Currently
   275  	// we only test amd64 running i386 here.
   276  	if syscallArch != "native" {
   277  		syscallNr, err = seccomp.GetSyscallFromNameByArch(syscallName, main.DpkgArchToScmpArch(syscallArch))
   278  		c.Assert(err, IsNil)
   279  
   280  		switch syscallArch {
   281  		case "amd64":
   282  			// default syscallRunner
   283  		case "i386":
   284  			syscallRunner = s.seccompSyscallRunner + ".m32"
   285  		default:
   286  			c.Errorf("unexpected non-native arch: %s", syscallArch)
   287  		}
   288  	}
   289  	switch {
   290  	case syscallNr == -101:
   291  		// "socket"
   292  		// see libseccomp: _s390x_sock_demux(), _x86_sock_demux()
   293  		// the -101 is translated to 359 (socket)
   294  		syscallNr = 359
   295  	case syscallNr == -10165:
   296  		// "mknod" on arm64 is not available at all on arm64
   297  		// only "mknodat" but libseccomp will not generate a
   298  		// "mknodat" whitelist, it geneates a whitelist with
   299  		// syscall -10165 (!?!) so we cannot test this.
   300  		c.Skip("skipping mknod tests on arm64")
   301  	case syscallNr < 0:
   302  		c.Errorf("failed to resolve %v: %v", l[0], syscallNr)
   303  		return
   304  	}
   305  
   306  	var syscallRunnerArgs [7]string
   307  	syscallRunnerArgs[0] = strconv.FormatInt(int64(syscallNr), 10)
   308  	if len(l) > 2 {
   309  		args := strings.Split(l[2], ",")
   310  		for i := range args {
   311  			// init with random number argument
   312  			syscallArg := (uint64)(rand.Uint32())
   313  			// override if the test specifies a specific number;
   314  			// this must match main.go:readNumber()
   315  			if nr, ok := main.SeccompResolver[args[i]]; ok {
   316  				syscallArg = nr
   317  			} else if nr, err := strconv.ParseUint(args[i], 10, 32); err == nil {
   318  				syscallArg = nr
   319  			} else if nr, err := strconv.ParseInt(args[i], 10, 32); err == nil {
   320  				syscallArg = uint64(uint32(nr))
   321  			}
   322  			syscallRunnerArgs[i+1] = strconv.FormatUint(syscallArg, 10)
   323  		}
   324  	}
   325  
   326  	cmd := exec.Command(s.seccompBpfLoader, bpfPath, syscallRunner, syscallRunnerArgs[0], syscallRunnerArgs[1], syscallRunnerArgs[2], syscallRunnerArgs[3], syscallRunnerArgs[4], syscallRunnerArgs[5], syscallRunnerArgs[6])
   327  	cmd.Stdin = os.Stdin
   328  	cmd.Stdout = os.Stdout
   329  	cmd.Stderr = os.Stderr
   330  	err = cmd.Run()
   331  	switch expected {
   332  	case Allow:
   333  		if err != nil {
   334  			c.Fatalf("unexpected error for %q (failed to run %q)", seccompWhitelist, err)
   335  		}
   336  	case Deny:
   337  		if err == nil {
   338  			c.Fatalf("unexpected success for %q %q (ran but should have failed)", seccompWhitelist, bpfInput)
   339  		}
   340  	default:
   341  		c.Fatalf("unknown expected result %v", expected)
   342  	}
   343  }
   344  
   345  func (s *snapSeccompSuite) TestUnrestricted(c *C) {
   346  	inp := "@unrestricted\n"
   347  	outPath := filepath.Join(c.MkDir(), "bpf")
   348  	err := main.Compile([]byte(inp), outPath)
   349  	c.Assert(err, IsNil)
   350  
   351  	c.Check(outPath, testutil.FileEquals, inp)
   352  }
   353  
   354  // TestCompile iterates over a range of textual seccomp whitelist rules and
   355  // mocked kernel syscall input. For each rule, the test consists of compiling
   356  // the rule into a bpf program and then running that program on a virtual bpf
   357  // machine and comparing the bpf machine output to the specified expected
   358  // output and seccomp operation. Eg:
   359  //    {"<rule>", "<mocked kernel input>", <seccomp result>}
   360  //
   361  // Eg to test that the rule 'read >=2' is allowed with 'read(2)' and 'read(3)'
   362  // and denied with 'read(1)' and 'read(0)', add the following tests:
   363  //    {"read >=2", "read;native;2", Allow},
   364  //    {"read >=2", "read;native;3", Allow},
   365  //    {"read >=2", "read;native;1", main.SeccompRetKill},
   366  //    {"read >=2", "read;native;0", main.SeccompRetKill},
   367  func (s *snapSeccompSuite) TestCompile(c *C) {
   368  
   369  	for _, t := range []struct {
   370  		seccompWhitelist string
   371  		bpfInput         string
   372  		expected         int
   373  	}{
   374  		// special
   375  		{"@complain", "execve", Allow},
   376  
   377  		// trivial allow
   378  		{"read", "read", Allow},
   379  		{"read\nwrite\nexecve\n", "write", Allow},
   380  
   381  		// trivial denial
   382  		{"read", "ioctl", Deny},
   383  
   384  		// test argument filtering syntax, we currently support:
   385  		//   >=, <=, !, <, >, |
   386  		// modifiers.
   387  
   388  		// reads >= 2 are ok
   389  		{"read >=2", "read;native;2", Allow},
   390  		{"read >=2", "read;native;3", Allow},
   391  		// but not reads < 2, those get killed
   392  		{"read >=2", "read;native;1", Deny},
   393  		{"read >=2", "read;native;0", Deny},
   394  
   395  		// reads <= 2 are ok
   396  		{"read <=2", "read;native;0", Allow},
   397  		{"read <=2", "read;native;1", Allow},
   398  		{"read <=2", "read;native;2", Allow},
   399  		// but not reads >2, those get killed
   400  		{"read <=2", "read;native;3", Deny},
   401  		{"read <=2", "read;native;4", Deny},
   402  
   403  		// reads that are not 2 are ok
   404  		{"read !2", "read;native;1", Allow},
   405  		{"read !2", "read;native;3", Allow},
   406  		// but not 2, this gets killed
   407  		{"read !2", "read;native;2", Deny},
   408  
   409  		// reads > 2 are ok
   410  		{"read >2", "read;native;4", Allow},
   411  		{"read >2", "read;native;3", Allow},
   412  		// but not reads <= 2, those get killed
   413  		{"read >2", "read;native;2", Deny},
   414  		{"read >2", "read;native;1", Deny},
   415  
   416  		// reads < 2 are ok
   417  		{"read <2", "read;native;0", Allow},
   418  		{"read <2", "read;native;1", Allow},
   419  		// but not reads >= 2, those get killed
   420  		{"read <2", "read;native;2", Deny},
   421  		{"read <2", "read;native;3", Deny},
   422  
   423  		// FIXME: test maskedEqual better
   424  		{"read |1", "read;native;1", Allow},
   425  		{"read |1", "read;native;2", Deny},
   426  
   427  		// exact match, reads == 2 are ok
   428  		{"read 2", "read;native;2", Allow},
   429  		// but not those != 2
   430  		{"read 2", "read;native;3", Deny},
   431  		{"read 2", "read;native;1", Deny},
   432  
   433  		// test actual syscalls and their expected usage
   434  		{"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow},
   435  		{"ioctl - TIOCSTI", "ioctl;native;-,99", Deny},
   436  		{"ioctl - !TIOCSTI", "ioctl;native;-,TIOCSTI", Deny},
   437  
   438  		// test_bad_seccomp_filter_args_clone
   439  		{"setns - CLONE_NEWNET", "setns;native;-,99", Deny},
   440  		{"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow},
   441  
   442  		// test_bad_seccomp_filter_args_mknod
   443  		{"mknod - |S_IFIFO", "mknod;native;-,S_IFIFO", Allow},
   444  		{"mknod - |S_IFIFO", "mknod;native;-,99", Deny},
   445  
   446  		// test_bad_seccomp_filter_args_prctl
   447  		{"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;PR_CAP_AMBIENT_RAISE", Allow},
   448  		{"prctl PR_CAP_AMBIENT_RAISE", "prctl;native;99", Deny},
   449  
   450  		// test_bad_seccomp_filter_args_prio
   451  		{"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;PRIO_PROCESS,0,19", Allow},
   452  		{"setpriority PRIO_PROCESS 0 >=0", "setpriority;native;99", Deny},
   453  
   454  		// test_bad_seccomp_filter_args_quotactl
   455  		{"quotactl Q_GETQUOTA", "quotactl;native;Q_GETQUOTA", Allow},
   456  		{"quotactl Q_GETQUOTA", "quotactl;native;99", Deny},
   457  
   458  		// test_bad_seccomp_filter_args_termios
   459  		{"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow},
   460  		{"ioctl - TIOCSTI", "ioctl;native;-,99", Deny},
   461  
   462  		// u:root g:root
   463  		{"fchown - u:root g:root", "fchown;native;-,0,0", Allow},
   464  		{"fchown - u:root g:root", "fchown;native;-,99,0", Deny},
   465  		{"chown - u:root g:root", "chown;native;-,0,0", Allow},
   466  		{"chown - u:root g:root", "chown;native;-,99,0", Deny},
   467  
   468  		// u:root -1
   469  		{"chown - u:root -1", "chown;native;-,0,-1", Allow},
   470  		{"chown - u:root -1", "chown;native;-,99,-1", Deny},
   471  		{"chown - -1 u:root", "chown;native;-,-1,0", Allow},
   472  		{"chown - -1 u:root", "chown;native;-,99,0", Deny},
   473  		{"chown - -1 -1", "chown;native;-,-1,-1", Allow},
   474  		{"chown - -1 -1", "chown;native;-,99,-1", Deny},
   475  	} {
   476  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   477  	}
   478  }
   479  
   480  // TestCompileSocket runs in a separate tests so that only this part
   481  // can be skipped when "socketcall()" is used instead of "socket()".
   482  //
   483  // Some architectures (i386, s390x) use the "socketcall" syscall instead
   484  // of "socket". This is the case on Ubuntu 14.04, 17.04, 17.10
   485  func (s *snapSeccompSuite) TestCompileSocket(c *C) {
   486  	if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" {
   487  		c.Skip("14.04/i386 uses socketcall which cannot be tested here")
   488  	}
   489  
   490  	for _, t := range []struct {
   491  		seccompWhitelist string
   492  		bpfInput         string
   493  		expected         int
   494  	}{
   495  
   496  		// test_bad_seccomp_filter_args_socket
   497  		{"socket AF_UNIX", "socket;native;AF_UNIX", Allow},
   498  		{"socket AF_UNIX", "socket;native;99", Deny},
   499  		{"socket - SOCK_STREAM", "socket;native;-,SOCK_STREAM", Allow},
   500  		{"socket - SOCK_STREAM", "socket;native;-,99", Deny},
   501  		{"socket AF_CONN", "socket;native;AF_CONN", Allow},
   502  		{"socket AF_CONN", "socket;native;99", Deny},
   503  	} {
   504  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   505  	}
   506  
   507  }
   508  
   509  func (s *snapSeccompSuite) TestCompileBadInput(c *C) {
   510  	for _, t := range []struct {
   511  		inp    string
   512  		errMsg string
   513  	}{
   514  		// test_bad_seccomp_filter_args_clone (various typos in input)
   515  		{"setns - CLONE_NEWNE", `cannot parse line: cannot parse token "CLONE_NEWNE" \(line "setns - CLONE_NEWNE"\)`},
   516  		{"setns - CLONE_NEWNETT", `cannot parse line: cannot parse token "CLONE_NEWNETT" \(line "setns - CLONE_NEWNETT"\)`},
   517  		{"setns - CL0NE_NEWNET", `cannot parse line: cannot parse token "CL0NE_NEWNET" \(line "setns - CL0NE_NEWNET"\)`},
   518  
   519  		// test_bad_seccomp_filter_args_mknod (various typos in input)
   520  		{"mknod - |S_IFIF", `cannot parse line: cannot parse token "S_IFIF" \(line "mknod - |S_IFIF"\)`},
   521  		{"mknod - |S_IFIFOO", `cannot parse line: cannot parse token "S_IFIFOO" \(line "mknod - |S_IFIFOO"\)`},
   522  		{"mknod - |S_!FIFO", `cannot parse line: cannot parse token "S_IFIFO" \(line "mknod - |S_!FIFO"\)`},
   523  
   524  		// test_bad_seccomp_filter_args_null
   525  		{"socket S\x00CK_STREAM", `cannot parse line: cannot parse token .*`},
   526  		{"socket SOCK_STREAM\x00bad stuff", `cannot parse line: cannot parse token .*`},
   527  
   528  		// test_bad_seccomp_filter_args
   529  		{"setpriority bar", `cannot parse line: cannot parse token "bar" .*`},
   530  		{"setpriority -1", `cannot parse line: cannot parse token "-1" .*`},
   531  		{"setpriority 0 - -1 0", `cannot parse line: cannot parse token "-1" .*`},
   532  		{"setpriority --10", `cannot parse line: cannot parse token "--10" .*`},
   533  		{"setpriority 0:10", `cannot parse line: cannot parse token "0:10" .*`},
   534  		{"setpriority 0-10", `cannot parse line: cannot parse token "0-10" .*`},
   535  		{"setpriority 0,1", `cannot parse line: cannot parse token "0,1" .*`},
   536  		{"setpriority 0x0", `cannot parse line: cannot parse token "0x0" .*`},
   537  		{"setpriority a1", `cannot parse line: cannot parse token "a1" .*`},
   538  		{"setpriority 1a", `cannot parse line: cannot parse token "1a" .*`},
   539  		{"setpriority 1-", `cannot parse line: cannot parse token "1-" .*`},
   540  		{"setpriority 1\\ 2", `cannot parse line: cannot parse token "1\\\\" .*`},
   541  		{"setpriority 1\\n2", `cannot parse line: cannot parse token "1\\\\n2" .*`},
   542  		// 1 bigger than uint32
   543  		{"chown 0 4294967296", `cannot parse line: cannot parse token "4294967296" .*`},
   544  		// 1 smaller than int32
   545  		{"chown - 0 -2147483649", `cannot parse line: cannot parse token "-2147483649" .*`},
   546  		{"setpriority 999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999", `cannot parse line: cannot parse token "999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" .*`},
   547  		{"mbind - - - - - - 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`},
   548  		{"mbind 1 2 3 4 5 6 7", `cannot parse line: too many arguments specified for syscall 'mbind' in line.*`},
   549  		// test_bad_seccomp_filter_args_prctl
   550  		{"prctl PR_GET_SECCOM", `cannot parse line: cannot parse token "PR_GET_SECCOM" .*`},
   551  		{"prctl PR_GET_SECCOMPP", `cannot parse line: cannot parse token "PR_GET_SECCOMPP" .*`},
   552  		{"prctl PR_GET_SECC0MP", `cannot parse line: cannot parse token "PR_GET_SECC0MP" .*`},
   553  		{"prctl PR_CAP_AMBIENT_RAIS", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAIS" .*`},
   554  		{"prctl PR_CAP_AMBIENT_RAISEE", `cannot parse line: cannot parse token "PR_CAP_AMBIENT_RAISEE" .*`},
   555  		// test_bad_seccomp_filter_args_prio
   556  		{"setpriority PRIO_PROCES 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCES" .*`},
   557  		{"setpriority PRIO_PROCESSS 0 >=0", `cannot parse line: cannot parse token "PRIO_PROCESSS" .*`},
   558  		{"setpriority PRIO_PR0CESS 0 >=0", `cannot parse line: cannot parse token "PRIO_PR0CESS" .*`},
   559  		// test_bad_seccomp_filter_args_quotactl
   560  		{"quotactl Q_GETQUOT", `cannot parse line: cannot parse token "Q_GETQUOT" .*`},
   561  		{"quotactl Q_GETQUOTAA", `cannot parse line: cannot parse token "Q_GETQUOTAA" .*`},
   562  		{"quotactl Q_GETQU0TA", `cannot parse line: cannot parse token "Q_GETQU0TA" .*`},
   563  		// test_bad_seccomp_filter_args_socket
   564  		{"socket AF_UNI", `cannot parse line: cannot parse token "AF_UNI" .*`},
   565  		{"socket AF_UNIXX", `cannot parse line: cannot parse token "AF_UNIXX" .*`},
   566  		{"socket AF_UN!X", `cannot parse line: cannot parse token "AF_UN!X" .*`},
   567  		{"socket - SOCK_STREA", `cannot parse line: cannot parse token "SOCK_STREA" .*`},
   568  		{"socket - SOCK_STREAMM", `cannot parse line: cannot parse token "SOCK_STREAMM" .*`},
   569  		{"socket - NETLINK_ROUT", `cannot parse line: cannot parse token "NETLINK_ROUT" .*`},
   570  		{"socket - NETLINK_ROUTEE", `cannot parse line: cannot parse token "NETLINK_ROUTEE" .*`},
   571  		{"socket - NETLINK_R0UTE", `cannot parse line: cannot parse token "NETLINK_R0UTE" .*`},
   572  		// test_bad_seccomp_filter_args_termios
   573  		{"ioctl - TIOCST", `cannot parse line: cannot parse token "TIOCST" .*`},
   574  		{"ioctl - TIOCSTII", `cannot parse line: cannot parse token "TIOCSTII" .*`},
   575  		{"ioctl - TIOCST1", `cannot parse line: cannot parse token "TIOCST1" .*`},
   576  		// ensure missing numbers are caught
   577  		{"setpriority >", `cannot parse line: cannot parse token ">" .*`},
   578  		{"setpriority >=", `cannot parse line: cannot parse token ">=" .*`},
   579  		{"setpriority <", `cannot parse line: cannot parse token "<" .*`},
   580  		{"setpriority <=", `cannot parse line: cannot parse token "<=" .*`},
   581  		{"setpriority |", `cannot parse line: cannot parse token "|" .*`},
   582  		{"setpriority !", `cannot parse line: cannot parse token "!" .*`},
   583  
   584  		// u:<username>
   585  		{"setuid :root", `cannot parse line: cannot parse token ":root" .*`},
   586  		{"setuid u:", `cannot parse line: cannot parse token "u:" \(line "setuid u:"\): "" must be a valid username`},
   587  		{"setuid u:!", `cannot parse line: cannot parse token "u:!" \(line "setuid u:!"\): "!" must be a valid username`},
   588  		{"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`},
   589  		{"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`},
   590  		{"setuid U:root", `cannot parse line: cannot parse token "U:root" .*`},
   591  		{"setuid u:nonexistent", `cannot parse line: cannot parse token "u:nonexistent" \(line "setuid u:nonexistent"\): user: unknown user nonexistent`},
   592  		// g:<groupname>
   593  		{"setgid g:", `cannot parse line: cannot parse token "g:" \(line "setgid g:"\): "" must be a valid group name`},
   594  		{"setgid g:!", `cannot parse line: cannot parse token "g:!" \(line "setgid g:!"\): "!" must be a valid group name`},
   595  		{"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`},
   596  		{"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`},
   597  		{"setgid G:root", `cannot parse line: cannot parse token "G:root" .*`},
   598  		{"setgid g:nonexistent", `cannot parse line: cannot parse token "g:nonexistent" \(line "setgid g:nonexistent"\): group: unknown group nonexistent`},
   599  	} {
   600  		outPath := filepath.Join(c.MkDir(), "bpf")
   601  		err := main.Compile([]byte(t.inp), outPath)
   602  		c.Check(err, ErrorMatches, t.errMsg, Commentf("%q errors in unexpected ways, got: %q expected %q", t.inp, err, t.errMsg))
   603  	}
   604  }
   605  
   606  // ported from test_restrictions_working_args_socket
   607  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsSocket(c *C) {
   608  	if release.ReleaseInfo.ID == "ubuntu" && release.ReleaseInfo.VersionID == "14.04" {
   609  		c.Skip("14.04/i386 uses socketcall which cannot be tested here")
   610  	}
   611  
   612  	for _, pre := range []string{"AF", "PF"} {
   613  		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"} {
   614  			seccompWhitelist := fmt.Sprintf("socket %s_%s", pre, i)
   615  			bpfInputGood := fmt.Sprintf("socket;native;%s_%s", pre, i)
   616  			bpfInputBad := "socket;native;99999"
   617  			s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   618  			s.runBpf(c, seccompWhitelist, bpfInputBad, Deny)
   619  
   620  			for _, j := range []string{"SOCK_STREAM", "SOCK_DGRAM", "SOCK_SEQPACKET", "SOCK_RAW", "SOCK_RDM", "SOCK_PACKET"} {
   621  				seccompWhitelist := fmt.Sprintf("socket %s_%s %s", pre, i, j)
   622  				bpfInputGood := fmt.Sprintf("socket;native;%s_%s,%s", pre, i, j)
   623  				bpfInputBad := fmt.Sprintf("socket;native;%s_%s,9999", pre, i)
   624  				s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   625  				s.runBpf(c, seccompWhitelist, bpfInputBad, Deny)
   626  			}
   627  		}
   628  	}
   629  
   630  	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"} {
   631  		for _, j := range []string{"AF_NETLINK", "PF_NETLINK"} {
   632  			seccompWhitelist := fmt.Sprintf("socket %s - %s", j, i)
   633  			bpfInputGood := fmt.Sprintf("socket;native;%s,0,%s", j, i)
   634  			bpfInputBad := fmt.Sprintf("socket;native;%s,0,99", j)
   635  			s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   636  			s.runBpf(c, seccompWhitelist, bpfInputBad, Deny)
   637  		}
   638  	}
   639  }
   640  
   641  // ported from test_restrictions_working_args_quotactl
   642  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsQuotactl(c *C) {
   643  	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"} {
   644  		// good input
   645  		seccompWhitelist := fmt.Sprintf("quotactl %s", arg)
   646  		bpfInputGood := fmt.Sprintf("quotactl;native;%s", arg)
   647  		s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   648  		// bad input
   649  		for _, bad := range []string{"quotactl;native;99999", "read;native;"} {
   650  			s.runBpf(c, seccompWhitelist, bad, Deny)
   651  		}
   652  	}
   653  }
   654  
   655  // ported from test_restrictions_working_args_prctl
   656  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrctl(c *C) {
   657  	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"} {
   658  		// good input
   659  		seccompWhitelist := fmt.Sprintf("prctl %s", arg)
   660  		bpfInputGood := fmt.Sprintf("prctl;native;%s", arg)
   661  		s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   662  		// bad input
   663  		for _, bad := range []string{"prctl;native;99999", "setpriority;native;"} {
   664  			s.runBpf(c, seccompWhitelist, bad, Deny)
   665  		}
   666  
   667  		if arg == "PR_CAP_AMBIENT" {
   668  			for _, j := range []string{"PR_CAP_AMBIENT_RAISE", "PR_CAP_AMBIENT_LOWER", "PR_CAP_AMBIENT_IS_SET", "PR_CAP_AMBIENT_CLEAR_ALL"} {
   669  				seccompWhitelist := fmt.Sprintf("prctl %s %s", arg, j)
   670  				bpfInputGood := fmt.Sprintf("prctl;native;%s,%s", arg, j)
   671  				s.runBpf(c, seccompWhitelist, bpfInputGood, Allow)
   672  				for _, bad := range []string{
   673  					fmt.Sprintf("prctl;native;%s,99999", arg),
   674  					"setpriority;native;",
   675  				} {
   676  					s.runBpf(c, seccompWhitelist, bad, Deny)
   677  				}
   678  			}
   679  		}
   680  	}
   681  }
   682  
   683  // ported from test_restrictions_working_args_clone
   684  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsClone(c *C) {
   685  	for _, t := range []struct {
   686  		seccompWhitelist string
   687  		bpfInput         string
   688  		expected         int
   689  	}{
   690  		// good input
   691  		{"setns - CLONE_NEWIPC", "setns;native;-,CLONE_NEWIPC", Allow},
   692  		{"setns - CLONE_NEWNET", "setns;native;-,CLONE_NEWNET", Allow},
   693  		{"setns - CLONE_NEWNS", "setns;native;-,CLONE_NEWNS", Allow},
   694  		{"setns - CLONE_NEWPID", "setns;native;-,CLONE_NEWPID", Allow},
   695  		{"setns - CLONE_NEWUSER", "setns;native;-,CLONE_NEWUSER", Allow},
   696  		{"setns - CLONE_NEWUTS", "setns;native;-,CLONE_NEWUTS", Allow},
   697  		// bad input
   698  		{"setns - CLONE_NEWIPC", "setns;native;-,99", Deny},
   699  		{"setns - CLONE_NEWNET", "setns;native;-,99", Deny},
   700  		{"setns - CLONE_NEWNS", "setns;native;-,99", Deny},
   701  		{"setns - CLONE_NEWPID", "setns;native;-,99", Deny},
   702  		{"setns - CLONE_NEWUSER", "setns;native;-,99", Deny},
   703  		{"setns - CLONE_NEWUTS", "setns;native;-,99", Deny},
   704  	} {
   705  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   706  	}
   707  }
   708  
   709  // ported from test_restrictions_working_args_mknod
   710  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsMknod(c *C) {
   711  	for _, t := range []struct {
   712  		seccompWhitelist string
   713  		bpfInput         string
   714  		expected         int
   715  	}{
   716  		// good input
   717  		{"mknod - S_IFREG", "mknod;native;-,S_IFREG", Allow},
   718  		{"mknod - S_IFCHR", "mknod;native;-,S_IFCHR", Allow},
   719  		{"mknod - S_IFBLK", "mknod;native;-,S_IFBLK", Allow},
   720  		{"mknod - S_IFIFO", "mknod;native;-,S_IFIFO", Allow},
   721  		{"mknod - S_IFSOCK", "mknod;native;-,S_IFSOCK", Allow},
   722  		// bad input
   723  		{"mknod - S_IFREG", "mknod;native;-,999", Deny},
   724  		{"mknod - S_IFCHR", "mknod;native;-,999", Deny},
   725  		{"mknod - S_IFBLK", "mknod;native;-,999", Deny},
   726  		{"mknod - S_IFIFO", "mknod;native;-,999", Deny},
   727  		{"mknod - S_IFSOCK", "mknod;native;-,999", Deny},
   728  	} {
   729  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   730  	}
   731  }
   732  
   733  // ported from test_restrictions_working_args_prio
   734  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsPrio(c *C) {
   735  	for _, t := range []struct {
   736  		seccompWhitelist string
   737  		bpfInput         string
   738  		expected         int
   739  	}{
   740  		// good input
   741  		{"setpriority PRIO_PROCESS", "setpriority;native;PRIO_PROCESS", Allow},
   742  		{"setpriority PRIO_PGRP", "setpriority;native;PRIO_PGRP", Allow},
   743  		{"setpriority PRIO_USER", "setpriority;native;PRIO_USER", Allow},
   744  		// bad input
   745  		{"setpriority PRIO_PROCESS", "setpriority;native;99", Deny},
   746  		{"setpriority PRIO_PGRP", "setpriority;native;99", Deny},
   747  		{"setpriority PRIO_USER", "setpriority;native;99", Deny},
   748  	} {
   749  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   750  	}
   751  }
   752  
   753  // ported from test_restrictions_working_args_termios
   754  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsTermios(c *C) {
   755  	for _, t := range []struct {
   756  		seccompWhitelist string
   757  		bpfInput         string
   758  		expected         int
   759  	}{
   760  		// good input
   761  		{"ioctl - TIOCSTI", "ioctl;native;-,TIOCSTI", Allow},
   762  		// bad input
   763  		{"ioctl - TIOCSTI", "quotactl;native;-,99", Deny},
   764  	} {
   765  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   766  	}
   767  }
   768  
   769  func (s *snapSeccompSuite) TestRestrictionsWorkingArgsUidGid(c *C) {
   770  	// while 'root' user usually has uid 0, 'daemon' user uid may vary
   771  	// across distributions, best lookup the uid directly
   772  	daemonUid, err := osutil.FindUid("daemon")
   773  
   774  	if err != nil {
   775  		c.Skip("daemon user not available, perhaps we are in a buildroot jail")
   776  	}
   777  
   778  	for _, t := range []struct {
   779  		seccompWhitelist string
   780  		bpfInput         string
   781  		expected         int
   782  	}{
   783  		// good input. 'root' is guaranteed to be '0' and 'daemon' uid
   784  		// was determined at runtime
   785  		{"setuid u:root", "setuid;native;0", Allow},
   786  		{"setuid u:daemon", fmt.Sprintf("setuid;native;%v", daemonUid), Allow},
   787  		{"setgid g:root", "setgid;native;0", Allow},
   788  		{"setgid g:daemon", fmt.Sprintf("setgid;native;%v", daemonUid), Allow},
   789  		// bad input
   790  		{"setuid u:root", "setuid;native;99", Deny},
   791  		{"setuid u:daemon", "setuid;native;99", Deny},
   792  		{"setgid g:root", "setgid;native;99", Deny},
   793  		{"setgid g:daemon", "setgid;native;99", Deny},
   794  	} {
   795  		s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   796  	}
   797  }
   798  
   799  func (s *snapSeccompSuite) TestCompatArchWorks(c *C) {
   800  	if !s.canCheckCompatArch {
   801  		c.Skip("multi-lib syscall runner not supported by this host")
   802  	}
   803  	for _, t := range []struct {
   804  		arch             string
   805  		seccompWhitelist string
   806  		bpfInput         string
   807  		expected         int
   808  	}{
   809  		// on amd64 we add compat i386
   810  		{"amd64", "read", "read;i386", Allow},
   811  		{"amd64", "read", "read;amd64", Allow},
   812  		{"amd64", "chown - 0 -1", "chown;i386;-,0,-1", Allow},
   813  		{"amd64", "chown - 0 -1", "chown;amd64;-,0,-1", Allow},
   814  		{"amd64", "chown - 0 -1", "chown;i386;-,99,-1", Deny},
   815  		{"amd64", "chown - 0 -1", "chown;amd64;-,99,-1", Deny},
   816  		{"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,-1,-1", Allow},
   817  		{"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,-1,-1", Allow},
   818  		{"amd64", "setresuid -1 -1 -1", "setresuid;i386;-1,99,-1", Deny},
   819  		{"amd64", "setresuid -1 -1 -1", "setresuid;amd64;-1,99,-1", Deny},
   820  	} {
   821  		// It is tricky to mock the architecture here because
   822  		// seccomp is always adding the native arch to the seccomp
   823  		// filter and it will silently discard arches that have
   824  		// an endian mismatch:
   825  		// https://github.com/seccomp/libseccomp/issues/86
   826  		//
   827  		// This means we can not just
   828  		//    main.MockArchDpkgArchitecture(t.arch)
   829  		// here because on endian mismatch the arch will *not* be
   830  		// added
   831  		if arch.DpkgArchitecture() == t.arch {
   832  			s.runBpf(c, t.seccompWhitelist, t.bpfInput, t.expected)
   833  		}
   834  	}
   835  }