github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/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  	"encoding/binary"
    19  	"testing"
    20  
    21  	"github.com/SagerNet/gvisor/pkg/abi/linux"
    22  	"github.com/SagerNet/gvisor/pkg/hostarch"
    23  	"github.com/SagerNet/gvisor/pkg/marshal"
    24  )
    25  
    26  func TestCompilationErrors(t *testing.T) {
    27  	for _, test := range []struct {
    28  		// desc is the test's description.
    29  		desc string
    30  
    31  		// insns is the BPF instructions to be compiled.
    32  		insns []linux.BPFInstruction
    33  
    34  		// expectedErr is the expected compilation error.
    35  		expectedErr error
    36  	}{
    37  		{
    38  			desc:        "Instructions must not be nil",
    39  			expectedErr: Error{InvalidInstructionCount, 0},
    40  		},
    41  		{
    42  			desc:        "Instructions must not be empty",
    43  			insns:       []linux.BPFInstruction{},
    44  			expectedErr: Error{InvalidInstructionCount, 0},
    45  		},
    46  		{
    47  			desc:        "A program must end with a return",
    48  			insns:       make([]linux.BPFInstruction, MaxInstructions),
    49  			expectedErr: Error{InvalidEndOfProgram, MaxInstructions - 1},
    50  		},
    51  		{
    52  			desc:        "A program must have MaxInstructions or fewer instructions",
    53  			insns:       append(make([]linux.BPFInstruction, MaxInstructions), Stmt(Ret|K, 0)),
    54  			expectedErr: Error{InvalidInstructionCount, MaxInstructions + 1},
    55  		},
    56  		{
    57  			desc: "A load from an invalid M register is a compilation error",
    58  			insns: []linux.BPFInstruction{
    59  				Stmt(Ld|Mem|W, ScratchMemRegisters), // A = M[16]
    60  				Stmt(Ret|K, 0),                      // return 0
    61  			},
    62  			expectedErr: Error{InvalidRegister, 0},
    63  		},
    64  		{
    65  			desc: "A store to an invalid M register is a compilation error",
    66  			insns: []linux.BPFInstruction{
    67  				Stmt(St, ScratchMemRegisters), // M[16] = A
    68  				Stmt(Ret|K, 0),                // return 0
    69  			},
    70  			expectedErr: Error{InvalidRegister, 0},
    71  		},
    72  		{
    73  			desc: "Division by literal zero is a compilation error",
    74  			insns: []linux.BPFInstruction{
    75  				Stmt(Alu|Div|K, 0), // A /= 0
    76  				Stmt(Ret|K, 0),     // return 0
    77  			},
    78  			expectedErr: Error{DivisionByZero, 0},
    79  		},
    80  		{
    81  			desc: "An unconditional jump outside of the program is a compilation error",
    82  			insns: []linux.BPFInstruction{
    83  				Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1
    84  				Stmt(Ret|K, 0),        // return 0
    85  			},
    86  			expectedErr: Error{InvalidJumpTarget, 0},
    87  		},
    88  		{
    89  			desc: "A conditional jump outside of the program in the true case is a compilation error",
    90  			insns: []linux.BPFInstruction{
    91  				Jump(Jmp|Jeq|K, 0, 1, 0), // if (A == K) jmp nextpc+1
    92  				Stmt(Ret|K, 0),           // return 0
    93  			},
    94  			expectedErr: Error{InvalidJumpTarget, 0},
    95  		},
    96  		{
    97  			desc: "A conditional jump outside of the program in the false case is a compilation error",
    98  			insns: []linux.BPFInstruction{
    99  				Jump(Jmp|Jeq|K, 0, 0, 1), // if (A != K) jmp nextpc+1
   100  				Stmt(Ret|K, 0),           // return 0
   101  			},
   102  			expectedErr: Error{InvalidJumpTarget, 0},
   103  		},
   104  	} {
   105  		_, err := Compile(test.insns)
   106  		if err != test.expectedErr {
   107  			t.Errorf("%s: expected error %q, got error %q", test.desc, test.expectedErr, err)
   108  		}
   109  	}
   110  }
   111  
   112  func TestExecErrors(t *testing.T) {
   113  	for _, test := range []struct {
   114  		// desc is the test's description.
   115  		desc string
   116  
   117  		// insns is the BPF instructions to be executed.
   118  		insns []linux.BPFInstruction
   119  
   120  		// expectedErr is the expected execution error.
   121  		expectedErr error
   122  	}{
   123  		{
   124  			desc: "An out-of-bounds load of input data is an execution error",
   125  			insns: []linux.BPFInstruction{
   126  				Stmt(Ld|Abs|B, 0), // A = input[0]
   127  				Stmt(Ret|K, 0),    // return 0
   128  			},
   129  			expectedErr: Error{InvalidLoad, 0},
   130  		},
   131  		{
   132  			desc: "Division by zero at runtime is an execution error",
   133  			insns: []linux.BPFInstruction{
   134  				Stmt(Alu|Div|X, 0), // A /= X
   135  				Stmt(Ret|K, 0),     // return 0
   136  			},
   137  			expectedErr: Error{DivisionByZero, 0},
   138  		},
   139  		{
   140  			desc: "Modulo zero at runtime is an execution error",
   141  			insns: []linux.BPFInstruction{
   142  				Stmt(Alu|Mod|X, 0), // A %= X
   143  				Stmt(Ret|K, 0),     // return 0
   144  			},
   145  			expectedErr: Error{DivisionByZero, 0},
   146  		},
   147  	} {
   148  		p, err := Compile(test.insns)
   149  		if err != nil {
   150  			t.Errorf("%s: unexpected compilation error: %v", test.desc, err)
   151  			continue
   152  		}
   153  		ret, err := Exec(p, InputBytes{nil, binary.BigEndian})
   154  		if err != test.expectedErr {
   155  			t.Errorf("%s: expected execution error %q, got (%d, %v)", test.desc, test.expectedErr, ret, err)
   156  		}
   157  	}
   158  }
   159  
   160  func TestValidInstructions(t *testing.T) {
   161  	for _, test := range []struct {
   162  		// desc is the test's description.
   163  		desc string
   164  
   165  		// insns is the BPF instructions to be compiled.
   166  		insns []linux.BPFInstruction
   167  
   168  		// input is the input data. Note that input will be read as big-endian.
   169  		input []byte
   170  
   171  		// expectedRet is the expected return value of the BPF program.
   172  		expectedRet uint32
   173  	}{
   174  		{
   175  			desc: "Return of immediate",
   176  			insns: []linux.BPFInstruction{
   177  				Stmt(Ret|K, 42), // return 42
   178  			},
   179  			expectedRet: 42,
   180  		},
   181  		{
   182  			desc: "Load of immediate into A",
   183  			insns: []linux.BPFInstruction{
   184  				Stmt(Ld|Imm|W, 42), // A = 42
   185  				Stmt(Ret|A, 0),     // return A
   186  			},
   187  			expectedRet: 42,
   188  		},
   189  		{
   190  			desc: "Load of immediate into X and copying of X into A",
   191  			insns: []linux.BPFInstruction{
   192  				Stmt(Ldx|Imm|W, 42), // X = 42
   193  				Stmt(Misc|Tax, 0),   // A = X
   194  				Stmt(Ret|A, 0),      // return A
   195  			},
   196  			expectedRet: 42,
   197  		},
   198  		{
   199  			desc: "Copying of A into X and back",
   200  			insns: []linux.BPFInstruction{
   201  				Stmt(Ld|Imm|W, 42), // A = 42
   202  				Stmt(Misc|Txa, 0),  // X = A
   203  				Stmt(Ld|Imm|W, 0),  // A = 0
   204  				Stmt(Misc|Tax, 0),  // A = X
   205  				Stmt(Ret|A, 0),     // return A
   206  			},
   207  			expectedRet: 42,
   208  		},
   209  		{
   210  			desc: "Load of 32-bit input by absolute offset into A",
   211  			insns: []linux.BPFInstruction{
   212  				Stmt(Ld|Abs|W, 1), // A = input[1..4]
   213  				Stmt(Ret|A, 0),    // return A
   214  			},
   215  			input:       []byte{0x00, 0x11, 0x22, 0x33, 0x44},
   216  			expectedRet: 0x11223344,
   217  		},
   218  		{
   219  			desc: "Load of 16-bit input by absolute offset into A",
   220  			insns: []linux.BPFInstruction{
   221  				Stmt(Ld|Abs|H, 1), // A = input[1..2]
   222  				Stmt(Ret|A, 0),    // return A
   223  			},
   224  			input:       []byte{0x00, 0x11, 0x22},
   225  			expectedRet: 0x1122,
   226  		},
   227  		{
   228  			desc: "Load of 8-bit input by absolute offset into A",
   229  			insns: []linux.BPFInstruction{
   230  				Stmt(Ld|Abs|B, 1), // A = input[1]
   231  				Stmt(Ret|A, 0),    // return A
   232  			},
   233  			input:       []byte{0x00, 0x11},
   234  			expectedRet: 0x11,
   235  		},
   236  		{
   237  			desc: "Load of 32-bit input by relative offset into A",
   238  			insns: []linux.BPFInstruction{
   239  				Stmt(Ldx|Imm|W, 1), // X = 1
   240  				Stmt(Ld|Ind|W, 1),  // A = input[X+1..X+4]
   241  				Stmt(Ret|A, 0),     // return A
   242  			},
   243  			input:       []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
   244  			expectedRet: 0x22334455,
   245  		},
   246  		{
   247  			desc: "Load of 16-bit input by relative offset into A",
   248  			insns: []linux.BPFInstruction{
   249  				Stmt(Ldx|Imm|W, 1), // X = 1
   250  				Stmt(Ld|Ind|H, 1),  // A = input[X+1..X+2]
   251  				Stmt(Ret|A, 0),     // return A
   252  			},
   253  			input:       []byte{0x00, 0x11, 0x22, 0x33},
   254  			expectedRet: 0x2233,
   255  		},
   256  		{
   257  			desc: "Load of 8-bit input by relative offset into A",
   258  			insns: []linux.BPFInstruction{
   259  				Stmt(Ldx|Imm|W, 1), // X = 1
   260  				Stmt(Ld|Ind|B, 1),  // A = input[X+1]
   261  				Stmt(Ret|A, 0),     // return A
   262  			},
   263  			input:       []byte{0x00, 0x11, 0x22},
   264  			expectedRet: 0x22,
   265  		},
   266  		{
   267  			desc: "Load/store between A and scratch memory",
   268  			insns: []linux.BPFInstruction{
   269  				Stmt(Ld|Imm|W, 42), // A = 42
   270  				Stmt(St, 2),        // M[2] = A
   271  				Stmt(Ld|Imm|W, 0),  // A = 0
   272  				Stmt(Ld|Mem|W, 2),  // A = M[2]
   273  				Stmt(Ret|A, 0),     // return A
   274  			},
   275  			expectedRet: 42,
   276  		},
   277  		{
   278  			desc: "Load/store between X and scratch memory",
   279  			insns: []linux.BPFInstruction{
   280  				Stmt(Ldx|Imm|W, 42), // X = 42
   281  				Stmt(Stx, 3),        // M[3] = X
   282  				Stmt(Ldx|Imm|W, 0),  // X = 0
   283  				Stmt(Ldx|Mem|W, 3),  // X = M[3]
   284  				Stmt(Misc|Tax, 0),   // A = X
   285  				Stmt(Ret|A, 0),      // return A
   286  			},
   287  			expectedRet: 42,
   288  		},
   289  		{
   290  			desc: "Load of input length into A",
   291  			insns: []linux.BPFInstruction{
   292  				Stmt(Ld|Len|W, 0), // A = len(input)
   293  				Stmt(Ret|A, 0),    // return A
   294  			},
   295  			input:       []byte{1, 2, 3},
   296  			expectedRet: 3,
   297  		},
   298  		{
   299  			desc: "Load of input length into X",
   300  			insns: []linux.BPFInstruction{
   301  				Stmt(Ldx|Len|W, 0), // X = len(input)
   302  				Stmt(Misc|Tax, 0),  // A = X
   303  				Stmt(Ret|A, 0),     // return A
   304  			},
   305  			input:       []byte{1, 2, 3},
   306  			expectedRet: 3,
   307  		},
   308  		{
   309  			desc: "Load of MSH (?) into X",
   310  			insns: []linux.BPFInstruction{
   311  				Stmt(Ldx|Msh|B, 0), // X = 4*(input[0]&0xf)
   312  				Stmt(Misc|Tax, 0),  // A = X
   313  				Stmt(Ret|A, 0),     // return A
   314  			},
   315  			input:       []byte{0xf1},
   316  			expectedRet: 4,
   317  		},
   318  		{
   319  			desc: "Addition of immediate",
   320  			insns: []linux.BPFInstruction{
   321  				Stmt(Ld|Imm|W, 10),  // A = 10
   322  				Stmt(Alu|Add|K, 20), // A += 20
   323  				Stmt(Ret|A, 0),      // return A
   324  			},
   325  			expectedRet: 30,
   326  		},
   327  		{
   328  			desc: "Addition of X",
   329  			insns: []linux.BPFInstruction{
   330  				Stmt(Ld|Imm|W, 10),  // A = 10
   331  				Stmt(Ldx|Imm|W, 20), // X = 20
   332  				Stmt(Alu|Add|X, 0),  // A += X
   333  				Stmt(Ret|A, 0),      // return A
   334  			},
   335  			expectedRet: 30,
   336  		},
   337  		{
   338  			desc: "Subtraction of immediate",
   339  			insns: []linux.BPFInstruction{
   340  				Stmt(Ld|Imm|W, 30),  // A = 30
   341  				Stmt(Alu|Sub|K, 20), // A -= 20
   342  				Stmt(Ret|A, 0),      // return A
   343  			},
   344  			expectedRet: 10,
   345  		},
   346  		{
   347  			desc: "Subtraction of X",
   348  			insns: []linux.BPFInstruction{
   349  				Stmt(Ld|Imm|W, 30),  // A = 30
   350  				Stmt(Ldx|Imm|W, 20), // X = 20
   351  				Stmt(Alu|Sub|X, 0),  // A -= X
   352  				Stmt(Ret|A, 0),      // return A
   353  			},
   354  			expectedRet: 10,
   355  		},
   356  		{
   357  			desc: "Multiplication of immediate",
   358  			insns: []linux.BPFInstruction{
   359  				Stmt(Ld|Imm|W, 2),  // A = 2
   360  				Stmt(Alu|Mul|K, 3), // A *= 3
   361  				Stmt(Ret|A, 0),     // return A
   362  			},
   363  			expectedRet: 6,
   364  		},
   365  		{
   366  			desc: "Multiplication of X",
   367  			insns: []linux.BPFInstruction{
   368  				Stmt(Ld|Imm|W, 2),  // A = 2
   369  				Stmt(Ldx|Imm|W, 3), // X = 3
   370  				Stmt(Alu|Mul|X, 0), // A *= X
   371  				Stmt(Ret|A, 0),     // return A
   372  			},
   373  			expectedRet: 6,
   374  		},
   375  		{
   376  			desc: "Division by immediate",
   377  			insns: []linux.BPFInstruction{
   378  				Stmt(Ld|Imm|W, 6),  // A = 6
   379  				Stmt(Alu|Div|K, 3), // A /= 3
   380  				Stmt(Ret|A, 0),     // return A
   381  			},
   382  			expectedRet: 2,
   383  		},
   384  		{
   385  			desc: "Division by X",
   386  			insns: []linux.BPFInstruction{
   387  				Stmt(Ld|Imm|W, 6),  // A = 6
   388  				Stmt(Ldx|Imm|W, 3), // X = 3
   389  				Stmt(Alu|Div|X, 0), // A /= X
   390  				Stmt(Ret|A, 0),     // return A
   391  			},
   392  			expectedRet: 2,
   393  		},
   394  		{
   395  			desc: "Modulo immediate",
   396  			insns: []linux.BPFInstruction{
   397  				Stmt(Ld|Imm|W, 17), // A = 17
   398  				Stmt(Alu|Mod|K, 7), // A %= 7
   399  				Stmt(Ret|A, 0),     // return A
   400  			},
   401  			expectedRet: 3,
   402  		},
   403  		{
   404  			desc: "Modulo X",
   405  			insns: []linux.BPFInstruction{
   406  				Stmt(Ld|Imm|W, 17), // A = 17
   407  				Stmt(Ldx|Imm|W, 7), // X = 7
   408  				Stmt(Alu|Mod|X, 0), // A %= X
   409  				Stmt(Ret|A, 0),     // return A
   410  			},
   411  			expectedRet: 3,
   412  		},
   413  		{
   414  			desc: "Arithmetic negation",
   415  			insns: []linux.BPFInstruction{
   416  				Stmt(Ld|Imm|W, 1), // A = 1
   417  				Stmt(Alu|Neg, 0),  // A = -A
   418  				Stmt(Ret|A, 0),    // return A
   419  			},
   420  			expectedRet: 0xffffffff,
   421  		},
   422  		{
   423  			desc: "Bitwise OR with immediate",
   424  			insns: []linux.BPFInstruction{
   425  				Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
   426  				Stmt(Alu|Or|K, 0xff0055aa), // A |= 0xff0055aa
   427  				Stmt(Ret|A, 0),             // return A
   428  			},
   429  			expectedRet: 0xff00ffff,
   430  		},
   431  		{
   432  			desc: "Bitwise OR with X",
   433  			insns: []linux.BPFInstruction{
   434  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   435  				Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
   436  				Stmt(Alu|Or|X, 0),           // A |= X
   437  				Stmt(Ret|A, 0),              // return A
   438  			},
   439  			expectedRet: 0xff00ffff,
   440  		},
   441  		{
   442  			desc: "Bitwise AND with immediate",
   443  			insns: []linux.BPFInstruction{
   444  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   445  				Stmt(Alu|And|K, 0xff0055aa), // A &= 0xff0055aa
   446  				Stmt(Ret|A, 0),              // return A
   447  			},
   448  			expectedRet: 0xff000000,
   449  		},
   450  		{
   451  			desc: "Bitwise AND with X",
   452  			insns: []linux.BPFInstruction{
   453  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   454  				Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
   455  				Stmt(Alu|And|X, 0),          // A &= X
   456  				Stmt(Ret|A, 0),              // return A
   457  			},
   458  			expectedRet: 0xff000000,
   459  		},
   460  		{
   461  			desc: "Bitwise XOR with immediate",
   462  			insns: []linux.BPFInstruction{
   463  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   464  				Stmt(Alu|Xor|K, 0xff0055aa), // A ^= 0xff0055aa
   465  				Stmt(Ret|A, 0),              // return A
   466  			},
   467  			expectedRet: 0x0000ffff,
   468  		},
   469  		{
   470  			desc: "Bitwise XOR with X",
   471  			insns: []linux.BPFInstruction{
   472  				Stmt(Ld|Imm|W, 0xff00aa55),  // A = 0xff00aa55
   473  				Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
   474  				Stmt(Alu|Xor|X, 0),          // A ^= X
   475  				Stmt(Ret|A, 0),              // return A
   476  			},
   477  			expectedRet: 0x0000ffff,
   478  		},
   479  		{
   480  			desc: "Left shift by immediate",
   481  			insns: []linux.BPFInstruction{
   482  				Stmt(Ld|Imm|W, 1),  // A = 1
   483  				Stmt(Alu|Lsh|K, 5), // A <<= 5
   484  				Stmt(Ret|A, 0),     // return A
   485  			},
   486  			expectedRet: 32,
   487  		},
   488  		{
   489  			desc: "Left shift by X",
   490  			insns: []linux.BPFInstruction{
   491  				Stmt(Ld|Imm|W, 1),  // A = 1
   492  				Stmt(Ldx|Imm|W, 5), // X = 5
   493  				Stmt(Alu|Lsh|X, 0), // A <<= X
   494  				Stmt(Ret|A, 0),     // return A
   495  			},
   496  			expectedRet: 32,
   497  		},
   498  		{
   499  			desc: "Right shift by immediate",
   500  			insns: []linux.BPFInstruction{
   501  				Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff
   502  				Stmt(Alu|Rsh|K, 31),        // A >>= 31
   503  				Stmt(Ret|A, 0),             // return A
   504  			},
   505  			expectedRet: 1,
   506  		},
   507  		{
   508  			desc: "Right shift by X",
   509  			insns: []linux.BPFInstruction{
   510  				Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff
   511  				Stmt(Ldx|Imm|W, 31),        // X = 31
   512  				Stmt(Alu|Rsh|X, 0),         // A >>= X
   513  				Stmt(Ret|A, 0),             // return A
   514  			},
   515  			expectedRet: 1,
   516  		},
   517  		{
   518  			desc: "Unconditional jump",
   519  			insns: []linux.BPFInstruction{
   520  				Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1
   521  				Stmt(Ret|K, 0),        // return 0
   522  				Stmt(Ret|K, 1),        // return 1
   523  			},
   524  			expectedRet: 1,
   525  		},
   526  		{
   527  			desc: "Jump when A == immediate",
   528  			insns: []linux.BPFInstruction{
   529  				Stmt(Ld|Imm|W, 42),        // A = 42
   530  				Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2
   531  				Stmt(Ret|K, 0),            // return 0
   532  				Stmt(Ret|K, 1),            // return 1
   533  				Stmt(Ret|K, 2),            // return 2
   534  			},
   535  			expectedRet: 1,
   536  		},
   537  		{
   538  			desc: "Jump when A != immediate",
   539  			insns: []linux.BPFInstruction{
   540  				Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2
   541  				Stmt(Ret|K, 0),            // return 0
   542  				Stmt(Ret|K, 1),            // return 1
   543  				Stmt(Ret|K, 2),            // return 2
   544  			},
   545  			expectedRet: 2,
   546  		},
   547  		{
   548  			desc: "Jump when A == X",
   549  			insns: []linux.BPFInstruction{
   550  				Stmt(Ld|Imm|W, 42),       // A = 42
   551  				Stmt(Ldx|Imm|W, 42),      // X = 42
   552  				Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2
   553  				Stmt(Ret|K, 0),           // return 0
   554  				Stmt(Ret|K, 1),           // return 1
   555  				Stmt(Ret|K, 2),           // return 2
   556  			},
   557  			expectedRet: 1,
   558  		},
   559  		{
   560  			desc: "Jump when A != X",
   561  			insns: []linux.BPFInstruction{
   562  				Stmt(Ld|Imm|W, 42),       // A = 42
   563  				Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2
   564  				Stmt(Ret|K, 0),           // return 0
   565  				Stmt(Ret|K, 1),           // return 1
   566  				Stmt(Ret|K, 2),           // return 2
   567  			},
   568  			expectedRet: 2,
   569  		},
   570  		{
   571  			desc: "Jump when A > immediate",
   572  			insns: []linux.BPFInstruction{
   573  				Stmt(Ld|Imm|W, 10),       // A = 10
   574  				Jump(Jmp|Jgt|K, 9, 1, 2), // if (A > 9) jmp nextpc+1 else jmp nextpc+2
   575  				Stmt(Ret|K, 0),           // return 0
   576  				Stmt(Ret|K, 1),           // return 1
   577  				Stmt(Ret|K, 2),           // return 2
   578  			},
   579  			expectedRet: 1,
   580  		},
   581  		{
   582  			desc: "Jump when A <= immediate",
   583  			insns: []linux.BPFInstruction{
   584  				Stmt(Ld|Imm|W, 10),        // A = 10
   585  				Jump(Jmp|Jgt|K, 10, 1, 2), // if (A > 10) jmp nextpc+1 else jmp nextpc+2
   586  				Stmt(Ret|K, 0),            // return 0
   587  				Stmt(Ret|K, 1),            // return 1
   588  				Stmt(Ret|K, 2),            // return 2
   589  			},
   590  			expectedRet: 2,
   591  		},
   592  		{
   593  			desc: "Jump when A > X",
   594  			insns: []linux.BPFInstruction{
   595  				Stmt(Ld|Imm|W, 10),       // A = 10
   596  				Stmt(Ldx|Imm|W, 9),       // X = 9
   597  				Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) 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  			expectedRet: 1,
   603  		},
   604  		{
   605  			desc: "Jump when A <= X",
   606  			insns: []linux.BPFInstruction{
   607  				Stmt(Ld|Imm|W, 10),       // A = 10
   608  				Stmt(Ldx|Imm|W, 10),      // X = 10
   609  				Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) jmp nextpc+1 else jmp nextpc+2
   610  				Stmt(Ret|K, 0),           // return 0
   611  				Stmt(Ret|K, 1),           // return 1
   612  				Stmt(Ret|K, 2),           // return 2
   613  			},
   614  			expectedRet: 2,
   615  		},
   616  		{
   617  			desc: "Jump when A >= immediate",
   618  			insns: []linux.BPFInstruction{
   619  				Stmt(Ld|Imm|W, 10),        // A = 10
   620  				Jump(Jmp|Jge|K, 10, 1, 2), // if (A >= 10) jmp nextpc+1 else jmp nextpc+2
   621  				Stmt(Ret|K, 0),            // return 0
   622  				Stmt(Ret|K, 1),            // return 1
   623  				Stmt(Ret|K, 2),            // return 2
   624  			},
   625  			expectedRet: 1,
   626  		},
   627  		{
   628  			desc: "Jump when A < immediate",
   629  			insns: []linux.BPFInstruction{
   630  				Stmt(Ld|Imm|W, 10),        // A = 10
   631  				Jump(Jmp|Jge|K, 11, 1, 2), // if (A >= 11) jmp nextpc+1 else jmp nextpc+2
   632  				Stmt(Ret|K, 0),            // return 0
   633  				Stmt(Ret|K, 1),            // return 1
   634  				Stmt(Ret|K, 2),            // return 2
   635  			},
   636  			expectedRet: 2,
   637  		},
   638  		{
   639  			desc: "Jump when A >= X",
   640  			insns: []linux.BPFInstruction{
   641  				Stmt(Ld|Imm|W, 10),       // A = 10
   642  				Stmt(Ldx|Imm|W, 10),      // X = 10
   643  				Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2
   644  				Stmt(Ret|K, 0),           // return 0
   645  				Stmt(Ret|K, 1),           // return 1
   646  				Stmt(Ret|K, 2),           // return 2
   647  			},
   648  			expectedRet: 1,
   649  		},
   650  		{
   651  			desc: "Jump when A < X",
   652  			insns: []linux.BPFInstruction{
   653  				Stmt(Ld|Imm|W, 10),       // A = 10
   654  				Stmt(Ldx|Imm|W, 11),      // X = 11
   655  				Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2
   656  				Stmt(Ret|K, 0),           // return 0
   657  				Stmt(Ret|K, 1),           // return 1
   658  				Stmt(Ret|K, 2),           // return 2
   659  			},
   660  			expectedRet: 2,
   661  		},
   662  		{
   663  			desc: "Jump when A & immediate != 0",
   664  			insns: []linux.BPFInstruction{
   665  				Stmt(Ld|Imm|W, 0xff),          // A = 0xff
   666  				Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2
   667  				Stmt(Ret|K, 0),                // return 0
   668  				Stmt(Ret|K, 1),                // return 1
   669  				Stmt(Ret|K, 2),                // return 2
   670  			},
   671  			expectedRet: 1,
   672  		},
   673  		{
   674  			desc: "Jump when A & immediate == 0",
   675  			insns: []linux.BPFInstruction{
   676  				Stmt(Ld|Imm|W, 0xfe),          // A = 0xfe
   677  				Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2
   678  				Stmt(Ret|K, 0),                // return 0
   679  				Stmt(Ret|K, 1),                // return 1
   680  				Stmt(Ret|K, 2),                // return 2
   681  			},
   682  			expectedRet: 2,
   683  		},
   684  		{
   685  			desc: "Jump when A & X != 0",
   686  			insns: []linux.BPFInstruction{
   687  				Stmt(Ld|Imm|W, 0xff),      // A = 0xff
   688  				Stmt(Ldx|Imm|W, 0x101),    // X = 0x101
   689  				Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2
   690  				Stmt(Ret|K, 0),            // return 0
   691  				Stmt(Ret|K, 1),            // return 1
   692  				Stmt(Ret|K, 2),            // return 2
   693  			},
   694  			expectedRet: 1,
   695  		},
   696  		{
   697  			desc: "Jump when A & X == 0",
   698  			insns: []linux.BPFInstruction{
   699  				Stmt(Ld|Imm|W, 0xfe),      // A = 0xfe
   700  				Stmt(Ldx|Imm|W, 0x101),    // X = 0x101
   701  				Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2
   702  				Stmt(Ret|K, 0),            // return 0
   703  				Stmt(Ret|K, 1),            // return 1
   704  				Stmt(Ret|K, 2),            // return 2
   705  			},
   706  			expectedRet: 2,
   707  		},
   708  	} {
   709  		p, err := Compile(test.insns)
   710  		if err != nil {
   711  			t.Errorf("%s: unexpected compilation error: %v", test.desc, err)
   712  			continue
   713  		}
   714  		ret, err := Exec(p, InputBytes{test.input, binary.BigEndian})
   715  		if err != nil {
   716  			t.Errorf("%s: expected return value of %d, got execution error: %v", test.desc, test.expectedRet, err)
   717  			continue
   718  		}
   719  		if ret != test.expectedRet {
   720  			t.Errorf("%s: expected return value of %d, got value %d", test.desc, test.expectedRet, ret)
   721  		}
   722  	}
   723  }
   724  
   725  func TestSimpleFilter(t *testing.T) {
   726  	// Seccomp filter example given in Linux's
   727  	// Documentation/networking/filter.txt, translated to bytecode using the
   728  	// Linux kernel tree's tools/net/bpf_asm.
   729  	filter := []linux.BPFInstruction{
   730  		{0x20, 0, 0, 0x00000004},  // ld [4]                  /* offsetof(struct seccomp_data, arch) */
   731  		{0x15, 0, 11, 0xc000003e}, // jne #0xc000003e, bad    /* AUDIT_ARCH_X86_64 */
   732  		{0x20, 0, 0, 0000000000},  // ld [0]                  /* offsetof(struct seccomp_data, nr) */
   733  		{0x15, 10, 0, 0x0000000f}, // jeq #15, good           /* __NR_rt_sigreturn */
   734  		{0x15, 9, 0, 0x000000e7},  // jeq #231, good          /* __NR_exit_group */
   735  		{0x15, 8, 0, 0x0000003c},  // jeq #60, good           /* __NR_exit */
   736  		{0x15, 7, 0, 0000000000},  // jeq #0, good            /* __NR_read */
   737  		{0x15, 6, 0, 0x00000001},  // jeq #1, good            /* __NR_write */
   738  		{0x15, 5, 0, 0x00000005},  // jeq #5, good            /* __NR_fstat */
   739  		{0x15, 4, 0, 0x00000009},  // jeq #9, good            /* __NR_mmap */
   740  		{0x15, 3, 0, 0x0000000e},  // jeq #14, good           /* __NR_rt_sigprocmask */
   741  		{0x15, 2, 0, 0x0000000d},  // jeq #13, good           /* __NR_rt_sigaction */
   742  		{0x15, 1, 0, 0x00000023},  // jeq #35, good           /* __NR_nanosleep */
   743  		{0x06, 0, 0, 0000000000},  // bad: ret #0             /* SECCOMP_RET_KILL */
   744  		{0x06, 0, 0, 0x7fff0000},  // good: ret #0x7fff0000   /* SECCOMP_RET_ALLOW */
   745  	}
   746  	p, err := Compile(filter)
   747  	if err != nil {
   748  		t.Fatalf("Unexpected compilation error: %v", err)
   749  	}
   750  
   751  	for _, test := range []struct {
   752  		// desc is the test's description.
   753  		desc string
   754  
   755  		// SeccompData is the input data.
   756  		data linux.SeccompData
   757  
   758  		// expectedRet is the expected return value of the BPF program.
   759  		expectedRet uint32
   760  	}{
   761  		{
   762  			desc:        "Invalid arch is rejected",
   763  			data:        linux.SeccompData{Nr: 1 /* x86 exit */, Arch: 0x40000003 /* AUDIT_ARCH_I386 */},
   764  			expectedRet: 0,
   765  		},
   766  		{
   767  			desc:        "Disallowed syscall is rejected",
   768  			data:        linux.SeccompData{Nr: 105 /* __NR_setuid */, Arch: 0xc000003e},
   769  			expectedRet: 0,
   770  		},
   771  		{
   772  			desc:        "Allowed syscall is indeed allowed",
   773  			data:        linux.SeccompData{Nr: 231 /* __NR_exit_group */, Arch: 0xc000003e},
   774  			expectedRet: 0x7fff0000,
   775  		},
   776  	} {
   777  		ret, err := Exec(p, dataAsInput(&test.data))
   778  		if err != nil {
   779  			t.Errorf("%s: expected return value of %d, got execution error: %v", test.desc, test.expectedRet, err)
   780  			continue
   781  		}
   782  		if ret != test.expectedRet {
   783  			t.Errorf("%s: expected return value of %d, got value %d", test.desc, test.expectedRet, ret)
   784  		}
   785  	}
   786  }
   787  
   788  // seccompData is equivalent to struct seccomp_data.
   789  type seccompData struct {
   790  	nr                 uint32
   791  	arch               uint32
   792  	instructionPointer uint64
   793  	args               [6]uint64
   794  }
   795  
   796  // asInput converts a seccompData to a bpf.Input.
   797  func dataAsInput(data *linux.SeccompData) Input {
   798  	return InputBytes{marshal.Marshal(data), hostarch.ByteOrder}
   799  }