gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/seccomp/seccomp_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package seccomp
    16  
    17  import (
    18  	"bytes"
    19  	_ "embed"
    20  	"fmt"
    21  	"io"
    22  	"io/ioutil"
    23  	"math"
    24  	"math/rand"
    25  	"os"
    26  	"os/exec"
    27  	"reflect"
    28  	"strings"
    29  	"testing"
    30  	"time"
    31  
    32  	"golang.org/x/sys/unix"
    33  	"gvisor.dev/gvisor/pkg/abi/linux"
    34  	"gvisor.dev/gvisor/pkg/bpf"
    35  )
    36  
    37  //go:embed victim
    38  var victimData []byte
    39  
    40  // newVictim makes a victim binary.
    41  func newVictim() (string, error) {
    42  	f, err := ioutil.TempFile("", "victim")
    43  	if err != nil {
    44  		return "", err
    45  	}
    46  	defer f.Close()
    47  	path := f.Name()
    48  	if _, err := io.Copy(f, bytes.NewBuffer(victimData)); err != nil {
    49  		os.Remove(path)
    50  		return "", err
    51  	}
    52  	if err := os.Chmod(path, 0755); err != nil {
    53  		os.Remove(path)
    54  		return "", err
    55  	}
    56  	return path, nil
    57  }
    58  
    59  func TestBasic(t *testing.T) {
    60  	buf := make([]byte, (&linux.SeccompData{}).SizeBytes())
    61  
    62  	type spec struct {
    63  		// desc is the test's description.
    64  		desc string
    65  
    66  		// data is the input data.
    67  		data linux.SeccompData
    68  
    69  		// want is the expected return value of the BPF program.
    70  		want linux.BPFAction
    71  	}
    72  
    73  	for _, test := range []struct {
    74  		name      string
    75  		ruleSets  []RuleSet
    76  		wantPanic bool
    77  		options   ProgramOptions
    78  		specs     []spec
    79  	}{
    80  		{
    81  			name: "Single syscall",
    82  			ruleSets: []RuleSet{
    83  				{
    84  					Rules:  MakeSyscallRules(map[uintptr]SyscallRule{1: MatchAll{}}),
    85  					Action: linux.SECCOMP_RET_ALLOW,
    86  				},
    87  			},
    88  			options: ProgramOptions{
    89  				DefaultAction: linux.SECCOMP_RET_TRAP,
    90  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
    91  			},
    92  			specs: []spec{
    93  				{
    94  					desc: "syscall allowed",
    95  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH},
    96  					want: linux.SECCOMP_RET_ALLOW,
    97  				},
    98  				{
    99  					desc: "syscall disallowed",
   100  					data: linux.SeccompData{Nr: 2, Arch: LINUX_AUDIT_ARCH},
   101  					want: linux.SECCOMP_RET_TRAP,
   102  				},
   103  			},
   104  		},
   105  		{
   106  			name: "Multiple rulesets",
   107  			ruleSets: []RuleSet{
   108  				{
   109  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   110  						1: PerArg{
   111  							EqualTo(0x1),
   112  						},
   113  					}),
   114  					Action: linux.SECCOMP_RET_ALLOW,
   115  				},
   116  				{
   117  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   118  						1: MatchAll{},
   119  						2: MatchAll{},
   120  					}),
   121  					Action: linux.SECCOMP_RET_TRAP,
   122  				},
   123  			},
   124  			options: ProgramOptions{
   125  				DefaultAction: linux.SECCOMP_RET_KILL_THREAD,
   126  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   127  			},
   128  			specs: []spec{
   129  				{
   130  					desc: "allowed (1a)",
   131  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x1}},
   132  					want: linux.SECCOMP_RET_ALLOW,
   133  				},
   134  				{
   135  					desc: "allowed (1b)",
   136  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH},
   137  					want: linux.SECCOMP_RET_TRAP,
   138  				},
   139  				{
   140  					desc: "syscall 1 matched 2nd rule",
   141  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH},
   142  					want: linux.SECCOMP_RET_TRAP,
   143  				},
   144  				{
   145  					desc: "no match",
   146  					data: linux.SeccompData{Nr: 0, Arch: LINUX_AUDIT_ARCH},
   147  					want: linux.SECCOMP_RET_KILL_THREAD,
   148  				},
   149  			},
   150  		},
   151  		{
   152  			name: "Multiple syscalls",
   153  			ruleSets: []RuleSet{
   154  				{
   155  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   156  						1: MatchAll{},
   157  						3: MatchAll{},
   158  						5: MatchAll{},
   159  					}),
   160  					Action: linux.SECCOMP_RET_ALLOW,
   161  				},
   162  			},
   163  			options: ProgramOptions{
   164  				DefaultAction: linux.SECCOMP_RET_TRAP,
   165  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   166  			},
   167  			specs: []spec{
   168  				{
   169  					desc: "allowed (1)",
   170  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH},
   171  					want: linux.SECCOMP_RET_ALLOW,
   172  				},
   173  				{
   174  					desc: "allowed (3)",
   175  					data: linux.SeccompData{Nr: 3, Arch: LINUX_AUDIT_ARCH},
   176  					want: linux.SECCOMP_RET_ALLOW,
   177  				},
   178  				{
   179  					desc: "allowed (5)",
   180  					data: linux.SeccompData{Nr: 5, Arch: LINUX_AUDIT_ARCH},
   181  					want: linux.SECCOMP_RET_ALLOW,
   182  				},
   183  				{
   184  					desc: "disallowed (0)",
   185  					data: linux.SeccompData{Nr: 0, Arch: LINUX_AUDIT_ARCH},
   186  					want: linux.SECCOMP_RET_TRAP,
   187  				},
   188  				{
   189  					desc: "disallowed (2)",
   190  					data: linux.SeccompData{Nr: 2, Arch: LINUX_AUDIT_ARCH},
   191  					want: linux.SECCOMP_RET_TRAP,
   192  				},
   193  				{
   194  					desc: "disallowed (4)",
   195  					data: linux.SeccompData{Nr: 4, Arch: LINUX_AUDIT_ARCH},
   196  					want: linux.SECCOMP_RET_TRAP,
   197  				},
   198  				{
   199  					desc: "disallowed (6)",
   200  					data: linux.SeccompData{Nr: 6, Arch: LINUX_AUDIT_ARCH},
   201  					want: linux.SECCOMP_RET_TRAP,
   202  				},
   203  				{
   204  					desc: "disallowed (100)",
   205  					data: linux.SeccompData{Nr: 100, Arch: LINUX_AUDIT_ARCH},
   206  					want: linux.SECCOMP_RET_TRAP,
   207  				},
   208  			},
   209  		},
   210  		{
   211  			name: "Wrong architecture",
   212  			ruleSets: []RuleSet{
   213  				{
   214  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   215  						1: MatchAll{},
   216  					}),
   217  					Action: linux.SECCOMP_RET_ALLOW,
   218  				},
   219  			},
   220  			options: ProgramOptions{
   221  				DefaultAction: linux.SECCOMP_RET_TRAP,
   222  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   223  			},
   224  			specs: []spec{
   225  				{
   226  					desc: "arch (123)",
   227  					data: linux.SeccompData{Nr: 1, Arch: 123},
   228  					want: linux.SECCOMP_RET_KILL_THREAD,
   229  				},
   230  			},
   231  		},
   232  		{
   233  			name: "Syscall disallowed",
   234  			ruleSets: []RuleSet{
   235  				{
   236  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   237  						1: MatchAll{},
   238  					}),
   239  					Action: linux.SECCOMP_RET_ALLOW,
   240  				},
   241  			},
   242  			options: ProgramOptions{
   243  				DefaultAction: linux.SECCOMP_RET_TRAP,
   244  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   245  			},
   246  			specs: []spec{
   247  				{
   248  					desc: "action trap",
   249  					data: linux.SeccompData{Nr: 2, Arch: LINUX_AUDIT_ARCH},
   250  					want: linux.SECCOMP_RET_TRAP,
   251  				},
   252  			},
   253  		},
   254  		{
   255  			name: "Syscall arguments",
   256  			ruleSets: []RuleSet{
   257  				{
   258  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   259  						1: PerArg{
   260  							AnyValue{},
   261  							EqualTo(0xf),
   262  						},
   263  					}),
   264  					Action: linux.SECCOMP_RET_ALLOW,
   265  				},
   266  			},
   267  			options: ProgramOptions{
   268  				DefaultAction: linux.SECCOMP_RET_TRAP,
   269  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   270  			},
   271  			specs: []spec{
   272  				{
   273  					desc: "allowed",
   274  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xf}},
   275  					want: linux.SECCOMP_RET_ALLOW,
   276  				},
   277  				{
   278  					desc: "disallowed",
   279  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xe}},
   280  					want: linux.SECCOMP_RET_TRAP,
   281  				},
   282  			},
   283  		},
   284  		{
   285  			name: "Multiple arguments",
   286  			ruleSets: []RuleSet{
   287  				{
   288  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   289  						1: Or{
   290  							PerArg{
   291  								EqualTo(0xf),
   292  							},
   293  							PerArg{
   294  								EqualTo(0xe),
   295  							},
   296  						},
   297  					}),
   298  					Action: linux.SECCOMP_RET_ALLOW,
   299  				},
   300  			},
   301  			options: ProgramOptions{
   302  				DefaultAction: linux.SECCOMP_RET_TRAP,
   303  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   304  			},
   305  			specs: []spec{
   306  				{
   307  					desc: "match first rule",
   308  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf}},
   309  					want: linux.SECCOMP_RET_ALLOW,
   310  				},
   311  				{
   312  					desc: "match 2nd rule",
   313  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xe}},
   314  					want: linux.SECCOMP_RET_ALLOW,
   315  				},
   316  				{
   317  					desc: "match neither rule",
   318  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xd}},
   319  					want: linux.SECCOMP_RET_TRAP,
   320  				},
   321  			},
   322  		},
   323  		{
   324  			name: "empty Or is invalid",
   325  			ruleSets: []RuleSet{
   326  				{
   327  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   328  						1: Or{},
   329  					}),
   330  					Action: linux.SECCOMP_RET_ALLOW,
   331  				},
   332  			},
   333  			wantPanic: true,
   334  		},
   335  		{
   336  			name: "And of multiple rules",
   337  			ruleSets: []RuleSet{
   338  				{
   339  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   340  						1: And{
   341  							PerArg{
   342  								NotEqual(0xf),
   343  							},
   344  							PerArg{
   345  								NotEqual(0xe),
   346  							},
   347  						},
   348  					}),
   349  					Action: linux.SECCOMP_RET_ALLOW,
   350  				},
   351  			},
   352  			options: ProgramOptions{
   353  				DefaultAction: linux.SECCOMP_RET_TRAP,
   354  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   355  			},
   356  			specs: []spec{
   357  				{
   358  					desc: "hit first rule",
   359  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf}},
   360  					want: linux.SECCOMP_RET_TRAP,
   361  				},
   362  				{
   363  					desc: "hit 2nd rule",
   364  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xe}},
   365  					want: linux.SECCOMP_RET_TRAP,
   366  				},
   367  				{
   368  					desc: "hit neither rule",
   369  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xd}},
   370  					want: linux.SECCOMP_RET_ALLOW,
   371  				},
   372  			},
   373  		},
   374  		{
   375  			name: "empty And is invalid",
   376  			ruleSets: []RuleSet{
   377  				{
   378  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   379  						1: And{},
   380  					}),
   381  					Action: linux.SECCOMP_RET_ALLOW,
   382  				},
   383  			},
   384  			wantPanic: true,
   385  		},
   386  		{
   387  			name: "EqualTo",
   388  			ruleSets: []RuleSet{
   389  				{
   390  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   391  						1: PerArg{
   392  							EqualTo(0),
   393  							EqualTo(math.MaxUint64 - 1),
   394  							EqualTo(math.MaxUint32),
   395  						},
   396  					}),
   397  					Action: linux.SECCOMP_RET_ALLOW,
   398  				},
   399  			},
   400  			options: ProgramOptions{
   401  				DefaultAction: linux.SECCOMP_RET_TRAP,
   402  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   403  			},
   404  			specs: []spec{
   405  				{
   406  					desc: "argument allowed (all match)",
   407  					data: linux.SeccompData{
   408  						Nr:   1,
   409  						Arch: LINUX_AUDIT_ARCH,
   410  						Args: [6]uint64{0, math.MaxUint64 - 1, math.MaxUint32},
   411  					},
   412  					want: linux.SECCOMP_RET_ALLOW,
   413  				},
   414  				{
   415  					desc: "argument disallowed (one mismatch)",
   416  					data: linux.SeccompData{
   417  						Nr:   1,
   418  						Arch: LINUX_AUDIT_ARCH,
   419  						Args: [6]uint64{0, math.MaxUint64, math.MaxUint32},
   420  					},
   421  					want: linux.SECCOMP_RET_TRAP,
   422  				},
   423  				{
   424  					desc: "argument disallowed (multiple mismatch)",
   425  					data: linux.SeccompData{
   426  						Nr:   1,
   427  						Arch: LINUX_AUDIT_ARCH,
   428  						Args: [6]uint64{0, math.MaxUint64, math.MaxUint32 - 1},
   429  					},
   430  					want: linux.SECCOMP_RET_TRAP,
   431  				},
   432  			},
   433  		},
   434  		{
   435  			name: "NotEqual",
   436  			ruleSets: []RuleSet{
   437  				{
   438  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   439  						1: PerArg{
   440  							NotEqual(0x7aabbccdd),
   441  							NotEqual(math.MaxUint64 - 1),
   442  							NotEqual(math.MaxUint32),
   443  						},
   444  					}),
   445  					Action: linux.SECCOMP_RET_ALLOW,
   446  				},
   447  			},
   448  			options: ProgramOptions{
   449  				DefaultAction: linux.SECCOMP_RET_TRAP,
   450  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   451  			},
   452  			specs: []spec{
   453  				{
   454  					desc: "arg allowed",
   455  					data: linux.SeccompData{
   456  						Nr:   1,
   457  						Arch: LINUX_AUDIT_ARCH,
   458  						Args: [6]uint64{0, math.MaxUint64, math.MaxUint32 - 1},
   459  					},
   460  					want: linux.SECCOMP_RET_ALLOW,
   461  				},
   462  				{
   463  					desc: "arg disallowed (one equal)",
   464  					data: linux.SeccompData{
   465  						Nr:   1,
   466  						Arch: LINUX_AUDIT_ARCH,
   467  						Args: [6]uint64{0x7aabbccdd, math.MaxUint64, math.MaxUint32 - 1},
   468  					},
   469  					want: linux.SECCOMP_RET_TRAP,
   470  				},
   471  				{
   472  					desc: "arg disallowed (all equal)",
   473  					data: linux.SeccompData{
   474  						Nr:   1,
   475  						Arch: LINUX_AUDIT_ARCH,
   476  						Args: [6]uint64{0x7aabbccdd, math.MaxUint64 - 1, math.MaxUint32},
   477  					},
   478  					want: linux.SECCOMP_RET_TRAP,
   479  				},
   480  			},
   481  		},
   482  		{
   483  			name: "GreaterThan",
   484  			ruleSets: []RuleSet{
   485  				{
   486  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   487  						1: PerArg{
   488  							// 4294967298
   489  							// Both upper 32 bits and lower 32 bits are non-zero.
   490  							// 00000000000000000000000000000010
   491  							// 00000000000000000000000000000010
   492  							GreaterThan(0x00000002_00000002),
   493  						},
   494  					}),
   495  					Action: linux.SECCOMP_RET_ALLOW,
   496  				},
   497  			},
   498  			options: ProgramOptions{
   499  				DefaultAction: linux.SECCOMP_RET_TRAP,
   500  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   501  			},
   502  			specs: []spec{
   503  				{
   504  					desc: "high 32bits greater",
   505  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}},
   506  					want: linux.SECCOMP_RET_ALLOW,
   507  				},
   508  				{
   509  					desc: "high 32bits equal, low 32bits greater",
   510  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}},
   511  					want: linux.SECCOMP_RET_ALLOW,
   512  				},
   513  				{
   514  					desc: "high 32bits equal, low 32bits equal",
   515  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}},
   516  					want: linux.SECCOMP_RET_TRAP,
   517  				},
   518  				{
   519  					desc: "high 32bits equal, low 32bits less",
   520  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}},
   521  					want: linux.SECCOMP_RET_TRAP,
   522  				},
   523  				{
   524  					desc: "high 32bits less",
   525  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000003}},
   526  					want: linux.SECCOMP_RET_TRAP,
   527  				},
   528  			},
   529  		},
   530  		{
   531  			name: "GreaterThan (multi)",
   532  			ruleSets: []RuleSet{
   533  				{
   534  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   535  						1: PerArg{
   536  							GreaterThan(0xf),
   537  							GreaterThan(0xabcd000d),
   538  						},
   539  					}),
   540  					Action: linux.SECCOMP_RET_ALLOW,
   541  				},
   542  			},
   543  			options: ProgramOptions{
   544  				DefaultAction: linux.SECCOMP_RET_TRAP,
   545  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   546  			},
   547  			specs: []spec{
   548  				{
   549  					desc: "arg allowed",
   550  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xffffffff}},
   551  					want: linux.SECCOMP_RET_ALLOW,
   552  				},
   553  				{
   554  					desc: "arg disallowed (first arg equal)",
   555  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xffffffff}},
   556  					want: linux.SECCOMP_RET_TRAP,
   557  				},
   558  				{
   559  					desc: "arg disallowed (first arg smaller)",
   560  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}},
   561  					want: linux.SECCOMP_RET_TRAP,
   562  				},
   563  				{
   564  					desc: "arg disallowed (second arg equal)",
   565  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xabcd000d}},
   566  					want: linux.SECCOMP_RET_TRAP,
   567  				},
   568  				{
   569  					desc: "arg disallowed (second arg smaller)",
   570  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xa000ffff}},
   571  					want: linux.SECCOMP_RET_TRAP,
   572  				},
   573  			},
   574  		},
   575  		{
   576  			name: "GreaterThanOrEqual",
   577  			ruleSets: []RuleSet{
   578  				{
   579  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   580  						1: PerArg{
   581  							// 4294967298
   582  							// Both upper 32 bits and lower 32 bits are non-zero.
   583  							// 00000000000000000000000000000010
   584  							// 00000000000000000000000000000010
   585  							GreaterThanOrEqual(0x00000002_00000002),
   586  						},
   587  					}),
   588  					Action: linux.SECCOMP_RET_ALLOW,
   589  				},
   590  			},
   591  			options: ProgramOptions{
   592  				DefaultAction: linux.SECCOMP_RET_TRAP,
   593  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   594  			},
   595  			specs: []spec{
   596  				{
   597  					desc: "high 32bits greater",
   598  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}},
   599  					want: linux.SECCOMP_RET_ALLOW,
   600  				},
   601  				{
   602  					desc: "high 32bits equal, low 32bits greater",
   603  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}},
   604  					want: linux.SECCOMP_RET_ALLOW,
   605  				},
   606  				{
   607  					desc: "high 32bits equal, low 32bits equal",
   608  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}},
   609  					want: linux.SECCOMP_RET_ALLOW,
   610  				},
   611  				{
   612  					desc: "high 32bits equal, low 32bits less",
   613  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}},
   614  					want: linux.SECCOMP_RET_TRAP,
   615  				},
   616  				{
   617  					desc: "high 32bits less",
   618  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000002}},
   619  					want: linux.SECCOMP_RET_TRAP,
   620  				},
   621  			},
   622  		},
   623  		{
   624  			name: "GreaterThanOrEqual (multi)",
   625  			ruleSets: []RuleSet{
   626  				{
   627  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   628  						1: PerArg{
   629  							GreaterThanOrEqual(0xf),
   630  							GreaterThanOrEqual(0xabcd000d),
   631  						},
   632  					}),
   633  					Action: linux.SECCOMP_RET_ALLOW,
   634  				},
   635  			},
   636  			options: ProgramOptions{
   637  				DefaultAction: linux.SECCOMP_RET_TRAP,
   638  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   639  			},
   640  			specs: []spec{
   641  				{
   642  					desc: "arg allowed (both greater)",
   643  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xffffffff}},
   644  					want: linux.SECCOMP_RET_ALLOW,
   645  				},
   646  				{
   647  					desc: "arg allowed (first arg equal)",
   648  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xf, 0xffffffff}},
   649  					want: linux.SECCOMP_RET_ALLOW,
   650  				},
   651  				{
   652  					desc: "arg disallowed (first arg smaller)",
   653  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}},
   654  					want: linux.SECCOMP_RET_TRAP,
   655  				},
   656  				{
   657  					desc: "arg allowed (second arg equal)",
   658  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xabcd000d}},
   659  					want: linux.SECCOMP_RET_ALLOW,
   660  				},
   661  				{
   662  					desc: "arg disallowed (second arg smaller)",
   663  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x10, 0xa000ffff}},
   664  					want: linux.SECCOMP_RET_TRAP,
   665  				},
   666  				{
   667  					desc: "arg disallowed (both arg smaller)",
   668  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xa000ffff}},
   669  					want: linux.SECCOMP_RET_TRAP,
   670  				},
   671  			},
   672  		},
   673  		{
   674  			name: "LessThan",
   675  			ruleSets: []RuleSet{
   676  				{
   677  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   678  						1: PerArg{
   679  							// 4294967298
   680  							// Both upper 32 bits and lower 32 bits are non-zero.
   681  							// 00000000000000000000000000000010
   682  							// 00000000000000000000000000000010
   683  							LessThan(0x00000002_00000002),
   684  						},
   685  					}),
   686  					Action: linux.SECCOMP_RET_ALLOW,
   687  				},
   688  			},
   689  			options: ProgramOptions{
   690  				DefaultAction: linux.SECCOMP_RET_TRAP,
   691  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   692  			},
   693  			specs: []spec{
   694  				{
   695  					desc: "high 32bits greater",
   696  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}},
   697  					want: linux.SECCOMP_RET_TRAP,
   698  				},
   699  				{
   700  					desc: "high 32bits equal, low 32bits greater",
   701  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}},
   702  					want: linux.SECCOMP_RET_TRAP,
   703  				},
   704  				{
   705  					desc: "high 32bits equal, low 32bits equal",
   706  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}},
   707  					want: linux.SECCOMP_RET_TRAP,
   708  				},
   709  				{
   710  					desc: "high 32bits equal, low 32bits less",
   711  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}},
   712  					want: linux.SECCOMP_RET_ALLOW,
   713  				},
   714  				{
   715  					desc: "high 32bits less",
   716  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000002}},
   717  					want: linux.SECCOMP_RET_ALLOW,
   718  				},
   719  			},
   720  		},
   721  		{
   722  			name: "LessThan (multi)",
   723  			ruleSets: []RuleSet{
   724  				{
   725  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   726  						1: PerArg{
   727  							LessThan(0x1),
   728  							LessThan(0xabcd000d),
   729  						},
   730  					}),
   731  					Action: linux.SECCOMP_RET_ALLOW,
   732  				},
   733  			},
   734  			options: ProgramOptions{
   735  				DefaultAction: linux.SECCOMP_RET_TRAP,
   736  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   737  			},
   738  			specs: []spec{
   739  				{
   740  					desc: "arg allowed",
   741  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0x0}},
   742  					want: linux.SECCOMP_RET_ALLOW,
   743  				},
   744  				{
   745  					desc: "arg disallowed (first arg equal)",
   746  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x1, 0x0}},
   747  					want: linux.SECCOMP_RET_TRAP,
   748  				},
   749  				{
   750  					desc: "arg disallowed (first arg greater)",
   751  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0x0}},
   752  					want: linux.SECCOMP_RET_TRAP,
   753  				},
   754  				{
   755  					desc: "arg disallowed (second arg equal)",
   756  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xabcd000d}},
   757  					want: linux.SECCOMP_RET_TRAP,
   758  				},
   759  				{
   760  					desc: "arg disallowed (second arg greater)",
   761  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}},
   762  					want: linux.SECCOMP_RET_TRAP,
   763  				},
   764  				{
   765  					desc: "arg disallowed (both arg greater)",
   766  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0xffffffff}},
   767  					want: linux.SECCOMP_RET_TRAP,
   768  				},
   769  			},
   770  		},
   771  		{
   772  			name: "LessThanOrEqual",
   773  			ruleSets: []RuleSet{
   774  				{
   775  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   776  						1: PerArg{
   777  							// 4294967298
   778  							// Both upper 32 bits and lower 32 bits are non-zero.
   779  							// 00000000000000000000000000000010
   780  							// 00000000000000000000000000000010
   781  							LessThanOrEqual(0x00000002_00000002),
   782  						},
   783  					}),
   784  					Action: linux.SECCOMP_RET_ALLOW,
   785  				},
   786  			},
   787  			options: ProgramOptions{
   788  				DefaultAction: linux.SECCOMP_RET_TRAP,
   789  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   790  			},
   791  			specs: []spec{
   792  				{
   793  					desc: "high 32bits greater",
   794  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000003_00000002}},
   795  					want: linux.SECCOMP_RET_TRAP,
   796  				},
   797  				{
   798  					desc: "high 32bits equal, low 32bits greater",
   799  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000003}},
   800  					want: linux.SECCOMP_RET_TRAP,
   801  				},
   802  				{
   803  					desc: "high 32bits equal, low 32bits equal",
   804  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000002}},
   805  					want: linux.SECCOMP_RET_ALLOW,
   806  				},
   807  				{
   808  					desc: "high 32bits equal, low 32bits less",
   809  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000002_00000001}},
   810  					want: linux.SECCOMP_RET_ALLOW,
   811  				},
   812  				{
   813  					desc: "high 32bits less",
   814  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x00000001_00000002}},
   815  					want: linux.SECCOMP_RET_ALLOW,
   816  				},
   817  			},
   818  		},
   819  
   820  		{
   821  			name: "LessThanOrEqual (multi)",
   822  			ruleSets: []RuleSet{
   823  				{
   824  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   825  						1: PerArg{
   826  							LessThanOrEqual(0x1),
   827  							LessThanOrEqual(0xabcd000d),
   828  						},
   829  					}),
   830  					Action: linux.SECCOMP_RET_ALLOW,
   831  				},
   832  			},
   833  			options: ProgramOptions{
   834  				DefaultAction: linux.SECCOMP_RET_TRAP,
   835  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   836  			},
   837  			specs: []spec{
   838  				{
   839  					desc: "arg allowed",
   840  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0x0}},
   841  					want: linux.SECCOMP_RET_ALLOW,
   842  				},
   843  				{
   844  					desc: "arg allowed (first arg equal)",
   845  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x1, 0x0}},
   846  					want: linux.SECCOMP_RET_ALLOW,
   847  				},
   848  				{
   849  					desc: "arg disallowed (first arg greater)",
   850  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0x0}},
   851  					want: linux.SECCOMP_RET_TRAP,
   852  				},
   853  				{
   854  					desc: "arg allowed (second arg equal)",
   855  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xabcd000d}},
   856  					want: linux.SECCOMP_RET_ALLOW,
   857  				},
   858  				{
   859  					desc: "arg disallowed (second arg greater)",
   860  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0, 0xffffffff}},
   861  					want: linux.SECCOMP_RET_TRAP,
   862  				},
   863  				{
   864  					desc: "arg disallowed (both arg greater)",
   865  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x2, 0xffffffff}},
   866  					want: linux.SECCOMP_RET_TRAP,
   867  				},
   868  			},
   869  		},
   870  		{
   871  			name: "MaskedEqual",
   872  			ruleSets: []RuleSet{
   873  				{
   874  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   875  						1: PerArg{
   876  							// x & 00000001 00000011 (0x103) == 00000000 00000001 (0x1)
   877  							// Input x must have lowest order bit set and
   878  							// must *not* have 8th or second lowest order bit set.
   879  							MaskedEqual(0x103, 0x1),
   880  						},
   881  					}),
   882  					Action: linux.SECCOMP_RET_ALLOW,
   883  				},
   884  			},
   885  			options: ProgramOptions{
   886  				DefaultAction: linux.SECCOMP_RET_TRAP,
   887  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   888  			},
   889  			specs: []spec{
   890  				{
   891  					desc: "arg allowed (low order mandatory bit)",
   892  					data: linux.SeccompData{
   893  						Nr:   1,
   894  						Arch: LINUX_AUDIT_ARCH,
   895  						// 00000000 00000000 00000000 00000001
   896  						Args: [6]uint64{0x1},
   897  					},
   898  					want: linux.SECCOMP_RET_ALLOW,
   899  				},
   900  				{
   901  					desc: "arg allowed (low order optional bit)",
   902  					data: linux.SeccompData{
   903  						Nr:   1,
   904  						Arch: LINUX_AUDIT_ARCH,
   905  						// 00000000 00000000 00000000 00000101
   906  						Args: [6]uint64{0x5},
   907  					},
   908  					want: linux.SECCOMP_RET_ALLOW,
   909  				},
   910  				{
   911  					desc: "arg disallowed (lowest order bit not set)",
   912  					data: linux.SeccompData{
   913  						Nr:   1,
   914  						Arch: LINUX_AUDIT_ARCH,
   915  						// 00000000 00000000 00000000 00000010
   916  						Args: [6]uint64{0x2},
   917  					},
   918  					want: linux.SECCOMP_RET_TRAP,
   919  				},
   920  				{
   921  					desc: "arg disallowed (second lowest order bit set)",
   922  					data: linux.SeccompData{
   923  						Nr:   1,
   924  						Arch: LINUX_AUDIT_ARCH,
   925  						// 00000000 00000000 00000000 00000011
   926  						Args: [6]uint64{0x3},
   927  					},
   928  					want: linux.SECCOMP_RET_TRAP,
   929  				},
   930  				{
   931  					desc: "arg disallowed (8th bit set)",
   932  					data: linux.SeccompData{
   933  						Nr:   1,
   934  						Arch: LINUX_AUDIT_ARCH,
   935  						// 00000000 00000000 00000001 00000000
   936  						Args: [6]uint64{0x100},
   937  					},
   938  					want: linux.SECCOMP_RET_TRAP,
   939  				},
   940  			},
   941  		},
   942  		{
   943  			name: "NonNegativeFD",
   944  			ruleSets: []RuleSet{
   945  				{
   946  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
   947  						1: PerArg{
   948  							NonNegativeFD{},
   949  						},
   950  					}),
   951  					Action: linux.SECCOMP_RET_ALLOW,
   952  				},
   953  			},
   954  			options: ProgramOptions{
   955  				DefaultAction: linux.SECCOMP_RET_TRAP,
   956  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
   957  			},
   958  			specs: []spec{
   959  				{
   960  					desc: "zero allowed",
   961  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0}},
   962  					want: linux.SECCOMP_RET_ALLOW,
   963  				},
   964  				{
   965  					desc: "one allowed",
   966  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x0}},
   967  					want: linux.SECCOMP_RET_ALLOW,
   968  				},
   969  				{
   970  					desc: "seven allowed",
   971  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x7}},
   972  					want: linux.SECCOMP_RET_ALLOW,
   973  				},
   974  				{
   975  					desc: "largest int32 allowed",
   976  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x7fffffff}},
   977  					want: linux.SECCOMP_RET_ALLOW,
   978  				},
   979  				{
   980  					desc: "negative 1 not allowed",
   981  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x80000000}},
   982  					want: linux.SECCOMP_RET_TRAP,
   983  				},
   984  				{
   985  					desc: "largest uint32 not allowed",
   986  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xffffffff}},
   987  					want: linux.SECCOMP_RET_TRAP,
   988  				},
   989  				{
   990  					desc: "a positive int64 larger than max uint32 is not allowed",
   991  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x100000000}},
   992  					want: linux.SECCOMP_RET_TRAP,
   993  				},
   994  				{
   995  					desc: "largest int64 not allowed",
   996  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0x7fffffffffffffff}},
   997  					want: linux.SECCOMP_RET_TRAP,
   998  				},
   999  				{
  1000  					desc: "largest uint64 not allowed",
  1001  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{0xffffffffffffffff}},
  1002  					want: linux.SECCOMP_RET_TRAP,
  1003  				},
  1004  			},
  1005  		},
  1006  		{
  1007  			name: "Instruction Pointer",
  1008  			ruleSets: []RuleSet{
  1009  				{
  1010  					Rules: MakeSyscallRules(map[uintptr]SyscallRule{
  1011  						1: PerArg{
  1012  							RuleIP: EqualTo(0x7aabbccdd),
  1013  						},
  1014  					}),
  1015  					Action: linux.SECCOMP_RET_ALLOW,
  1016  				},
  1017  			},
  1018  			options: ProgramOptions{
  1019  				DefaultAction: linux.SECCOMP_RET_TRAP,
  1020  				BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
  1021  			},
  1022  			specs: []spec{
  1023  				{
  1024  					desc: "allowed",
  1025  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{}, InstructionPointer: 0x7aabbccdd},
  1026  					want: linux.SECCOMP_RET_ALLOW,
  1027  				},
  1028  				{
  1029  					desc: "disallowed",
  1030  					data: linux.SeccompData{Nr: 1, Arch: LINUX_AUDIT_ARCH, Args: [6]uint64{}, InstructionPointer: 0x711223344},
  1031  					want: linux.SECCOMP_RET_TRAP,
  1032  				},
  1033  			},
  1034  		},
  1035  	} {
  1036  		t.Run(test.name, func(t *testing.T) {
  1037  			var instrs []bpf.Instruction
  1038  			var panicErr any
  1039  			func() {
  1040  				t.Helper()
  1041  				defer func() {
  1042  					panicErr = recover()
  1043  					t.Helper()
  1044  				}()
  1045  				var err error
  1046  				instrs, _, err = BuildProgram(test.ruleSets, test.options)
  1047  				if err != nil {
  1048  					t.Fatalf("BuildProgram() got error: %v", err)
  1049  				}
  1050  			}()
  1051  			if test.wantPanic {
  1052  				if panicErr == nil {
  1053  					t.Fatal("BuildProgram did not panick")
  1054  				}
  1055  				return
  1056  			}
  1057  			if panicErr != nil {
  1058  				t.Fatalf("BuildProgram unexpectedly panicked: %v", panicErr)
  1059  			}
  1060  			p, err := bpf.Compile(instrs, true /* optimize */)
  1061  			if err != nil {
  1062  				t.Fatalf("bpf.Compile got error: %v", err)
  1063  			}
  1064  			for _, spec := range test.specs {
  1065  				got, err := bpf.Exec[bpf.NativeEndian](p, DataAsBPFInput(&spec.data, buf))
  1066  				if err != nil {
  1067  					t.Fatalf("%s: bpf.Exec got error: %v", spec.desc, err)
  1068  				}
  1069  				if got != uint32(spec.want) {
  1070  					// Include a decoded version of the program in output for debugging purposes.
  1071  					decoded, _ := bpf.DecodeInstructions(instrs)
  1072  					t.Fatalf("%s: got: %d, want: %d\nBPF Program\n%s", spec.desc, got, spec.want, decoded)
  1073  				}
  1074  			}
  1075  		})
  1076  	}
  1077  }
  1078  
  1079  // TestRandom tests that randomly generated rules are encoded correctly.
  1080  func TestRandom(t *testing.T) {
  1081  	rand.Seed(time.Now().UnixNano())
  1082  	size := rand.Intn(50) + 1
  1083  	syscallRules := NewSyscallRules()
  1084  	for syscallRules.Size() < size {
  1085  		n := uintptr(rand.Intn(200))
  1086  		if !syscallRules.Has(n) {
  1087  			syscallRules.Set(n, MatchAll{})
  1088  		}
  1089  	}
  1090  
  1091  	t.Logf("Testing filters: %v", syscallRules)
  1092  	instrs, _, err := BuildProgram([]RuleSet{
  1093  		{
  1094  			Rules:  syscallRules,
  1095  			Action: linux.SECCOMP_RET_ALLOW,
  1096  		},
  1097  	}, ProgramOptions{
  1098  		DefaultAction: linux.SECCOMP_RET_TRAP,
  1099  		BadArchAction: linux.SECCOMP_RET_KILL_THREAD,
  1100  	})
  1101  	if err != nil {
  1102  		t.Fatalf("buildProgram() got error: %v", err)
  1103  	}
  1104  	p, err := bpf.Compile(instrs, true /* optimize */)
  1105  	if err != nil {
  1106  		t.Fatalf("bpf.Compile got error: %v", err)
  1107  	}
  1108  	buf := make([]byte, (&linux.SeccompData{}).SizeBytes())
  1109  	for i := uint32(0); i < 200; i++ {
  1110  		data := linux.SeccompData{Nr: int32(i), Arch: LINUX_AUDIT_ARCH}
  1111  		got, err := bpf.Exec[bpf.NativeEndian](p, DataAsBPFInput(&data, buf))
  1112  		if err != nil {
  1113  			t.Errorf("bpf.Exec got error: %v, for syscall %d", err, i)
  1114  			continue
  1115  		}
  1116  		want := linux.SECCOMP_RET_TRAP
  1117  		if syscallRules.Has(uintptr(i)) {
  1118  			want = linux.SECCOMP_RET_ALLOW
  1119  		}
  1120  		if got != uint32(want) {
  1121  			t.Errorf("bpf.Exec = %d, want: %d, for syscall %d", got, want, i)
  1122  		}
  1123  	}
  1124  }
  1125  
  1126  // TestReadDeal checks that a process dies when it trips over the filter and
  1127  // that it doesn't die when the filter is not triggered.
  1128  func TestRealDeal(t *testing.T) {
  1129  	for _, test := range []struct {
  1130  		name string
  1131  		die  bool
  1132  		want string
  1133  	}{
  1134  		{name: "bad syscall", die: true, want: "bad system call"},
  1135  		{name: "allowed syscall", die: false, want: "Syscall was allowed!!!"},
  1136  	} {
  1137  		t.Run(test.name, func(t *testing.T) {
  1138  			victim, err := newVictim()
  1139  			if err != nil {
  1140  				t.Fatalf("unable to get victim: %v", err)
  1141  			}
  1142  			defer func() {
  1143  				if err := os.Remove(victim); err != nil {
  1144  					t.Fatalf("Unable to remove victim: %v", err)
  1145  				}
  1146  			}()
  1147  
  1148  			dieFlag := fmt.Sprintf("-die=%v", test.die)
  1149  			cmd := exec.Command(victim, dieFlag)
  1150  			out, err := cmd.CombinedOutput()
  1151  			if test.die {
  1152  				if err == nil {
  1153  					t.Fatalf("Victim was not killed as expected, output: %s", out)
  1154  				}
  1155  				// Depending on kernel version, either RET_TRAP or RET_KILL_PROCESS is
  1156  				// used. RET_TRAP dumps reason for exit in output, while RET_KILL_PROCESS
  1157  				// returns SIGSYS as exit status.
  1158  				if !strings.Contains(string(out), test.want) &&
  1159  					!strings.Contains(err.Error(), test.want) {
  1160  					t.Fatalf("Victim error is wrong, got: %v, err: %v, want: %v", string(out), err, test.want)
  1161  				}
  1162  				return
  1163  			}
  1164  			// test.die is false
  1165  			if err != nil {
  1166  				t.Logf("out: %s", string(out))
  1167  				t.Fatalf("Victim failed to execute, err: %v", err)
  1168  			}
  1169  			if !strings.Contains(string(out), test.want) {
  1170  				t.Fatalf("Victim output is wrong, got: %v, want: %v", string(out), test.want)
  1171  			}
  1172  		})
  1173  	}
  1174  }
  1175  
  1176  // TestMerge ensures that empty rules are not erased when rules are merged.
  1177  func TestMerge(t *testing.T) {
  1178  	for _, tst := range []struct {
  1179  		name  string
  1180  		main  SyscallRule
  1181  		merge SyscallRule
  1182  		want  SyscallRule
  1183  	}{
  1184  		{
  1185  			name:  "MatchAll both",
  1186  			main:  MatchAll{},
  1187  			merge: MatchAll{},
  1188  			want:  Or{MatchAll{}, MatchAll{}},
  1189  		},
  1190  		{
  1191  			name:  "MatchAll and Or",
  1192  			main:  MatchAll{},
  1193  			merge: Or{PerArg{EqualTo(0)}},
  1194  			want:  Or{MatchAll{}, Or{PerArg{EqualTo(0)}}},
  1195  		},
  1196  		{
  1197  			name:  "Or and MatchAll",
  1198  			main:  Or{PerArg{EqualTo(0)}},
  1199  			merge: MatchAll{},
  1200  			want:  Or{Or{PerArg{EqualTo(0)}}, MatchAll{}},
  1201  		},
  1202  		{
  1203  			name:  "2 Ors",
  1204  			main:  Or{PerArg{EqualTo(0)}},
  1205  			merge: Or{PerArg{EqualTo(1)}},
  1206  			want:  Or{Or{PerArg{EqualTo(0)}}, Or{PerArg{EqualTo(1)}}},
  1207  		},
  1208  	} {
  1209  		t.Run(tst.name, func(t *testing.T) {
  1210  			mainRules := MakeSyscallRules(map[uintptr]SyscallRule{
  1211  				1: tst.main,
  1212  			}).Merge(MakeSyscallRules(map[uintptr]SyscallRule{
  1213  				1: tst.merge,
  1214  			}))
  1215  			wantRules := MakeSyscallRules(map[uintptr]SyscallRule{1: tst.want})
  1216  			if !reflect.DeepEqual(mainRules, wantRules) {
  1217  				t.Errorf("got rules:\n%v\nwant rules:\n%v\n", mainRules, wantRules)
  1218  			}
  1219  		})
  1220  	}
  1221  }
  1222  
  1223  // TestOptimizeSyscallRule tests the behavior of syscall rule optimizers.
  1224  func TestOptimizeSyscallRule(t *testing.T) {
  1225  	// av is a shorthand for `AnyValue{}`, used below to keep `PerArg`
  1226  	// structs short enough to comfortably fit on one line.
  1227  	av := AnyValue{}
  1228  
  1229  	// Some useful constants that are larger than uint32.
  1230  	const (
  1231  		a1 = 0xA1A1A1A1A1A1A1A1
  1232  		a2 = 0xA2A2A2A2A2A2A2A2
  1233  		b1 = 0xB1B1B1B1B1B1B1B1
  1234  		b2 = 0xB2B2B2B2B2B2B2B2
  1235  		b3 = 0xB3B3B3B3B3B3B3B3
  1236  		c1 = 0xC1C1C1C1C1C1C1C1
  1237  		c2 = 0xC2C2C2C2C2C2C2C2
  1238  		c3 = 0xC3C3C3C3C3C3C3C3
  1239  		d0 = 0xD0D0D0D0D0D0D0D0
  1240  		d1 = 0xD1D1D1D1D1D1D1D1
  1241  	)
  1242  
  1243  	// split replaces a `splittableValueMatcher` rule with its `splitMatcher`
  1244  	// version.
  1245  	s := func(matcher splittableValueMatcher) ValueMatcher {
  1246  		return matcher.split()
  1247  	}
  1248  
  1249  	highEq := func(val uintptr) splitMatcher {
  1250  		return high32BitsMatch(halfEqualTo(val))
  1251  	}
  1252  	lowEq := func(val uintptr) splitMatcher {
  1253  		return low32BitsMatch(halfEqualTo(val))
  1254  	}
  1255  
  1256  	for _, test := range []struct {
  1257  		name       string
  1258  		rule       SyscallRule
  1259  		optimizers []ruleOptimizerFunc
  1260  		want       SyscallRule
  1261  	}{
  1262  		{
  1263  			name: "do nothing to a simple rule",
  1264  			rule: PerArg{NotEqual(0xff), av, av, av, av, av, av},
  1265  			want: PerArg{NotEqual(0xff), av, av, av, av, av, av},
  1266  		},
  1267  		{
  1268  			name: "flatten Or rule",
  1269  			rule: Or{
  1270  				Or{
  1271  					PerArg{EqualTo(a1)},
  1272  					Or{
  1273  						PerArg{EqualTo(b1)},
  1274  						PerArg{EqualTo(b2)},
  1275  					},
  1276  					PerArg{EqualTo(c1)},
  1277  				},
  1278  				Or{
  1279  					PerArg{EqualTo(d0)},
  1280  					PerArg{EqualTo(d1)},
  1281  				},
  1282  			},
  1283  			want: Or{
  1284  				PerArg{s(EqualTo(a1)), av, av, av, av, av, av},
  1285  				PerArg{s(EqualTo(b1)), av, av, av, av, av, av},
  1286  				PerArg{s(EqualTo(b2)), av, av, av, av, av, av},
  1287  				PerArg{s(EqualTo(c1)), av, av, av, av, av, av},
  1288  				PerArg{s(EqualTo(d0)), av, av, av, av, av, av},
  1289  				PerArg{s(EqualTo(d1)), av, av, av, av, av, av},
  1290  			},
  1291  		},
  1292  		{
  1293  			name: "flatten And rule",
  1294  			rule: And{
  1295  				And{
  1296  					PerArg{NotEqual(0x11)},
  1297  					And{
  1298  						PerArg{NotEqual(0x22)},
  1299  						PerArg{NotEqual(0x33)},
  1300  					},
  1301  					PerArg{NotEqual(0x44)},
  1302  				},
  1303  				And{
  1304  					PerArg{NotEqual(0x55)},
  1305  					PerArg{NotEqual(0x66)},
  1306  				},
  1307  			},
  1308  			want: And{
  1309  				PerArg{NotEqual(0x11), av, av, av, av, av, av},
  1310  				PerArg{NotEqual(0x22), av, av, av, av, av, av},
  1311  				PerArg{NotEqual(0x33), av, av, av, av, av, av},
  1312  				PerArg{NotEqual(0x44), av, av, av, av, av, av},
  1313  				PerArg{NotEqual(0x55), av, av, av, av, av, av},
  1314  				PerArg{NotEqual(0x66), av, av, av, av, av, av},
  1315  			},
  1316  		},
  1317  		{
  1318  			name: "simplify Or with single rule",
  1319  			rule: Or{
  1320  				PerArg{EqualTo(0x11)},
  1321  			},
  1322  			want: PerArg{s(EqualTo(0x11)), av, av, av, av, av, av},
  1323  		},
  1324  		{
  1325  			name: "simplify And with single rule",
  1326  			rule: And{
  1327  				PerArg{EqualTo(0x11)},
  1328  			},
  1329  			want: PerArg{s(EqualTo(0x11)), av, av, av, av, av, av},
  1330  		},
  1331  		{
  1332  			name: "simplify Or with MatchAll",
  1333  			rule: Or{
  1334  				PerArg{EqualTo(0x11)},
  1335  				Or{
  1336  					MatchAll{},
  1337  				},
  1338  				PerArg{EqualTo(0x22)},
  1339  			},
  1340  			want: MatchAll{},
  1341  		},
  1342  		{
  1343  			name: "single MatchAll in Or is not an empty rule",
  1344  			rule: Or{
  1345  				MatchAll{},
  1346  				MatchAll{},
  1347  			},
  1348  			optimizers: []ruleOptimizerFunc{
  1349  				convertMatchAllOrXToMatchAll,
  1350  			},
  1351  			want: MatchAll{},
  1352  		},
  1353  		{
  1354  			name: "simplify And with MatchAll",
  1355  			rule: And{
  1356  				PerArg{NotEqual(0x11)},
  1357  				And{
  1358  					MatchAll{},
  1359  				},
  1360  				PerArg{NotEqual(0x22)},
  1361  			},
  1362  			want: And{
  1363  				PerArg{NotEqual(0x11), av, av, av, av, av, av},
  1364  				PerArg{NotEqual(0x22), av, av, av, av, av, av},
  1365  			},
  1366  		},
  1367  		{
  1368  			name: "single MatchAll in And is not optimized to an empty rule",
  1369  			rule: And{
  1370  				MatchAll{},
  1371  				MatchAll{},
  1372  			},
  1373  			optimizers: []ruleOptimizerFunc{
  1374  				convertMatchAllAndXToX,
  1375  			},
  1376  			want: MatchAll{},
  1377  		},
  1378  		{
  1379  			name: "PerArg nil to AnyValue",
  1380  			rule: PerArg{av, EqualTo(0)},
  1381  			optimizers: []ruleOptimizerFunc{
  1382  				nilInPerArgToAnyValue,
  1383  			},
  1384  			want: PerArg{av, EqualTo(0), av, av, av, av, av},
  1385  		},
  1386  		{
  1387  			name: "Useless PerArg is MatchAll",
  1388  			rule: PerArg{av, av},
  1389  			optimizers: []ruleOptimizerFunc{
  1390  				nilInPerArgToAnyValue,
  1391  				convertUselessPerArgToMatchAll,
  1392  			},
  1393  			want: MatchAll{},
  1394  		},
  1395  		{
  1396  			name: "halfValueMatchers are simplified",
  1397  			rule: PerArg{
  1398  				EqualTo(0),
  1399  				splitMatcher{
  1400  					highMatcher: halfNotSet(0),
  1401  					lowMatcher: halfMaskedEqual{
  1402  						mask:  0,
  1403  						value: 0x89abcdef,
  1404  					},
  1405  				},
  1406  				splitMatcher{
  1407  					highMatcher: halfNotSet(1 << 4),
  1408  					lowMatcher: halfMaskedEqual{
  1409  						mask:  0xffffffff,
  1410  						value: 0x89abcdef,
  1411  					},
  1412  				},
  1413  				splitMatcher{
  1414  					highMatcher: halfNotSet(0),
  1415  					lowMatcher: halfMaskedEqual{
  1416  						mask:  0xffffffff,
  1417  						value: 0x89abcdef,
  1418  					},
  1419  				},
  1420  				splitMatcher{
  1421  					highMatcher: halfNotSet(0),
  1422  					lowMatcher: halfMaskedEqual{
  1423  						mask:  0,
  1424  						value: 0,
  1425  					},
  1426  				},
  1427  				splitMatcher{
  1428  					highMatcher: halfAnyValue{},
  1429  					lowMatcher: halfMaskedEqual{
  1430  						mask:  0x89abcdef,
  1431  						value: 0x89abcdef,
  1432  					},
  1433  				},
  1434  				BitsAllowlist(0xabcdef),
  1435  			},
  1436  			want: PerArg{
  1437  				s(EqualTo(0)),
  1438  				splitMatcher{
  1439  					highMatcher: halfAnyValue{},
  1440  					lowMatcher: halfMaskedEqual{
  1441  						mask:  0,
  1442  						value: 0x89abcdef,
  1443  					},
  1444  				},
  1445  				splitMatcher{
  1446  					highMatcher: halfNotSet(1 << 4),
  1447  					lowMatcher:  halfEqualTo(0x89abcdef),
  1448  				},
  1449  				splitMatcher{
  1450  					highMatcher: halfAnyValue{},
  1451  					lowMatcher:  halfEqualTo(0x89abcdef),
  1452  				},
  1453  				av, // Both halves are simplified to `halfAnyValue`.
  1454  				splitMatcher{
  1455  					highMatcher: halfAnyValue{},
  1456  					lowMatcher: halfMaskedEqual{
  1457  						mask:  0x89abcdef,
  1458  						value: 0x89abcdef,
  1459  					},
  1460  				},
  1461  				splitMatcher{
  1462  					// High half gets simplified to equaling zero.
  1463  					highMatcher: halfEqualTo(0),
  1464  					// Low half gets simplified to a bit-not-set check.
  1465  					lowMatcher: halfNotSet(^uint32(0xabcdef)),
  1466  					// Keep user-friendly representation.
  1467  					repr: fmt.Sprintf("& %#x == %#x", ^uint64(0xabcdef), 0),
  1468  				},
  1469  			},
  1470  		},
  1471  		{
  1472  			name: "Common value matchers in PerArg are extracted",
  1473  			rule: Or{
  1474  				PerArg{EqualTo(a1), EqualTo(b1), EqualTo(c1), EqualTo(d0)},
  1475  				PerArg{EqualTo(a2), EqualTo(b1), EqualTo(c1), EqualTo(d0)},
  1476  				PerArg{EqualTo(a1), EqualTo(b2), EqualTo(c2), EqualTo(d0)},
  1477  				PerArg{EqualTo(a2), EqualTo(b2), EqualTo(c2), EqualTo(d0)},
  1478  				PerArg{EqualTo(a1), EqualTo(b3), EqualTo(c3), EqualTo(d0)},
  1479  				PerArg{EqualTo(a2), EqualTo(b3), EqualTo(c3), EqualTo(d0)},
  1480  			},
  1481  			want: And{
  1482  				Or{
  1483  					PerArg{s(EqualTo(a1)), av, av, av, av, av, av},
  1484  					PerArg{s(EqualTo(a2)), av, av, av, av, av, av},
  1485  				},
  1486  				PerArg{av, av, av, s(EqualTo(d0)), av, av, av},
  1487  				Or{
  1488  					PerArg{av, s(EqualTo(b1)), s(EqualTo(c1)), av, av, av, av},
  1489  					PerArg{av, s(EqualTo(b2)), s(EqualTo(c2)), av, av, av, av},
  1490  					PerArg{av, s(EqualTo(b3)), s(EqualTo(c3)), av, av, av, av},
  1491  				},
  1492  			},
  1493  		},
  1494  		{
  1495  			name: "Common halfValueMatchers in splitMatchers are extracted",
  1496  			rule: Or{
  1497  				PerArg{EqualTo(0xA1), EqualTo(0xB1), EqualTo(c1), EqualTo(d0)},
  1498  				PerArg{EqualTo(0xA1), EqualTo(0xB1), EqualTo(c2), EqualTo(d0)},
  1499  				PerArg{EqualTo(0xA2), EqualTo(0xB2), EqualTo(c1), EqualTo(d0)},
  1500  				PerArg{EqualTo(0xA2), EqualTo(0xB2), EqualTo(c2), EqualTo(d0)},
  1501  			},
  1502  			want: And{
  1503  				PerArg{highEq(0), av, av, av, av, av, av},
  1504  				PerArg{av, highEq(0), av, av, av, av, av},
  1505  				Or{
  1506  					PerArg{av, av, s(EqualTo(c1)), av, av, av, av},
  1507  					PerArg{av, av, s(EqualTo(c2)), av, av, av, av},
  1508  				},
  1509  				PerArg{av, av, av, s(EqualTo(d0)), av, av, av},
  1510  				Or{
  1511  					PerArg{lowEq(0xA1), lowEq(0xB1), av, av, av, av, av},
  1512  					PerArg{lowEq(0xA2), lowEq(0xB2), av, av, av, av, av},
  1513  				},
  1514  			},
  1515  		},
  1516  	} {
  1517  		t.Run(test.name, func(t *testing.T) {
  1518  			var got SyscallRule
  1519  			if len(test.optimizers) == 0 {
  1520  				got = optimizeSyscallRule(test.rule)
  1521  			} else {
  1522  				got = (&optimizationRun{funcs: test.optimizers}).optimize(test.rule)
  1523  			}
  1524  			if !reflect.DeepEqual(got, test.want) {
  1525  				t.Errorf("got rule:\n%v\nwant rule:\n%v\n", got, test.want)
  1526  			}
  1527  		})
  1528  	}
  1529  }
  1530  
  1531  func TestOrderRuleSets(t *testing.T) {
  1532  	for _, test := range []struct {
  1533  		name     string
  1534  		ruleSets []RuleSet
  1535  		options  ProgramOptions // Optimizations are always enabled regardless of this
  1536  		want     orderedRuleSets
  1537  		wantErr  bool
  1538  	}{
  1539  		{
  1540  			name:    "no RuleSets",
  1541  			options: DefaultProgramOptions(),
  1542  			want:    orderedRuleSets{},
  1543  		},
  1544  		{
  1545  			name:    "inconsistent vsyscall",
  1546  			options: DefaultProgramOptions(),
  1547  			ruleSets: []RuleSet{
  1548  				{
  1549  					Rules:    NewSyscallRules().Add(unix.SYS_READ, MatchAll{}),
  1550  					Action:   linux.SECCOMP_RET_TRACE,
  1551  					Vsyscall: false,
  1552  				},
  1553  				{
  1554  					Rules:    NewSyscallRules().Add(unix.SYS_READ, MatchAll{}),
  1555  					Action:   linux.SECCOMP_RET_TRACE,
  1556  					Vsyscall: true,
  1557  				},
  1558  			},
  1559  			wantErr: true,
  1560  		},
  1561  		{
  1562  			name: "single trivial rule",
  1563  			ruleSets: []RuleSet{
  1564  				{
  1565  					Rules:    NewSyscallRules().Add(unix.SYS_READ, MatchAll{}),
  1566  					Action:   linux.SECCOMP_RET_TRACE,
  1567  					Vsyscall: false,
  1568  				},
  1569  			},
  1570  			options: DefaultProgramOptions(),
  1571  			want: orderedRuleSets{
  1572  				trivial: map[uintptr]singleSyscallRuleSet{
  1573  					unix.SYS_READ: {
  1574  						sysno: unix.SYS_READ,
  1575  						rules: []syscallRuleAction{
  1576  							{
  1577  								rule:   MatchAll{},
  1578  								action: linux.SECCOMP_RET_TRACE,
  1579  							},
  1580  						},
  1581  						vsyscall: false,
  1582  					},
  1583  				},
  1584  			},
  1585  		},
  1586  		{
  1587  			name: "hot single trivial rule still ends up as trivial",
  1588  			ruleSets: []RuleSet{
  1589  				{
  1590  					Rules:    NewSyscallRules().Add(unix.SYS_READ, MatchAll{}),
  1591  					Action:   linux.SECCOMP_RET_TRACE,
  1592  					Vsyscall: false,
  1593  				},
  1594  			},
  1595  			options: ProgramOptions{
  1596  				HotSyscalls: []uintptr{unix.SYS_READ},
  1597  			},
  1598  			want: orderedRuleSets{
  1599  				trivial: map[uintptr]singleSyscallRuleSet{
  1600  					unix.SYS_READ: {
  1601  						sysno: unix.SYS_READ,
  1602  						rules: []syscallRuleAction{
  1603  							{
  1604  								rule:   MatchAll{},
  1605  								action: linux.SECCOMP_RET_TRACE,
  1606  							},
  1607  						},
  1608  						vsyscall: false,
  1609  					},
  1610  				},
  1611  			},
  1612  		},
  1613  		{
  1614  			name: "hot single non-trivial rule",
  1615  			ruleSets: []RuleSet{
  1616  				{
  1617  					Rules: NewSyscallRules().Add(
  1618  						unix.SYS_READ, PerArg{EqualTo(0)},
  1619  					),
  1620  					Action:   linux.SECCOMP_RET_TRACE,
  1621  					Vsyscall: false,
  1622  				},
  1623  			},
  1624  			options: ProgramOptions{
  1625  				HotSyscalls: []uintptr{unix.SYS_READ},
  1626  			},
  1627  			want: orderedRuleSets{
  1628  				hotNonTrivial: map[uintptr]singleSyscallRuleSet{
  1629  					unix.SYS_READ: {
  1630  						sysno: unix.SYS_READ,
  1631  						rules: []syscallRuleAction{
  1632  							{
  1633  								rule:   PerArg{EqualTo(0)},
  1634  								action: linux.SECCOMP_RET_TRACE,
  1635  							},
  1636  						},
  1637  						vsyscall: false,
  1638  					},
  1639  				},
  1640  				hotNonTrivialOrder: []uintptr{unix.SYS_READ},
  1641  			},
  1642  		},
  1643  		{
  1644  			name: "hot rule ordering",
  1645  			ruleSets: []RuleSet{
  1646  				{
  1647  					Rules: NewSyscallRules().Add(
  1648  						unix.SYS_FLOCK, PerArg{EqualTo(0)},
  1649  					),
  1650  					Action:   linux.SECCOMP_RET_TRACE,
  1651  					Vsyscall: false,
  1652  				},
  1653  				{
  1654  					Rules: NewSyscallRules().Add(
  1655  						unix.SYS_WRITE, PerArg{EqualTo(1)},
  1656  					).Add(
  1657  						unix.SYS_READ, PerArg{EqualTo(2)},
  1658  					),
  1659  					Action:   linux.SECCOMP_RET_TRACE,
  1660  					Vsyscall: false,
  1661  				},
  1662  			},
  1663  			options: ProgramOptions{
  1664  				HotSyscalls: []uintptr{
  1665  					unix.SYS_READ,
  1666  					unix.SYS_WRITE,
  1667  					unix.SYS_FLOCK,
  1668  				},
  1669  			},
  1670  			want: orderedRuleSets{
  1671  				hotNonTrivial: map[uintptr]singleSyscallRuleSet{
  1672  					unix.SYS_READ: {
  1673  						sysno: unix.SYS_READ,
  1674  						rules: []syscallRuleAction{
  1675  							{
  1676  								rule:   PerArg{EqualTo(2)},
  1677  								action: linux.SECCOMP_RET_TRACE,
  1678  							},
  1679  						},
  1680  						vsyscall: false,
  1681  					},
  1682  					unix.SYS_WRITE: {
  1683  						sysno: unix.SYS_WRITE,
  1684  						rules: []syscallRuleAction{
  1685  							{
  1686  								rule:   PerArg{EqualTo(1)},
  1687  								action: linux.SECCOMP_RET_TRACE,
  1688  							},
  1689  						},
  1690  						vsyscall: false,
  1691  					},
  1692  					unix.SYS_FLOCK: {
  1693  						sysno: unix.SYS_FLOCK,
  1694  						rules: []syscallRuleAction{
  1695  							{
  1696  								rule:   PerArg{EqualTo(0)},
  1697  								action: linux.SECCOMP_RET_TRACE,
  1698  							},
  1699  						},
  1700  						vsyscall: false,
  1701  					},
  1702  				},
  1703  				hotNonTrivialOrder: []uintptr{
  1704  					unix.SYS_READ,
  1705  					unix.SYS_WRITE,
  1706  					unix.SYS_FLOCK,
  1707  				},
  1708  			},
  1709  		},
  1710  		{
  1711  			name: "cold single non-trivial non-vsyscall rule",
  1712  			ruleSets: []RuleSet{
  1713  				{
  1714  					Rules: NewSyscallRules().Add(
  1715  						unix.SYS_READ, PerArg{EqualTo(0)},
  1716  					),
  1717  					Action:   linux.SECCOMP_RET_TRACE,
  1718  					Vsyscall: false,
  1719  				},
  1720  			},
  1721  			options: ProgramOptions{
  1722  				HotSyscalls: []uintptr{unix.SYS_WRITE},
  1723  			},
  1724  			want: orderedRuleSets{
  1725  				coldNonTrivial: map[uintptr]singleSyscallRuleSet{
  1726  					unix.SYS_READ: {
  1727  						sysno: unix.SYS_READ,
  1728  						rules: []syscallRuleAction{
  1729  							{
  1730  								rule:   PerArg{EqualTo(0)},
  1731  								action: linux.SECCOMP_RET_TRACE,
  1732  							},
  1733  						},
  1734  						vsyscall: false,
  1735  					},
  1736  				},
  1737  				hotNonTrivialOrder: nil, // Empty
  1738  			},
  1739  		},
  1740  		{
  1741  			name: "cold single non-trivial yes-vsyscall rule",
  1742  			ruleSets: []RuleSet{
  1743  				{
  1744  					Rules: NewSyscallRules().Add(
  1745  						unix.SYS_READ, PerArg{EqualTo(0)},
  1746  					),
  1747  					Action:   linux.SECCOMP_RET_TRACE,
  1748  					Vsyscall: true,
  1749  				},
  1750  			},
  1751  			options: ProgramOptions{
  1752  				HotSyscalls: []uintptr{unix.SYS_WRITE},
  1753  			},
  1754  			want: orderedRuleSets{
  1755  				coldNonTrivial: map[uintptr]singleSyscallRuleSet{
  1756  					unix.SYS_READ: {
  1757  						sysno: unix.SYS_READ,
  1758  						rules: []syscallRuleAction{
  1759  							{
  1760  								rule:   PerArg{EqualTo(0)},
  1761  								action: linux.SECCOMP_RET_TRACE,
  1762  							},
  1763  						},
  1764  						vsyscall: true,
  1765  					},
  1766  				},
  1767  				hotNonTrivialOrder: nil, // Empty
  1768  			},
  1769  		},
  1770  		{
  1771  			name: "all rule types at once",
  1772  			ruleSets: []RuleSet{
  1773  				{
  1774  					Rules: NewSyscallRules().Add(
  1775  						// Hot, non-vsyscall, trivial
  1776  						unix.SYS_READ, MatchAll{},
  1777  					).Add(
  1778  						// Hot, non-vsyscall, non-trivial
  1779  						unix.SYS_WRITE, PerArg{EqualTo(4)},
  1780  					).Add(
  1781  						// Hot, non-vsyscall, non-trivial
  1782  						unix.SYS_FLOCK, PerArg{EqualTo(131)},
  1783  					),
  1784  					Action:   linux.SECCOMP_RET_TRACE,
  1785  					Vsyscall: false,
  1786  				},
  1787  				{
  1788  					Rules: NewSyscallRules().Add(
  1789  						// Hot, vsyscall, trivial (after optimizations)
  1790  						unix.SYS_FUTEX, PerArg{AnyValue{}},
  1791  					).Add(
  1792  						// Hot, vsyscall, non-trivial
  1793  						unix.SYS_GETPID, PerArg{EqualTo(20)},
  1794  					).Add(
  1795  						// Cold, vsyscall, trivial (after optimizations)
  1796  						unix.SYS_GETTIMEOFDAY, PerArg{AnyValue{}},
  1797  					).Add(
  1798  						// Cold, vsyscall, non-trivial
  1799  						unix.SYS_MINCORE, PerArg{EqualTo(78)},
  1800  					),
  1801  					Action:   linux.SECCOMP_RET_ERRNO,
  1802  					Vsyscall: true,
  1803  				},
  1804  				{
  1805  					Rules: NewSyscallRules().Add(
  1806  						// Hot, non-vsyscall, trivial (after optimizations)
  1807  						unix.SYS_CLOSE, PerArg{AnyValue{}},
  1808  					).Add(
  1809  						// Hot, non-vsyscall, non-trivial
  1810  						unix.SYS_OPENAT, PerArg{EqualTo(463)},
  1811  					).Add(
  1812  						// Cold, non-vsyscall, non-trivial
  1813  						unix.SYS_LINKAT, PerArg{EqualTo(471)},
  1814  					).Add(
  1815  						// Cold, non-vsyscall, trivial here but
  1816  						// a later RuleSet will make it non-trivial
  1817  						// overall.
  1818  						unix.SYS_UNLINKAT, MatchAll{},
  1819  					).Add(
  1820  						// Cold, non-vsyscall, trivial and another
  1821  						// later RuleSet will keep it trivial later.
  1822  						unix.SYS_CHDIR, MatchAll{},
  1823  					),
  1824  					Action:   linux.SECCOMP_RET_KILL_THREAD,
  1825  					Vsyscall: false,
  1826  				},
  1827  				{
  1828  					Rules: NewSyscallRules().Add(
  1829  						// Hot, non-vsyscall, non-trivial
  1830  						unix.SYS_OPENAT, PerArg{EqualTo(463463)},
  1831  					).Add(
  1832  						// Cold, non-vsyscall, non-trivial
  1833  						unix.SYS_LINKAT, PerArg{EqualTo(471471)},
  1834  					).Add(
  1835  						// Cold, non-vsyscall, no longer trivial.
  1836  						unix.SYS_UNLINKAT, PerArg{EqualTo(472)},
  1837  					).Add(
  1838  						// Cold, non-vsyscall, remains trivial.
  1839  						unix.SYS_FCHDIR, PerArg{AnyValue{}},
  1840  					),
  1841  					Action:   linux.SECCOMP_RET_KILL_PROCESS,
  1842  					Vsyscall: false,
  1843  				},
  1844  				{
  1845  					Rules: NewSyscallRules().Add(
  1846  						// Cold, non-vsyscall, adds to previous rule
  1847  						// after optimizations because it has the
  1848  						// same action.
  1849  						unix.SYS_UNLINKAT, PerArg{EqualTo(472472)},
  1850  					),
  1851  					Action:   linux.SECCOMP_RET_KILL_PROCESS,
  1852  					Vsyscall: false,
  1853  				},
  1854  				{
  1855  					Rules: NewSyscallRules().Add(
  1856  						// Cold, non-vsyscall, does not add to
  1857  						// previous rule because it has a different
  1858  						// action.
  1859  						unix.SYS_UNLINKAT, PerArg{EqualTo(472472472)},
  1860  					),
  1861  					Action:   linux.SECCOMP_RET_TRAP,
  1862  					Vsyscall: false,
  1863  				},
  1864  			},
  1865  			options: ProgramOptions{
  1866  				HotSyscalls: []uintptr{
  1867  					unix.SYS_READ,
  1868  					unix.SYS_WRITE,
  1869  					unix.SYS_FLOCK,
  1870  					unix.SYS_CLOSE,
  1871  					unix.SYS_OPENAT,
  1872  					unix.SYS_GETPID,
  1873  					// Note: not used in any RuleSet, so it should not
  1874  					// appear in `want`:
  1875  					unix.SYS_GETTID,
  1876  				},
  1877  			},
  1878  			want: orderedRuleSets{
  1879  				hotNonTrivial: map[uintptr]singleSyscallRuleSet{
  1880  					unix.SYS_WRITE: {
  1881  						sysno: unix.SYS_WRITE,
  1882  						rules: []syscallRuleAction{
  1883  							{
  1884  								rule:   PerArg{EqualTo(4)},
  1885  								action: linux.SECCOMP_RET_TRACE,
  1886  							},
  1887  						},
  1888  						vsyscall: false,
  1889  					},
  1890  					unix.SYS_FLOCK: {
  1891  						sysno: unix.SYS_FLOCK,
  1892  						rules: []syscallRuleAction{
  1893  							{
  1894  								rule:   PerArg{EqualTo(131)},
  1895  								action: linux.SECCOMP_RET_TRACE,
  1896  							},
  1897  						},
  1898  						vsyscall: false,
  1899  					},
  1900  					unix.SYS_GETPID: {
  1901  						sysno: unix.SYS_GETPID,
  1902  						rules: []syscallRuleAction{
  1903  							{
  1904  								rule:   PerArg{EqualTo(20)},
  1905  								action: linux.SECCOMP_RET_ERRNO,
  1906  							},
  1907  						},
  1908  						vsyscall: true,
  1909  					},
  1910  					unix.SYS_OPENAT: {
  1911  						sysno: unix.SYS_OPENAT,
  1912  						rules: []syscallRuleAction{
  1913  							{
  1914  								rule:   PerArg{EqualTo(463)},
  1915  								action: linux.SECCOMP_RET_KILL_THREAD,
  1916  							},
  1917  							{
  1918  								rule:   PerArg{EqualTo(463463)},
  1919  								action: linux.SECCOMP_RET_KILL_PROCESS,
  1920  							},
  1921  						},
  1922  						vsyscall: false,
  1923  					},
  1924  				},
  1925  				coldNonTrivial: map[uintptr]singleSyscallRuleSet{
  1926  					unix.SYS_LINKAT: {
  1927  						sysno: unix.SYS_LINKAT,
  1928  						rules: []syscallRuleAction{
  1929  							{
  1930  								rule:   PerArg{EqualTo(471)},
  1931  								action: linux.SECCOMP_RET_KILL_THREAD,
  1932  							},
  1933  							{
  1934  								rule:   PerArg{EqualTo(471471)},
  1935  								action: linux.SECCOMP_RET_KILL_PROCESS,
  1936  							},
  1937  						},
  1938  						vsyscall: false,
  1939  					},
  1940  					unix.SYS_UNLINKAT: {
  1941  						sysno: unix.SYS_UNLINKAT,
  1942  						rules: []syscallRuleAction{
  1943  							{
  1944  								rule:   MatchAll{},
  1945  								action: linux.SECCOMP_RET_KILL_THREAD,
  1946  							},
  1947  							{
  1948  								rule: Or{
  1949  									PerArg{EqualTo(472)},
  1950  									PerArg{EqualTo(472472)},
  1951  								},
  1952  								action: linux.SECCOMP_RET_KILL_PROCESS,
  1953  							},
  1954  							{
  1955  								rule:   PerArg{EqualTo(472472472)},
  1956  								action: linux.SECCOMP_RET_TRAP,
  1957  							},
  1958  						},
  1959  						vsyscall: false,
  1960  					},
  1961  					unix.SYS_MINCORE: {
  1962  						sysno: unix.SYS_MINCORE,
  1963  						rules: []syscallRuleAction{
  1964  							{
  1965  								rule:   PerArg{EqualTo(78)},
  1966  								action: linux.SECCOMP_RET_ERRNO,
  1967  							},
  1968  						},
  1969  						vsyscall: true,
  1970  					},
  1971  					unix.SYS_FUTEX: {
  1972  						sysno: unix.SYS_FUTEX,
  1973  						rules: []syscallRuleAction{
  1974  							{
  1975  								rule:   MatchAll{},
  1976  								action: linux.SECCOMP_RET_ERRNO,
  1977  							},
  1978  						},
  1979  						vsyscall: true,
  1980  					},
  1981  					unix.SYS_GETTIMEOFDAY: {
  1982  						sysno: unix.SYS_GETTIMEOFDAY,
  1983  						rules: []syscallRuleAction{
  1984  							{
  1985  								rule:   MatchAll{},
  1986  								action: linux.SECCOMP_RET_ERRNO,
  1987  							},
  1988  						},
  1989  						vsyscall: true,
  1990  					},
  1991  				},
  1992  				trivial: map[uintptr]singleSyscallRuleSet{
  1993  					unix.SYS_READ: {
  1994  						sysno: unix.SYS_READ,
  1995  						rules: []syscallRuleAction{
  1996  							{
  1997  								rule:   MatchAll{},
  1998  								action: linux.SECCOMP_RET_TRACE,
  1999  							},
  2000  						},
  2001  						vsyscall: false,
  2002  					},
  2003  					unix.SYS_CHDIR: {
  2004  						sysno: unix.SYS_CHDIR,
  2005  						rules: []syscallRuleAction{
  2006  							{
  2007  								rule:   MatchAll{},
  2008  								action: linux.SECCOMP_RET_KILL_THREAD,
  2009  							},
  2010  						},
  2011  						vsyscall: false,
  2012  					},
  2013  					unix.SYS_CLOSE: {
  2014  						sysno: unix.SYS_CLOSE,
  2015  						rules: []syscallRuleAction{
  2016  							{
  2017  								rule:   MatchAll{},
  2018  								action: linux.SECCOMP_RET_KILL_THREAD,
  2019  							},
  2020  						},
  2021  						vsyscall: false,
  2022  					},
  2023  					unix.SYS_FCHDIR: {
  2024  						sysno: unix.SYS_FCHDIR,
  2025  						rules: []syscallRuleAction{
  2026  							{
  2027  								rule:   MatchAll{},
  2028  								action: linux.SECCOMP_RET_KILL_PROCESS,
  2029  							},
  2030  						},
  2031  						vsyscall: false,
  2032  					},
  2033  				},
  2034  				hotNonTrivialOrder: []uintptr{
  2035  					// SYS_READ becomes trivial so it does not show up here.
  2036  					unix.SYS_WRITE,
  2037  					unix.SYS_FLOCK,
  2038  					// SYS_CLOSE also becomes trivial.
  2039  					unix.SYS_OPENAT,
  2040  					unix.SYS_GETPID,
  2041  					// SYS_GETTID does not show up in any RuleSet so it
  2042  					// also does not show up here.
  2043  				},
  2044  			},
  2045  		},
  2046  	} {
  2047  		t.Run(test.name, func(t *testing.T) {
  2048  			test.options.Optimize = true
  2049  			got, _, gotErr := orderRuleSets(test.ruleSets, test.options)
  2050  			if (gotErr != nil) != test.wantErr {
  2051  				t.Errorf("got error: %v, want error: %v", gotErr, test.wantErr)
  2052  			}
  2053  			if gotErr != nil || t.Failed() {
  2054  				return
  2055  			}
  2056  			// Replace empty maps with nil for simpler comparison output.
  2057  			for _, ors := range []*orderedRuleSets{&got, &test.want} {
  2058  				if len(ors.hotNonTrivial) == 0 {
  2059  					ors.hotNonTrivial = nil
  2060  				}
  2061  				if len(ors.hotNonTrivialOrder) == 0 {
  2062  					ors.hotNonTrivialOrder = nil
  2063  				}
  2064  				if len(ors.coldNonTrivial) == 0 {
  2065  					ors.coldNonTrivial = nil
  2066  				}
  2067  				if len(ors.trivial) == 0 {
  2068  					ors.trivial = nil
  2069  				}
  2070  			}
  2071  			// Run optimizers on all rules of `test.want`.
  2072  			for _, m := range []map[uintptr]singleSyscallRuleSet{
  2073  				test.want.hotNonTrivial,
  2074  				test.want.coldNonTrivial,
  2075  				test.want.trivial,
  2076  			} {
  2077  				for sysno, ssrs := range m {
  2078  					for i, r := range ssrs.rules {
  2079  						ssrs.rules[i].rule = optimizeSyscallRule(r.rule)
  2080  					}
  2081  					m[sysno] = ssrs
  2082  				}
  2083  			}
  2084  			if !reflect.DeepEqual(got, test.want) {
  2085  				t.Errorf("got orderedRuleSets:\n%v\nwant orderedRuleSets:\n%v\n", got, test.want)
  2086  				t.Error("got:")
  2087  				got.log(t.Errorf)
  2088  				t.Errorf("want:")
  2089  				test.want.log(t.Errorf)
  2090  				return
  2091  			}
  2092  			// Log the orderedRuleSet, if only to make sure the log function
  2093  			// actually works and doesn't panic:
  2094  			t.Log("orderedRuleSets matched expectations:")
  2095  			got.log(t.Logf)
  2096  
  2097  			// Attempt to render the `orderedRuleSets`.
  2098  			// We don't check the instructions it generates, but the rendering
  2099  			// code checks itself and panics if it fails an assertion.
  2100  			program := &syscallProgram{bpf.NewProgramBuilder()}
  2101  			if err := got.render(program); err != nil {
  2102  				t.Fatalf("got unexpected error while rendering: %v", err)
  2103  			}
  2104  		})
  2105  	}
  2106  }