gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/bpf/interpreter_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 bpf
    16  
    17  import (
    18  	"reflect"
    19  	"slices"
    20  	"testing"
    21  
    22  	"gvisor.dev/gvisor/pkg/abi/linux"
    23  	"gvisor.dev/gvisor/pkg/hostarch"
    24  	"gvisor.dev/gvisor/pkg/marshal"
    25  )
    26  
    27  func TestCompilationErrors(t *testing.T) {
    28  	for _, test := range []struct {
    29  		// desc is the test's description.
    30  		desc string
    31  
    32  		// insns is the BPF instructions to be compiled.
    33  		insns []Instruction
    34  
    35  		// expectedErr is the expected compilation error.
    36  		expectedErr error
    37  	}{
    38  		{
    39  			desc:        "Instructions must not be nil",
    40  			expectedErr: Error{InvalidInstructionCount, 0},
    41  		},
    42  		{
    43  			desc:        "Instructions must not be empty",
    44  			insns:       []Instruction{},
    45  			expectedErr: Error{InvalidInstructionCount, 0},
    46  		},
    47  		{
    48  			desc:        "A program must end with a return",
    49  			insns:       make([]Instruction, MaxInstructions),
    50  			expectedErr: Error{InvalidEndOfProgram, MaxInstructions - 1},
    51  		},
    52  		{
    53  			desc:        "A program must have MaxInstructions or fewer instructions",
    54  			insns:       append(make([]Instruction, MaxInstructions), Stmt(Ret|K, 0)),
    55  			expectedErr: Error{InvalidInstructionCount, MaxInstructions + 1},
    56  		},
    57  		{
    58  			desc: "A load from an invalid M register is a compilation error",
    59  			insns: []Instruction{
    60  				Stmt(Ld|Mem|W, ScratchMemRegisters), // A = M[16]
    61  				Stmt(Ret|K, 0),                      // return 0
    62  			},
    63  			expectedErr: Error{InvalidRegister, 0},
    64  		},
    65  		{
    66  			desc: "A store to an invalid M register is a compilation error",
    67  			insns: []Instruction{
    68  				Stmt(St, ScratchMemRegisters), // M[16] = A
    69  				Stmt(Ret|K, 0),                // return 0
    70  			},
    71  			expectedErr: Error{InvalidRegister, 0},
    72  		},
    73  		{
    74  			desc: "Division by literal zero is a compilation error",
    75  			insns: []Instruction{
    76  				Stmt(Alu|Div|K, 0), // A /= 0
    77  				Stmt(Ret|K, 0),     // return 0
    78  			},
    79  			expectedErr: Error{DivisionByZero, 0},
    80  		},
    81  		{
    82  			desc: "An unconditional jump outside of the program is a compilation error",
    83  			insns: []Instruction{
    84  				Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1
    85  				Stmt(Ret|K, 0),        // return 0
    86  			},
    87  			expectedErr: Error{InvalidJumpTarget, 0},
    88  		},
    89  		{
    90  			desc: "A conditional jump outside of the program in the true case is a compilation error",
    91  			insns: []Instruction{
    92  				Jump(Jmp|Jeq|K, 0, 1, 0), // if (A == K) jmp nextpc+1
    93  				Stmt(Ret|K, 0),           // return 0
    94  			},
    95  			expectedErr: Error{InvalidJumpTarget, 0},
    96  		},
    97  		{
    98  			desc: "A conditional jump outside of the program in the false case is a compilation error",
    99  			insns: []Instruction{
   100  				Jump(Jmp|Jeq|K, 0, 0, 1), // if (A != K) jmp nextpc+1
   101  				Stmt(Ret|K, 0),           // return 0
   102  			},
   103  			expectedErr: Error{InvalidJumpTarget, 0},
   104  		},
   105  	} {
   106  		t.Run(test.desc, func(t *testing.T) {
   107  			_, err := Compile(test.insns, false)
   108  			if err != test.expectedErr {
   109  				t.Errorf("expected error %q, got error %q", test.expectedErr, err)
   110  			}
   111  		})
   112  	}
   113  }
   114  
   115  func TestExecErrors(t *testing.T) {
   116  	for _, test := range []struct {
   117  		// desc is the test's description.
   118  		desc string
   119  
   120  		// insns is the BPF instructions to be executed.
   121  		insns []Instruction
   122  
   123  		// expectedErr is the expected execution error.
   124  		expectedErr error
   125  	}{
   126  		{
   127  			desc: "An out-of-bounds load of input data is an execution error",
   128  			insns: []Instruction{
   129  				Stmt(Ld|Abs|B, 0), // A = input[0]
   130  				Stmt(Ret|K, 0),    // return 0
   131  			},
   132  			expectedErr: Error{InvalidLoad, 0},
   133  		},
   134  		{
   135  			desc: "Division by zero at runtime is an execution error",
   136  			insns: []Instruction{
   137  				Stmt(Alu|Div|X, 0), // A /= X
   138  				Stmt(Ret|K, 0),     // return 0
   139  			},
   140  			expectedErr: Error{DivisionByZero, 0},
   141  		},
   142  		{
   143  			desc: "Modulo zero at runtime is an execution error",
   144  			insns: []Instruction{
   145  				Stmt(Alu|Mod|X, 0), // A %= X
   146  				Stmt(Ret|K, 0),     // return 0
   147  			},
   148  			expectedErr: Error{DivisionByZero, 0},
   149  		},
   150  	} {
   151  		t.Run(test.desc, func(t *testing.T) {
   152  			p, err := Compile(test.insns, false)
   153  			if err != nil {
   154  				t.Fatalf("unexpected compilation error: %v", err)
   155  			}
   156  			inp := Input{}
   157  			execution, err := InstrumentedExec[NativeEndian](p, inp)
   158  			if err != test.expectedErr {
   159  				t.Fatalf("expected execution error %q, got (%v, %v)", test.expectedErr, execution, err)
   160  			}
   161  			ret, err := Exec[NativeEndian](p, inp)
   162  			if err != test.expectedErr {
   163  				t.Fatalf("expected execution error %q, got (%d, %v)", test.expectedErr, ret, err)
   164  			}
   165  			optimizedProgram, err := Compile(test.insns, true)
   166  			if err != nil {
   167  				t.Fatalf("unexpected compilation error: %v", err)
   168  			}
   169  			if _, err := InstrumentedExec[NativeEndian](optimizedProgram, inp); err != test.expectedErr {
   170  				t.Fatalf("expected execution error from optimized program %q, got (%v, %v)", test.expectedErr, execution, err)
   171  			}
   172  		})
   173  	}
   174  }
   175  
   176  func TestValidInstructions(t *testing.T) {
   177  	want := func(ex ExecutionMetrics) func(insns []Instruction, input []byte) ExecutionMetrics {
   178  		return func(insns []Instruction, input []byte) ExecutionMetrics {
   179  			return ex
   180  		}
   181  	}
   182  	allCoveredNoneReadAndReturns := func(ret uint32) func(insns []Instruction, input []byte) ExecutionMetrics {
   183  		return func(insns []Instruction, input []byte) ExecutionMetrics {
   184  			coverage := make([]bool, len(insns))
   185  			for i := range insns {
   186  				coverage[i] = true
   187  			}
   188  			return ExecutionMetrics{
   189  				Coverage:      coverage,
   190  				InputAccessed: make([]bool, len(input)),
   191  				ReturnValue:   ret,
   192  			}
   193  		}
   194  	}
   195  	for _, test := range []struct {
   196  		// desc is the test's description.
   197  		desc string
   198  
   199  		// insns is the BPF instructions to be compiled.
   200  		insns []Instruction
   201  
   202  		// input is the input data. Note that input will be read as big-endian.
   203  		input Input
   204  
   205  		// expected is the expected result of executing the BPF program.
   206  		// It takes in the instructions and input that the test will run.
   207  		expected func(insns []Instruction, input []byte) ExecutionMetrics
   208  	}{
   209  		{
   210  			desc: "Return of immediate",
   211  			insns: []Instruction{
   212  				Stmt(Ret|K, 42), // return 42
   213  			},
   214  			expected: allCoveredNoneReadAndReturns(42),
   215  		},
   216  		{
   217  			desc: "Load of immediate into A",
   218  			insns: []Instruction{
   219  				Stmt(Ld|Imm|W, 42), // A = 42
   220  				Stmt(Ret|A, 0),     // return A
   221  			},
   222  			expected: allCoveredNoneReadAndReturns(42),
   223  		},
   224  		{
   225  			desc: "Load of immediate into X and copying of X into A",
   226  			insns: []Instruction{
   227  				Stmt(Ldx|Imm|W, 42), // X = 42
   228  				Stmt(Misc|Tax, 0),   // A = X
   229  				Stmt(Ret|A, 0),      // return A
   230  			},
   231  			expected: allCoveredNoneReadAndReturns(42),
   232  		},
   233  		{
   234  			desc: "Copying of A into X and back",
   235  			insns: []Instruction{
   236  				Stmt(Ld|Imm|W, 42), // A = 42
   237  				Stmt(Misc|Txa, 0),  // X = A
   238  				Stmt(Ld|Imm|W, 0),  // A = 0
   239  				Stmt(Misc|Tax, 0),  // A = X
   240  				Stmt(Ret|A, 0),     // return A
   241  			},
   242  			expected: allCoveredNoneReadAndReturns(42),
   243  		},
   244  		{
   245  			desc: "Load of 32-bit input by absolute offset into A",
   246  			insns: []Instruction{
   247  				Stmt(Ld|Abs|W, 1), // A = input[1..4]
   248  				Stmt(Ret|A, 0),    // return A
   249  			},
   250  			input: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
   251  			expected: want(ExecutionMetrics{
   252  				Coverage:      []bool{true, true},
   253  				InputAccessed: []bool{false, true, true, true, true, false},
   254  				ReturnValue:   hostarch.ByteOrder.Uint32([]byte{0x11, 0x22, 0x33, 0x44}),
   255  			}),
   256  		},
   257  		{
   258  			desc: "Load of 16-bit input by absolute offset into A",
   259  			insns: []Instruction{
   260  				Stmt(Ld|Abs|H, 1), // A = input[1..2]
   261  				Stmt(Ret|A, 0),    // return A
   262  			},
   263  			input: []byte{0x00, 0x11, 0x22, 0x33},
   264  			expected: want(ExecutionMetrics{
   265  				Coverage:      []bool{true, true},
   266  				InputAccessed: []bool{false, true, true, false},
   267  				ReturnValue:   uint32(hostarch.ByteOrder.Uint16([]byte{0x11, 0x22})),
   268  			}),
   269  		},
   270  		{
   271  			desc: "Load of 8-bit input by absolute offset into A",
   272  			insns: []Instruction{
   273  				Stmt(Ld|Abs|B, 1), // A = input[1]
   274  				Stmt(Ret|A, 0),    // return A
   275  			},
   276  			input: []byte{0x00, 0x11, 0x22},
   277  			expected: want(ExecutionMetrics{
   278  				Coverage:      []bool{true, true},
   279  				InputAccessed: []bool{false, true, false},
   280  				ReturnValue:   0x11,
   281  			}),
   282  		},
   283  		{
   284  			desc: "Load of 32-bit input by relative offset into A",
   285  			insns: []Instruction{
   286  				Stmt(Ldx|Imm|W, 1), // X = 1
   287  				Stmt(Ld|Ind|W, 1),  // A = input[X+1..X+4]
   288  				Stmt(Ret|A, 0),     // return A
   289  			},
   290  			input: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
   291  			expected: want(ExecutionMetrics{
   292  				Coverage:      []bool{true, true, true},
   293  				InputAccessed: []bool{false, false, true, true, true, true, false},
   294  				ReturnValue:   hostarch.ByteOrder.Uint32([]byte{0x22, 0x33, 0x44, 0x55}),
   295  			}),
   296  		},
   297  		{
   298  			desc: "Load of 16-bit input by relative offset into A",
   299  			insns: []Instruction{
   300  				Stmt(Ldx|Imm|W, 1), // X = 1
   301  				Stmt(Ld|Ind|H, 1),  // A = input[X+1..X+2]
   302  				Stmt(Ret|A, 0),     // return A
   303  			},
   304  			input: []byte{0x00, 0x11, 0x22, 0x33, 0x44},
   305  			expected: want(ExecutionMetrics{
   306  				Coverage:      []bool{true, true, true},
   307  				InputAccessed: []bool{false, false, true, true, false},
   308  				ReturnValue:   uint32(hostarch.ByteOrder.Uint16([]byte{0x22, 0x33})),
   309  			}),
   310  		},
   311  		{
   312  			desc: "Load of 8-bit input by relative offset into A",
   313  			insns: []Instruction{
   314  				Stmt(Ldx|Imm|W, 1), // X = 1
   315  				Stmt(Ld|Ind|B, 1),  // A = input[X+1]
   316  				Stmt(Ret|A, 0),     // return A
   317  			},
   318  			input: []byte{0x00, 0x11, 0x22, 0x33},
   319  			expected: want(ExecutionMetrics{
   320  				Coverage:      []bool{true, true, true},
   321  				InputAccessed: []bool{false, false, true, false},
   322  				ReturnValue:   0x22,
   323  			}),
   324  		},
   325  		{
   326  			desc: "Load/store between A and scratch memory",
   327  			insns: []Instruction{
   328  				Stmt(Ld|Imm|W, 42), // A = 42
   329  				Stmt(St, 2),        // M[2] = A
   330  				Stmt(Ld|Imm|W, 0),  // A = 0
   331  				Stmt(Ld|Mem|W, 2),  // A = M[2]
   332  				Stmt(Ret|A, 0),     // return A
   333  			},
   334  			expected: allCoveredNoneReadAndReturns(42),
   335  		},
   336  		{
   337  			desc: "Load/store between X and scratch memory",
   338  			insns: []Instruction{
   339  				Stmt(Ldx|Imm|W, 42), // X = 42
   340  				Stmt(Stx, 3),        // M[3] = X
   341  				Stmt(Ldx|Imm|W, 0),  // X = 0
   342  				Stmt(Ldx|Mem|W, 3),  // X = M[3]
   343  				Stmt(Misc|Tax, 0),   // A = X
   344  				Stmt(Ret|A, 0),      // return A
   345  			},
   346  			expected: allCoveredNoneReadAndReturns(42),
   347  		},
   348  		{
   349  			desc: "Load of input length into A",
   350  			insns: []Instruction{
   351  				Stmt(Ld|Len|W, 0), // A = len(input)
   352  				Stmt(Ret|A, 0),    // return A
   353  			},
   354  			input:    []byte{1, 2, 3},
   355  			expected: allCoveredNoneReadAndReturns(3),
   356  		},
   357  		{
   358  			desc: "Load of input length into X",
   359  			insns: []Instruction{
   360  				Stmt(Ldx|Len|W, 0), // X = len(input)
   361  				Stmt(Misc|Tax, 0),  // A = X
   362  				Stmt(Ret|A, 0),     // return A
   363  			},
   364  			input:    []byte{1, 2, 3},
   365  			expected: allCoveredNoneReadAndReturns(3),
   366  		},
   367  		{
   368  			desc: "Load of MSH (?) into X",
   369  			insns: []Instruction{
   370  				Stmt(Ldx|Msh|B, 0), // X = 4*(input[0]&0xf)
   371  				Stmt(Misc|Tax, 0),  // A = X
   372  				Stmt(Ret|A, 0),     // return A
   373  			},
   374  			input: []byte{0xf1},
   375  			expected: want(ExecutionMetrics{
   376  				Coverage:      []bool{true, true, true},
   377  				InputAccessed: []bool{true},
   378  				ReturnValue:   4,
   379  			}),
   380  		},
   381  		{
   382  			desc: "Addition of immediate",
   383  			insns: []Instruction{
   384  				Stmt(Ld|Imm|W, 10),  // A = 10
   385  				Stmt(Alu|Add|K, 20), // A += 20
   386  				Stmt(Ret|A, 0),      // return A
   387  			},
   388  			expected: allCoveredNoneReadAndReturns(30),
   389  		},
   390  		{
   391  			desc: "Addition of X",
   392  			insns: []Instruction{
   393  				Stmt(Ld|Imm|W, 10),  // A = 10
   394  				Stmt(Ldx|Imm|W, 20), // X = 20
   395  				Stmt(Alu|Add|X, 0),  // A += X
   396  				Stmt(Ret|A, 0),      // return A
   397  			},
   398  			expected: allCoveredNoneReadAndReturns(30),
   399  		},
   400  		{
   401  			desc: "Subtraction of immediate",
   402  			insns: []Instruction{
   403  				Stmt(Ld|Imm|W, 30),  // A = 30
   404  				Stmt(Alu|Sub|K, 20), // A -= 20
   405  				Stmt(Ret|A, 0),      // return A
   406  			},
   407  			expected: allCoveredNoneReadAndReturns(10),
   408  		},
   409  		{
   410  			desc: "Subtraction of X",
   411  			insns: []Instruction{
   412  				Stmt(Ld|Imm|W, 30),  // A = 30
   413  				Stmt(Ldx|Imm|W, 20), // X = 20
   414  				Stmt(Alu|Sub|X, 0),  // A -= X
   415  				Stmt(Ret|A, 0),      // return A
   416  			},
   417  			expected: allCoveredNoneReadAndReturns(10),
   418  		},
   419  		{
   420  			desc: "Multiplication of immediate",
   421  			insns: []Instruction{
   422  				Stmt(Ld|Imm|W, 2),  // A = 2
   423  				Stmt(Alu|Mul|K, 3), // A *= 3
   424  				Stmt(Ret|A, 0),     // return A
   425  			},
   426  			expected: allCoveredNoneReadAndReturns(6),
   427  		},
   428  		{
   429  			desc: "Multiplication of X",
   430  			insns: []Instruction{
   431  				Stmt(Ld|Imm|W, 2),  // A = 2
   432  				Stmt(Ldx|Imm|W, 3), // X = 3
   433  				Stmt(Alu|Mul|X, 0), // A *= X
   434  				Stmt(Ret|A, 0),     // return A
   435  			},
   436  			expected: allCoveredNoneReadAndReturns(6),
   437  		},
   438  		{
   439  			desc: "Division by immediate",
   440  			insns: []Instruction{
   441  				Stmt(Ld|Imm|W, 6),  // A = 6
   442  				Stmt(Alu|Div|K, 3), // A /= 3
   443  				Stmt(Ret|A, 0),     // return A
   444  			},
   445  			expected: allCoveredNoneReadAndReturns(2),
   446  		},
   447  		{
   448  			desc: "Division by X",
   449  			insns: []Instruction{
   450  				Stmt(Ld|Imm|W, 6),  // A = 6
   451  				Stmt(Ldx|Imm|W, 3), // X = 3
   452  				Stmt(Alu|Div|X, 0), // A /= X
   453  				Stmt(Ret|A, 0),     // return A
   454  			},
   455  			expected: allCoveredNoneReadAndReturns(2),
   456  		},
   457  		{
   458  			desc: "Modulo immediate",
   459  			insns: []Instruction{
   460  				Stmt(Ld|Imm|W, 17), // A = 17
   461  				Stmt(Alu|Mod|K, 7), // A %= 7
   462  				Stmt(Ret|A, 0),     // return A
   463  			},
   464  			expected: allCoveredNoneReadAndReturns(3),
   465  		},
   466  		{
   467  			desc: "Modulo X",
   468  			insns: []Instruction{
   469  				Stmt(Ld|Imm|W, 17), // A = 17
   470  				Stmt(Ldx|Imm|W, 7), // X = 7
   471  				Stmt(Alu|Mod|X, 0), // A %= X
   472  				Stmt(Ret|A, 0),     // return A
   473  			},
   474  			expected: allCoveredNoneReadAndReturns(3),
   475  		},
   476  		{
   477  			desc: "Arithmetic negation",
   478  			insns: []Instruction{
   479  				Stmt(Ld|Imm|W, 1), // A = 1
   480  				Stmt(Alu|Neg, 0),  // A = -A
   481  				Stmt(Ret|A, 0),    // return A
   482  			},
   483  			expected: allCoveredNoneReadAndReturns(0xffffffff),
   484  		},
   485  		{
   486  			desc: "Bitwise OR with immediate",
   487  			insns: []Instruction{
   488  				Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
   489  				Stmt(Alu|Or|K, 0xff0055aa), // A |= 0xff0055aa
   490  				Stmt(Ret|A, 0),             // return A
   491  			},
   492  			expected: allCoveredNoneReadAndReturns(0xff00ffff),
   493  		},
   494  		{
   495  			desc: "Bitwise OR with X",
   496  			insns: []Instruction{
   497  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   498  				Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
   499  				Stmt(Alu|Or|X, 0),           // A |= X
   500  				Stmt(Ret|A, 0),              // return A
   501  			},
   502  			expected: allCoveredNoneReadAndReturns(0xff00ffff),
   503  		},
   504  		{
   505  			desc: "Bitwise AND with immediate",
   506  			insns: []Instruction{
   507  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   508  				Stmt(Alu|And|K, 0xff0055aa), // A &= 0xff0055aa
   509  				Stmt(Ret|A, 0),              // return A
   510  			},
   511  			expected: allCoveredNoneReadAndReturns(0xff000000),
   512  		},
   513  		{
   514  			desc: "Bitwise AND with X",
   515  			insns: []Instruction{
   516  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   517  				Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
   518  				Stmt(Alu|And|X, 0),          // A &= X
   519  				Stmt(Ret|A, 0),              // return A
   520  			},
   521  			expected: allCoveredNoneReadAndReturns(0xff000000),
   522  		},
   523  		{
   524  			desc: "Bitwise XOR with immediate",
   525  			insns: []Instruction{
   526  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   527  				Stmt(Alu|Xor|K, 0xff0055aa), // A ^= 0xff0055aa
   528  				Stmt(Ret|A, 0),              // return A
   529  			},
   530  			expected: allCoveredNoneReadAndReturns(0x0000ffff),
   531  		},
   532  		{
   533  			desc: "Bitwise XOR with X",
   534  			insns: []Instruction{
   535  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   536  				Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
   537  				Stmt(Alu|Xor|X, 0),          // A ^= X
   538  				Stmt(Ret|A, 0),              // return A
   539  			},
   540  			expected: allCoveredNoneReadAndReturns(0x0000ffff),
   541  		},
   542  		{
   543  			desc: "Left shift by immediate",
   544  			insns: []Instruction{
   545  				Stmt(Ld|Imm|W, 1),  // A = 1
   546  				Stmt(Alu|Lsh|K, 5), // A <<= 5
   547  				Stmt(Ret|A, 0),     // return A
   548  			},
   549  			expected: allCoveredNoneReadAndReturns(32),
   550  		},
   551  		{
   552  			desc: "Left shift by X",
   553  			insns: []Instruction{
   554  				Stmt(Ld|Imm|W, 1),  // A = 1
   555  				Stmt(Ldx|Imm|W, 5), // X = 5
   556  				Stmt(Alu|Lsh|X, 0), // A <<= X
   557  				Stmt(Ret|A, 0),     // return A
   558  			},
   559  			expected: allCoveredNoneReadAndReturns(32),
   560  		},
   561  		{
   562  			desc: "Right shift by immediate",
   563  			insns: []Instruction{
   564  				Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff
   565  				Stmt(Alu|Rsh|K, 31),        // A >>= 31
   566  				Stmt(Ret|A, 0),             // return A
   567  			},
   568  			expected: allCoveredNoneReadAndReturns(1),
   569  		},
   570  		{
   571  			desc: "Right shift by X",
   572  			insns: []Instruction{
   573  				Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff
   574  				Stmt(Ldx|Imm|W, 31),        // X = 31
   575  				Stmt(Alu|Rsh|X, 0),         // A >>= X
   576  				Stmt(Ret|A, 0),             // return A
   577  			},
   578  			expected: allCoveredNoneReadAndReturns(1),
   579  		},
   580  		{
   581  			desc: "Unconditional jump",
   582  			insns: []Instruction{
   583  				Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1
   584  				Stmt(Ret|K, 0),        // return 0
   585  				Stmt(Ret|K, 1),        // return 1
   586  			},
   587  			expected: want(ExecutionMetrics{
   588  				Coverage:      []bool{true, false, true},
   589  				InputAccessed: []bool{},
   590  				ReturnValue:   1,
   591  			}),
   592  		},
   593  		{
   594  			desc: "Jump when A == immediate",
   595  			insns: []Instruction{
   596  				Stmt(Ld|Imm|W, 42),        // A = 42
   597  				Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2
   598  				Stmt(Ret|K, 0),            // return 0
   599  				Stmt(Ret|K, 1),            // return 1
   600  				Stmt(Ret|K, 2),            // return 2
   601  			},
   602  			expected: want(ExecutionMetrics{
   603  				Coverage:      []bool{true, true, false, true, false},
   604  				InputAccessed: []bool{},
   605  				ReturnValue:   1,
   606  			}),
   607  		},
   608  		{
   609  			desc: "Jump when A != immediate",
   610  			insns: []Instruction{
   611  				Stmt(Ld|Imm|W, 41),        // A = 41
   612  				Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2
   613  				Stmt(Ret|K, 0),            // return 0
   614  				Stmt(Ret|K, 1),            // return 1
   615  				Stmt(Ret|K, 2),            // return 2
   616  			},
   617  			expected: want(ExecutionMetrics{
   618  				Coverage:      []bool{true, true, false, false, true},
   619  				InputAccessed: []bool{},
   620  				ReturnValue:   2,
   621  			}),
   622  		},
   623  		{
   624  			desc: "Jump when A == X",
   625  			insns: []Instruction{
   626  				Stmt(Ld|Imm|W, 42),       // A = 42
   627  				Stmt(Ldx|Imm|W, 42),      // X = 42
   628  				Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2
   629  				Stmt(Ret|K, 0),           // return 0
   630  				Stmt(Ret|K, 1),           // return 1
   631  				Stmt(Ret|K, 2),           // return 2
   632  			},
   633  			expected: want(ExecutionMetrics{
   634  				Coverage:      []bool{true, true, true, false, true, false},
   635  				InputAccessed: []bool{},
   636  				ReturnValue:   1,
   637  			}),
   638  		},
   639  		{
   640  			desc: "Jump when A != X",
   641  			insns: []Instruction{
   642  				Stmt(Ld|Imm|W, 42),       // A = 42
   643  				Stmt(Ldx|Imm|W, 41),      // X = 41
   644  				Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2
   645  				Stmt(Ret|K, 0),           // return 0
   646  				Stmt(Ret|K, 1),           // return 1
   647  				Stmt(Ret|K, 2),           // return 2
   648  			},
   649  			expected: want(ExecutionMetrics{
   650  				Coverage:      []bool{true, true, true, false, false, true},
   651  				InputAccessed: []bool{},
   652  				ReturnValue:   2,
   653  			}),
   654  		},
   655  		{
   656  			desc: "Jump when A > immediate",
   657  			insns: []Instruction{
   658  				Stmt(Ld|Imm|W, 10),       // A = 10
   659  				Jump(Jmp|Jgt|K, 9, 1, 2), // if (A > 9) jmp nextpc+1 else jmp nextpc+2
   660  				Stmt(Ret|K, 0),           // return 0
   661  				Stmt(Ret|K, 1),           // return 1
   662  				Stmt(Ret|K, 2),           // return 2
   663  			},
   664  			expected: want(ExecutionMetrics{
   665  				Coverage:      []bool{true, true, false, true, false},
   666  				InputAccessed: []bool{},
   667  				ReturnValue:   1,
   668  			}),
   669  		},
   670  		{
   671  			desc: "Jump when A <= immediate",
   672  			insns: []Instruction{
   673  				Stmt(Ld|Imm|W, 10),        // A = 10
   674  				Jump(Jmp|Jgt|K, 10, 1, 2), // if (A > 10) jmp nextpc+1 else jmp nextpc+2
   675  				Stmt(Ret|K, 0),            // return 0
   676  				Stmt(Ret|K, 1),            // return 1
   677  				Stmt(Ret|K, 2),            // return 2
   678  			},
   679  			expected: want(ExecutionMetrics{
   680  				Coverage:      []bool{true, true, false, false, true},
   681  				InputAccessed: []bool{},
   682  				ReturnValue:   2,
   683  			}),
   684  		},
   685  		{
   686  			desc: "Jump when A > X",
   687  			insns: []Instruction{
   688  				Stmt(Ld|Imm|W, 10),       // A = 10
   689  				Stmt(Ldx|Imm|W, 9),       // X = 9
   690  				Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) jmp nextpc+1 else jmp nextpc+2
   691  				Stmt(Ret|K, 0),           // return 0
   692  				Stmt(Ret|K, 1),           // return 1
   693  				Stmt(Ret|K, 2),           // return 2
   694  			},
   695  			expected: want(ExecutionMetrics{
   696  				Coverage:      []bool{true, true, true, false, true, false},
   697  				InputAccessed: []bool{},
   698  				ReturnValue:   1,
   699  			}),
   700  		},
   701  		{
   702  			desc: "Jump when A <= X",
   703  			insns: []Instruction{
   704  				Stmt(Ld|Imm|W, 10),       // A = 10
   705  				Stmt(Ldx|Imm|W, 10),      // X = 10
   706  				Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) jmp nextpc+1 else jmp nextpc+2
   707  				Stmt(Ret|K, 0),           // return 0
   708  				Stmt(Ret|K, 1),           // return 1
   709  				Stmt(Ret|K, 2),           // return 2
   710  			},
   711  			expected: want(ExecutionMetrics{
   712  				Coverage:      []bool{true, true, true, false, false, true},
   713  				InputAccessed: []bool{},
   714  				ReturnValue:   2,
   715  			}),
   716  		},
   717  		{
   718  			desc: "Jump when A >= immediate",
   719  			insns: []Instruction{
   720  				Stmt(Ld|Imm|W, 10),        // A = 10
   721  				Jump(Jmp|Jge|K, 10, 1, 2), // if (A >= 10) jmp nextpc+1 else jmp nextpc+2
   722  				Stmt(Ret|K, 0),            // return 0
   723  				Stmt(Ret|K, 1),            // return 1
   724  				Stmt(Ret|K, 2),            // return 2
   725  			},
   726  			expected: want(ExecutionMetrics{
   727  				Coverage:      []bool{true, true, false, true, false},
   728  				InputAccessed: []bool{},
   729  				ReturnValue:   1,
   730  			}),
   731  		},
   732  		{
   733  			desc: "Jump when A < immediate",
   734  			insns: []Instruction{
   735  				Stmt(Ld|Imm|W, 10),        // A = 10
   736  				Jump(Jmp|Jge|K, 11, 1, 2), // if (A >= 11) jmp nextpc+1 else jmp nextpc+2
   737  				Stmt(Ret|K, 0),            // return 0
   738  				Stmt(Ret|K, 1),            // return 1
   739  				Stmt(Ret|K, 2),            // return 2
   740  			},
   741  			expected: want(ExecutionMetrics{
   742  				Coverage:      []bool{true, true, false, false, true},
   743  				InputAccessed: []bool{},
   744  				ReturnValue:   2,
   745  			}),
   746  		},
   747  		{
   748  			desc: "Jump when A >= X",
   749  			insns: []Instruction{
   750  				Stmt(Ld|Imm|W, 10),       // A = 10
   751  				Stmt(Ldx|Imm|W, 10),      // X = 10
   752  				Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2
   753  				Stmt(Ret|K, 0),           // return 0
   754  				Stmt(Ret|K, 1),           // return 1
   755  				Stmt(Ret|K, 2),           // return 2
   756  			},
   757  			expected: want(ExecutionMetrics{
   758  				Coverage:      []bool{true, true, true, false, true, false},
   759  				InputAccessed: []bool{},
   760  				ReturnValue:   1,
   761  			}),
   762  		},
   763  		{
   764  			desc: "Jump when A < X",
   765  			insns: []Instruction{
   766  				Stmt(Ld|Imm|W, 10),       // A = 10
   767  				Stmt(Ldx|Imm|W, 11),      // X = 11
   768  				Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2
   769  				Stmt(Ret|K, 0),           // return 0
   770  				Stmt(Ret|K, 1),           // return 1
   771  				Stmt(Ret|K, 2),           // return 2
   772  			},
   773  			expected: want(ExecutionMetrics{
   774  				Coverage:      []bool{true, true, true, false, false, true},
   775  				InputAccessed: []bool{},
   776  				ReturnValue:   2,
   777  			}),
   778  		},
   779  		{
   780  			desc: "Jump when A & immediate != 0",
   781  			insns: []Instruction{
   782  				Stmt(Ld|Imm|W, 0xff),          // A = 0xff
   783  				Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2
   784  				Stmt(Ret|K, 0),                // return 0
   785  				Stmt(Ret|K, 1),                // return 1
   786  				Stmt(Ret|K, 2),                // return 2
   787  			},
   788  			expected: want(ExecutionMetrics{
   789  				Coverage:      []bool{true, true, false, true, false},
   790  				InputAccessed: []bool{},
   791  				ReturnValue:   1,
   792  			}),
   793  		},
   794  		{
   795  			desc: "Jump when A & immediate == 0",
   796  			insns: []Instruction{
   797  				Stmt(Ld|Imm|W, 0xfe),          // A = 0xfe
   798  				Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2
   799  				Stmt(Ret|K, 0),                // return 0
   800  				Stmt(Ret|K, 1),                // return 1
   801  				Stmt(Ret|K, 2),                // return 2
   802  			},
   803  			expected: want(ExecutionMetrics{
   804  				Coverage:      []bool{true, true, false, false, true},
   805  				InputAccessed: []bool{},
   806  				ReturnValue:   2,
   807  			}),
   808  		},
   809  		{
   810  			desc: "Jump when A & X != 0",
   811  			insns: []Instruction{
   812  				Stmt(Ld|Imm|W, 0xff),      // A = 0xff
   813  				Stmt(Ldx|Imm|W, 0x101),    // X = 0x101
   814  				Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2
   815  				Stmt(Ret|K, 0),            // return 0
   816  				Stmt(Ret|K, 1),            // return 1
   817  				Stmt(Ret|K, 2),            // return 2
   818  			},
   819  			expected: want(ExecutionMetrics{
   820  				Coverage:      []bool{true, true, true, false, true, false},
   821  				InputAccessed: []bool{},
   822  				ReturnValue:   1,
   823  			}),
   824  		},
   825  		{
   826  			desc: "Jump when A & X == 0",
   827  			insns: []Instruction{
   828  				Stmt(Ld|Imm|W, 0xfe),      // A = 0xfe
   829  				Stmt(Ldx|Imm|W, 0x101),    // X = 0x101
   830  				Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2
   831  				Stmt(Ret|K, 0),            // return 0
   832  				Stmt(Ret|K, 1),            // return 1
   833  				Stmt(Ret|K, 2),            // return 2
   834  			},
   835  			expected: want(ExecutionMetrics{
   836  				Coverage:      []bool{true, true, true, false, false, true},
   837  				InputAccessed: []bool{},
   838  				ReturnValue:   2,
   839  			}),
   840  		},
   841  		{
   842  			desc: "Optimizable program",
   843  			insns: []Instruction{
   844  				Stmt(Ld|Imm|W, 42),        // A = 42
   845  				Jump(Jmp|Jeq|K, 42, 0, 1), // if (A == 42) jmp 0 else 1
   846  				Jump(Jmp|Ja, 1, 0, 0),     // jmp 1
   847  				Jump(Jmp|Ja, 2, 0, 0),     // jmp 2
   848  				Stmt(Ld|Imm|W, 37),        // A = 37
   849  				Stmt(Ret|K, 0),            // return 0
   850  				Stmt(Ret|K, 1),            // return 1
   851  			},
   852  			expected: want(ExecutionMetrics{
   853  				Coverage:      []bool{true, true, true, false, true, true, false},
   854  				InputAccessed: []bool{},
   855  				ReturnValue:   0,
   856  			}),
   857  		},
   858  	} {
   859  		t.Run(test.desc, func(t *testing.T) {
   860  			p, err := Compile(test.insns, false)
   861  			if err != nil {
   862  				t.Fatalf("unexpected compilation error: %v", err)
   863  			}
   864  			want := test.expected(test.insns, test.input)
   865  			execution, err := InstrumentedExec[NativeEndian](p, test.input)
   866  			if err != nil {
   867  				t.Fatalf("unexpected execution error: %v", err)
   868  			}
   869  			if !reflect.DeepEqual(execution, want) {
   870  				t.Fatalf("expected %s, got %s", want.String(), execution.String())
   871  			}
   872  			retFast, err := Exec[NativeEndian](p, test.input)
   873  			if err != nil {
   874  				t.Fatalf("unexpected execution error during fast execution: %v", err)
   875  			}
   876  			if retFast != execution.ReturnValue {
   877  				t.Fatalf("instrumented execution returned %d, fast execution returned %d", execution.ReturnValue, retFast)
   878  			}
   879  			optimizedProgram, err := Compile(test.insns, true)
   880  			if err != nil {
   881  				t.Fatalf("unexpected compilation error: %v", err)
   882  			}
   883  			retOptimized, err := InstrumentedExec[NativeEndian](optimizedProgram, test.input)
   884  			if err != nil {
   885  				t.Fatalf("unexpected execution error: %v", err)
   886  			}
   887  			if retOptimized.ReturnValue != retFast {
   888  				t.Fatalf("expected return value from optimized version: got %d, non-optimized execution returned %d", retOptimized.ReturnValue, retFast)
   889  			}
   890  			if !slices.Equal(retOptimized.InputAccessed, execution.InputAccessed) {
   891  				t.Fatalf("expected input read coverage from optimized version: got %s, non-optimized execution was %s", retOptimized.String(), execution.String())
   892  			}
   893  		})
   894  	}
   895  }
   896  
   897  // Seccomp filter example given in Linux's
   898  // Documentation/networking/filter.txt, translated to bytecode using the
   899  // Linux kernel tree's tools/net/bpf_asm.
   900  var sampleFilter = []Instruction{
   901  	{0x20, 0, 0, 0x00000004},  // ld [4]                  /* offsetof(struct seccomp_data, arch) */
   902  	{0x15, 0, 11, 0xc000003e}, // jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
   903  	{0x20, 0, 0, 0000000000},  // ld [0]                  /* offsetof(struct seccomp_data, nr) */
   904  	{0x15, 10, 0, 0x0000000f}, // jeq #15, good           /* __NR_rt_sigreturn */
   905  	{0x15, 9, 0, 0x000000e7},  // jeq #231, good          /* __NR_exit_group */
   906  	{0x15, 8, 0, 0x0000003c},  // jeq #60, good           /* __NR_exit */
   907  	{0x15, 7, 0, 0000000000},  // jeq #0, good            /* __NR_read */
   908  	{0x15, 6, 0, 0x00000001},  // jeq #1, good            /* __NR_write */
   909  	{0x15, 5, 0, 0x00000005},  // jeq #5, good            /* __NR_fstat */
   910  	{0x15, 4, 0, 0x00000009},  // jeq #9, good            /* __NR_mmap */
   911  	{0x15, 3, 0, 0x0000000e},  // jeq #14, good           /* __NR_rt_sigprocmask */
   912  	{0x15, 2, 0, 0x0000000d},  // jeq #13, good           /* __NR_rt_sigaction */
   913  	{0x15, 1, 0, 0x00000023},  // jeq #35, good           /* __NR_nanosleep */
   914  	{0x06, 0, 0, 0000000000},  // bad: ret #0             /* SECCOMP_RET_KILL */
   915  	{0x06, 0, 0, 0x7fff0000},  // good: ret #0x7fff0000   /* SECCOMP_RET_ALLOW */
   916  }
   917  
   918  func TestSimpleFilter(t *testing.T) {
   919  	p, err := Compile(sampleFilter, false)
   920  	if err != nil {
   921  		t.Fatalf("Unexpected compilation error: %v", err)
   922  	}
   923  
   924  	// linux.SeccompData is 64 bytes long.
   925  	// The first 4 bytes is the syscall number.
   926  	// The next 4 bytes is the architecture.
   927  	// The last 56 bytes are the instruction pointer and the syscall arguments,
   928  	// which this sample program never accesses.
   929  	noRIPOrSyscallArgsAccess := make([]bool, 56)
   930  
   931  	for _, test := range []struct {
   932  		// desc is the test's description.
   933  		desc string
   934  
   935  		// SeccompData is the input data.
   936  		data linux.SeccompData
   937  
   938  		// expected is the expected execution result of the BPF program.
   939  		expected ExecutionMetrics
   940  	}{
   941  		{
   942  			desc: "Invalid arch is rejected",
   943  			data: linux.SeccompData{Nr: 1 /* x86 exit */, Arch: 0x40000003 /* AUDIT_ARCH_I386 */},
   944  			expected: ExecutionMetrics{
   945  				ReturnValue: 0,
   946  				Coverage: []bool{
   947  					true,  // ld [4]                  /* offsetof(struct seccomp_data, arch) */
   948  					true,  // jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
   949  					false, // ld [0]                  /* offsetof(struct seccomp_data, nr) */
   950  					false, // jeq #15, good           /* __NR_rt_sigreturn */
   951  					false, // jeq #231, good          /* __NR_exit_group */
   952  					false, // jeq #60, good           /* __NR_exit */
   953  					false, // jeq #0, good            /* __NR_read */
   954  					false, // jeq #1, good            /* __NR_write */
   955  					false, // jeq #5, good            /* __NR_fstat */
   956  					false, // jeq #9, good            /* __NR_mmap */
   957  					false, // jeq #14, good           /* __NR_rt_sigprocmask */
   958  					false, // jeq #13, good           /* __NR_rt_sigaction */
   959  					false, // jeq #35, good           /* __NR_nanosleep */
   960  					true,  // bad: ret #0             /* SECCOMP_RET_KILL */
   961  					false, // good: ret #0x7fff0000   /* SECCOMP_RET_ALLOW */
   962  				},
   963  				InputAccessed: append(
   964  					[]bool{
   965  						false, false, false, false, // Syscall number
   966  						true, true, true, true, // Architecture
   967  					},
   968  					noRIPOrSyscallArgsAccess...),
   969  			},
   970  		},
   971  		{
   972  			desc: "Disallowed syscall is rejected",
   973  			data: linux.SeccompData{Nr: 105 /* __NR_setuid */, Arch: 0xc000003e},
   974  			expected: ExecutionMetrics{
   975  				ReturnValue: 0,
   976  				Coverage: []bool{
   977  					true,  // ld [4]                  /* offsetof(struct seccomp_data, arch) */
   978  					true,  // jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
   979  					true,  // ld [0]                  /* offsetof(struct seccomp_data, nr) */
   980  					true,  // jeq #15, good           /* __NR_rt_sigreturn */
   981  					true,  // jeq #231, good          /* __NR_exit_group */
   982  					true,  // jeq #60, good           /* __NR_exit */
   983  					true,  // jeq #0, good            /* __NR_read */
   984  					true,  // jeq #1, good            /* __NR_write */
   985  					true,  // jeq #5, good            /* __NR_fstat */
   986  					true,  // jeq #9, good            /* __NR_mmap */
   987  					true,  // jeq #14, good           /* __NR_rt_sigprocmask */
   988  					true,  // jeq #13, good           /* __NR_rt_sigaction */
   989  					true,  // jeq #35, good           /* __NR_nanosleep */
   990  					true,  // bad: ret #0             /* SECCOMP_RET_KILL */
   991  					false, // good: ret #0x7fff0000   /* SECCOMP_RET_ALLOW */
   992  				},
   993  				InputAccessed: append(
   994  					[]bool{
   995  						true, true, true, true, // Syscall number
   996  						true, true, true, true, // Architecture
   997  					},
   998  					noRIPOrSyscallArgsAccess...),
   999  			},
  1000  		},
  1001  		{
  1002  			desc: "Allowed syscall is indeed allowed",
  1003  			data: linux.SeccompData{Nr: 231 /* __NR_exit_group */, Arch: 0xc000003e},
  1004  			expected: ExecutionMetrics{
  1005  				ReturnValue: 0x7fff0000, /* SECCOMP_RET_ALLOW */
  1006  				Coverage: []bool{
  1007  					true,  // ld [4]                  /* offsetof(struct seccomp_data, arch) */
  1008  					true,  // jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
  1009  					true,  // ld [0]                  /* offsetof(struct seccomp_data, nr) */
  1010  					true,  // jeq #15, good           /* __NR_rt_sigreturn */
  1011  					true,  // jeq #231, good          /* __NR_exit_group */
  1012  					false, // jeq #60, good           /* __NR_exit */
  1013  					false, // jeq #0, good            /* __NR_read */
  1014  					false, // jeq #1, good            /* __NR_write */
  1015  					false, // jeq #5, good            /* __NR_fstat */
  1016  					false, // jeq #9, good            /* __NR_mmap */
  1017  					false, // jeq #14, good           /* __NR_rt_sigprocmask */
  1018  					false, // jeq #13, good           /* __NR_rt_sigaction */
  1019  					false, // jeq #35, good           /* __NR_nanosleep */
  1020  					false, // bad: ret #0             /* SECCOMP_RET_KILL */
  1021  					true,  // good: ret #0x7fff0000   /* SECCOMP_RET_ALLOW */
  1022  				},
  1023  				InputAccessed: append(
  1024  					[]bool{
  1025  						true, true, true, true, // Syscall number
  1026  						true, true, true, true, // Architecture
  1027  					},
  1028  					noRIPOrSyscallArgsAccess...),
  1029  			},
  1030  		},
  1031  	} {
  1032  		t.Run(test.desc, func(t *testing.T) {
  1033  			execution, err := InstrumentedExec[NativeEndian](p, dataAsInput(&test.data))
  1034  			if err != nil {
  1035  				t.Fatalf("expected return value of %d, got execution error: %v", test.expected.ReturnValue, err)
  1036  			}
  1037  			if !reflect.DeepEqual(execution, test.expected) {
  1038  				t.Errorf("expected %s, got %s", test.expected.String(), execution.String())
  1039  			}
  1040  		})
  1041  	}
  1042  }
  1043  
  1044  // asInput converts a seccompData to a bpf.Input.
  1045  func dataAsInput(data *linux.SeccompData) Input {
  1046  	return marshal.Marshal(data)
  1047  }
  1048  
  1049  // BenchmarkInterpreter benchmarks the execution of the sample filter
  1050  // for a sample syscall.
  1051  func BenchmarkInterpreter(b *testing.B) {
  1052  	p, err := Compile(sampleFilter, true)
  1053  	if err != nil {
  1054  		b.Fatalf("Unexpected compilation error: %v", err)
  1055  	}
  1056  	data := dataAsInput(&linux.SeccompData{Nr: 231 /* __NR_exit_group */, Arch: 0xc000003e})
  1057  	b.ResetTimer()
  1058  	for i := 0; i < b.N; i++ {
  1059  		if _, err := Exec[NativeEndian](p, data); err != nil {
  1060  			b.Fatalf("Unexpected execution error: %v", err)
  1061  		}
  1062  	}
  1063  }
  1064  
  1065  func BenchmarkInstrumentedInterpreter(b *testing.B) {
  1066  	p, err := Compile(sampleFilter, true)
  1067  	if err != nil {
  1068  		b.Fatalf("Unexpected compilation error: %v", err)
  1069  	}
  1070  	data := dataAsInput(&linux.SeccompData{Nr: 231 /* __NR_exit_group */, Arch: 0xc000003e})
  1071  	b.ResetTimer()
  1072  	for i := 0; i < b.N; i++ {
  1073  		if _, err := InstrumentedExec[NativeEndian](p, data); err != nil {
  1074  			b.Fatalf("Unexpected execution error: %v", err)
  1075  		}
  1076  	}
  1077  }