summaryrefslogtreecommitdiffhomepage
path: root/pkg/bpf
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/bpf')
-rw-r--r--pkg/bpf/BUILD31
-rw-r--r--pkg/bpf/bpf_state_autogen.go36
-rw-r--r--pkg/bpf/decoder_test.go146
-rw-r--r--pkg/bpf/interpreter_test.go797
-rw-r--r--pkg/bpf/program_builder_test.go185
5 files changed, 36 insertions, 1159 deletions
diff --git a/pkg/bpf/BUILD b/pkg/bpf/BUILD
deleted file mode 100644
index 2a6977f85..000000000
--- a/pkg/bpf/BUILD
+++ /dev/null
@@ -1,31 +0,0 @@
-load("//tools:defs.bzl", "go_library", "go_test")
-
-package(licenses = ["notice"])
-
-go_library(
- name = "bpf",
- srcs = [
- "bpf.go",
- "decoder.go",
- "input_bytes.go",
- "interpreter.go",
- "program_builder.go",
- ],
- visibility = ["//visibility:public"],
- deps = ["//pkg/abi/linux"],
-)
-
-go_test(
- name = "bpf_test",
- size = "small",
- srcs = [
- "decoder_test.go",
- "interpreter_test.go",
- "program_builder_test.go",
- ],
- library = ":bpf",
- deps = [
- "//pkg/abi/linux",
- "//pkg/binary",
- ],
-)
diff --git a/pkg/bpf/bpf_state_autogen.go b/pkg/bpf/bpf_state_autogen.go
new file mode 100644
index 000000000..3a020fe29
--- /dev/null
+++ b/pkg/bpf/bpf_state_autogen.go
@@ -0,0 +1,36 @@
+// automatically generated by stateify.
+
+package bpf
+
+import (
+ "gvisor.dev/gvisor/pkg/state"
+)
+
+func (p *Program) StateTypeName() string {
+ return "pkg/bpf.Program"
+}
+
+func (p *Program) StateFields() []string {
+ return []string{
+ "instructions",
+ }
+}
+
+func (p *Program) beforeSave() {}
+
+// +checklocksignore
+func (p *Program) StateSave(stateSinkObject state.Sink) {
+ p.beforeSave()
+ stateSinkObject.Save(0, &p.instructions)
+}
+
+func (p *Program) afterLoad() {}
+
+// +checklocksignore
+func (p *Program) StateLoad(stateSourceObject state.Source) {
+ stateSourceObject.Load(0, &p.instructions)
+}
+
+func init() {
+ state.Register((*Program)(nil))
+}
diff --git a/pkg/bpf/decoder_test.go b/pkg/bpf/decoder_test.go
deleted file mode 100644
index bb971ce21..000000000
--- a/pkg/bpf/decoder_test.go
+++ /dev/null
@@ -1,146 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bpf
-
-import (
- "testing"
-
- "gvisor.dev/gvisor/pkg/abi/linux"
-)
-
-func TestDecode(t *testing.T) {
- for _, test := range []struct {
- filter linux.BPFInstruction
- expected string
- fail bool
- }{
- {filter: Stmt(Ld+Imm, 10), expected: "A <- 10"},
- {filter: Stmt(Ld+Abs+W, 10), expected: "A <- P[10:4]"},
- {filter: Stmt(Ld+Ind+H, 10), expected: "A <- P[X+10:2]"},
- {filter: Stmt(Ld+Ind+B, 10), expected: "A <- P[X+10:1]"},
- {filter: Stmt(Ld+Mem, 10), expected: "A <- M[10]"},
- {filter: Stmt(Ld+Len, 0), expected: "A <- len"},
- {filter: Stmt(Ldx+Imm, 10), expected: "X <- 10"},
- {filter: Stmt(Ldx+Mem, 10), expected: "X <- M[10]"},
- {filter: Stmt(Ldx+Len, 0), expected: "X <- len"},
- {filter: Stmt(Ldx+Msh, 10), expected: "X <- 4*(P[10:1]&0xf)"},
- {filter: Stmt(St, 10), expected: "M[10] <- A"},
- {filter: Stmt(Stx, 10), expected: "M[10] <- X"},
- {filter: Stmt(Alu+Add+K, 10), expected: "A <- A + 10"},
- {filter: Stmt(Alu+Sub+K, 10), expected: "A <- A - 10"},
- {filter: Stmt(Alu+Mul+K, 10), expected: "A <- A * 10"},
- {filter: Stmt(Alu+Div+K, 10), expected: "A <- A / 10"},
- {filter: Stmt(Alu+Or+K, 10), expected: "A <- A | 10"},
- {filter: Stmt(Alu+And+K, 10), expected: "A <- A & 10"},
- {filter: Stmt(Alu+Lsh+K, 10), expected: "A <- A << 10"},
- {filter: Stmt(Alu+Rsh+K, 10), expected: "A <- A >> 10"},
- {filter: Stmt(Alu+Mod+K, 10), expected: "A <- A % 10"},
- {filter: Stmt(Alu+Xor+K, 10), expected: "A <- A ^ 10"},
- {filter: Stmt(Alu+Add+X, 0), expected: "A <- A + X"},
- {filter: Stmt(Alu+Sub+X, 0), expected: "A <- A - X"},
- {filter: Stmt(Alu+Mul+X, 0), expected: "A <- A * X"},
- {filter: Stmt(Alu+Div+X, 0), expected: "A <- A / X"},
- {filter: Stmt(Alu+Or+X, 0), expected: "A <- A | X"},
- {filter: Stmt(Alu+And+X, 0), expected: "A <- A & X"},
- {filter: Stmt(Alu+Lsh+X, 0), expected: "A <- A << X"},
- {filter: Stmt(Alu+Rsh+X, 0), expected: "A <- A >> X"},
- {filter: Stmt(Alu+Mod+X, 0), expected: "A <- A % X"},
- {filter: Stmt(Alu+Xor+X, 0), expected: "A <- A ^ X"},
- {filter: Stmt(Alu+Neg, 0), expected: "A <- -A"},
- {filter: Stmt(Jmp+Ja, 10), expected: "pc += 10"},
- {filter: Jump(Jmp+Jeq+K, 10, 2, 5), expected: "pc += (A == 10) ? 2 : 5"},
- {filter: Jump(Jmp+Jgt+K, 10, 2, 5), expected: "pc += (A > 10) ? 2 : 5"},
- {filter: Jump(Jmp+Jge+K, 10, 2, 5), expected: "pc += (A >= 10) ? 2 : 5"},
- {filter: Jump(Jmp+Jset+K, 10, 2, 5), expected: "pc += (A & 10) ? 2 : 5"},
- {filter: Jump(Jmp+Jeq+X, 0, 2, 5), expected: "pc += (A == X) ? 2 : 5"},
- {filter: Jump(Jmp+Jgt+X, 0, 2, 5), expected: "pc += (A > X) ? 2 : 5"},
- {filter: Jump(Jmp+Jge+X, 0, 2, 5), expected: "pc += (A >= X) ? 2 : 5"},
- {filter: Jump(Jmp+Jset+X, 0, 2, 5), expected: "pc += (A & X) ? 2 : 5"},
- {filter: Stmt(Ret+K, 10), expected: "ret 10"},
- {filter: Stmt(Ret+A, 0), expected: "ret A"},
- {filter: Stmt(Misc+Tax, 0), expected: "X <- A"},
- {filter: Stmt(Misc+Txa, 0), expected: "A <- X"},
- {filter: Stmt(Ld+Ind+Msh, 0), fail: true},
- } {
- got, err := Decode(test.filter)
- if test.fail {
- if err == nil {
- t.Errorf("Decode(%v) failed, expected: 'error', got: %q", test.filter, got)
- continue
- }
- } else {
- if err != nil {
- t.Errorf("Decode(%v) failed for test %q, error: %q", test.filter, test.expected, err)
- continue
- }
- if got != test.expected {
- t.Errorf("Decode(%v) failed, expected: %q, got: %q", test.filter, test.expected, got)
- continue
- }
- }
- }
-}
-
-func TestDecodeInstructions(t *testing.T) {
- for _, test := range []struct {
- name string
- program []linux.BPFInstruction
- expected string
- fail bool
- }{
- {name: "basic with jump indexes",
- program: []linux.BPFInstruction{
- Stmt(Ld+Abs+W, 10),
- Stmt(Ldx+Mem, 10),
- Stmt(St, 10),
- Stmt(Stx, 10),
- Stmt(Alu+Add+K, 10),
- Stmt(Jmp+Ja, 10),
- Jump(Jmp+Jeq+K, 10, 2, 5),
- Jump(Jmp+Jset+X, 0, 0, 5),
- Stmt(Misc+Tax, 0),
- },
- expected: "0: A <- P[10:4]\n" +
- "1: X <- M[10]\n" +
- "2: M[10] <- A\n" +
- "3: M[10] <- X\n" +
- "4: A <- A + 10\n" +
- "5: pc += 10 [16]\n" +
- "6: pc += (A == 10) ? 2 [9] : 5 [12]\n" +
- "7: pc += (A & X) ? 0 [8] : 5 [13]\n" +
- "8: X <- A\n",
- },
- {name: "invalid instruction",
- program: []linux.BPFInstruction{Stmt(Ld+Abs+W, 10), Stmt(Ld+Len+Mem, 0)},
- fail: true},
- } {
- got, err := DecodeInstructions(test.program)
- if test.fail {
- if err == nil {
- t.Errorf("%s: Decode(...) failed, expected: 'error', got: %q", test.name, got)
- continue
- }
- } else {
- if err != nil {
- t.Errorf("%s: Decode failed: %v", test.name, err)
- continue
- }
- if got != test.expected {
- t.Errorf("%s: Decode(...) failed, expected: %q, got: %q", test.name, test.expected, got)
- continue
- }
- }
- }
-}
diff --git a/pkg/bpf/interpreter_test.go b/pkg/bpf/interpreter_test.go
deleted file mode 100644
index c85d786b9..000000000
--- a/pkg/bpf/interpreter_test.go
+++ /dev/null
@@ -1,797 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bpf
-
-import (
- "testing"
-
- "gvisor.dev/gvisor/pkg/abi/linux"
- "gvisor.dev/gvisor/pkg/binary"
-)
-
-func TestCompilationErrors(t *testing.T) {
- for _, test := range []struct {
- // desc is the test's description.
- desc string
-
- // insns is the BPF instructions to be compiled.
- insns []linux.BPFInstruction
-
- // expectedErr is the expected compilation error.
- expectedErr error
- }{
- {
- desc: "Instructions must not be nil",
- expectedErr: Error{InvalidInstructionCount, 0},
- },
- {
- desc: "Instructions must not be empty",
- insns: []linux.BPFInstruction{},
- expectedErr: Error{InvalidInstructionCount, 0},
- },
- {
- desc: "A program must end with a return",
- insns: make([]linux.BPFInstruction, MaxInstructions),
- expectedErr: Error{InvalidEndOfProgram, MaxInstructions - 1},
- },
- {
- desc: "A program must have MaxInstructions or fewer instructions",
- insns: append(make([]linux.BPFInstruction, MaxInstructions), Stmt(Ret|K, 0)),
- expectedErr: Error{InvalidInstructionCount, MaxInstructions + 1},
- },
- {
- desc: "A load from an invalid M register is a compilation error",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Mem|W, ScratchMemRegisters), // A = M[16]
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{InvalidRegister, 0},
- },
- {
- desc: "A store to an invalid M register is a compilation error",
- insns: []linux.BPFInstruction{
- Stmt(St, ScratchMemRegisters), // M[16] = A
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{InvalidRegister, 0},
- },
- {
- desc: "Division by literal zero is a compilation error",
- insns: []linux.BPFInstruction{
- Stmt(Alu|Div|K, 0), // A /= 0
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{DivisionByZero, 0},
- },
- {
- desc: "An unconditional jump outside of the program is a compilation error",
- insns: []linux.BPFInstruction{
- Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{InvalidJumpTarget, 0},
- },
- {
- desc: "A conditional jump outside of the program in the true case is a compilation error",
- insns: []linux.BPFInstruction{
- Jump(Jmp|Jeq|K, 0, 1, 0), // if (A == K) jmp nextpc+1
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{InvalidJumpTarget, 0},
- },
- {
- desc: "A conditional jump outside of the program in the false case is a compilation error",
- insns: []linux.BPFInstruction{
- Jump(Jmp|Jeq|K, 0, 0, 1), // if (A != K) jmp nextpc+1
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{InvalidJumpTarget, 0},
- },
- } {
- _, err := Compile(test.insns)
- if err != test.expectedErr {
- t.Errorf("%s: expected error %q, got error %q", test.desc, test.expectedErr, err)
- }
- }
-}
-
-func TestExecErrors(t *testing.T) {
- for _, test := range []struct {
- // desc is the test's description.
- desc string
-
- // insns is the BPF instructions to be executed.
- insns []linux.BPFInstruction
-
- // expectedErr is the expected execution error.
- expectedErr error
- }{
- {
- desc: "An out-of-bounds load of input data is an execution error",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Abs|B, 0), // A = input[0]
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{InvalidLoad, 0},
- },
- {
- desc: "Division by zero at runtime is an execution error",
- insns: []linux.BPFInstruction{
- Stmt(Alu|Div|X, 0), // A /= X
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{DivisionByZero, 0},
- },
- {
- desc: "Modulo zero at runtime is an execution error",
- insns: []linux.BPFInstruction{
- Stmt(Alu|Mod|X, 0), // A %= X
- Stmt(Ret|K, 0), // return 0
- },
- expectedErr: Error{DivisionByZero, 0},
- },
- } {
- p, err := Compile(test.insns)
- if err != nil {
- t.Errorf("%s: unexpected compilation error: %v", test.desc, err)
- continue
- }
- ret, err := Exec(p, InputBytes{nil, binary.BigEndian})
- if err != test.expectedErr {
- t.Errorf("%s: expected execution error %q, got (%d, %v)", test.desc, test.expectedErr, ret, err)
- }
- }
-}
-
-func TestValidInstructions(t *testing.T) {
- for _, test := range []struct {
- // desc is the test's description.
- desc string
-
- // insns is the BPF instructions to be compiled.
- insns []linux.BPFInstruction
-
- // input is the input data. Note that input will be read as big-endian.
- input []byte
-
- // expectedRet is the expected return value of the BPF program.
- expectedRet uint32
- }{
- {
- desc: "Return of immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ret|K, 42), // return 42
- },
- expectedRet: 42,
- },
- {
- desc: "Load of immediate into A",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 42), // A = 42
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 42,
- },
- {
- desc: "Load of immediate into X and copying of X into A",
- insns: []linux.BPFInstruction{
- Stmt(Ldx|Imm|W, 42), // X = 42
- Stmt(Misc|Tax, 0), // A = X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 42,
- },
- {
- desc: "Copying of A into X and back",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 42), // A = 42
- Stmt(Misc|Txa, 0), // X = A
- Stmt(Ld|Imm|W, 0), // A = 0
- Stmt(Misc|Tax, 0), // A = X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 42,
- },
- {
- desc: "Load of 32-bit input by absolute offset into A",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Abs|W, 1), // A = input[1..4]
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{0x00, 0x11, 0x22, 0x33, 0x44},
- expectedRet: 0x11223344,
- },
- {
- desc: "Load of 16-bit input by absolute offset into A",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Abs|H, 1), // A = input[1..2]
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{0x00, 0x11, 0x22},
- expectedRet: 0x1122,
- },
- {
- desc: "Load of 8-bit input by absolute offset into A",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Abs|B, 1), // A = input[1]
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{0x00, 0x11},
- expectedRet: 0x11,
- },
- {
- desc: "Load of 32-bit input by relative offset into A",
- insns: []linux.BPFInstruction{
- Stmt(Ldx|Imm|W, 1), // X = 1
- Stmt(Ld|Ind|W, 1), // A = input[X+1..X+4]
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{0x00, 0x11, 0x22, 0x33, 0x44, 0x55},
- expectedRet: 0x22334455,
- },
- {
- desc: "Load of 16-bit input by relative offset into A",
- insns: []linux.BPFInstruction{
- Stmt(Ldx|Imm|W, 1), // X = 1
- Stmt(Ld|Ind|H, 1), // A = input[X+1..X+2]
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{0x00, 0x11, 0x22, 0x33},
- expectedRet: 0x2233,
- },
- {
- desc: "Load of 8-bit input by relative offset into A",
- insns: []linux.BPFInstruction{
- Stmt(Ldx|Imm|W, 1), // X = 1
- Stmt(Ld|Ind|B, 1), // A = input[X+1]
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{0x00, 0x11, 0x22},
- expectedRet: 0x22,
- },
- {
- desc: "Load/store between A and scratch memory",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 42), // A = 42
- Stmt(St, 2), // M[2] = A
- Stmt(Ld|Imm|W, 0), // A = 0
- Stmt(Ld|Mem|W, 2), // A = M[2]
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 42,
- },
- {
- desc: "Load/store between X and scratch memory",
- insns: []linux.BPFInstruction{
- Stmt(Ldx|Imm|W, 42), // X = 42
- Stmt(Stx, 3), // M[3] = X
- Stmt(Ldx|Imm|W, 0), // X = 0
- Stmt(Ldx|Mem|W, 3), // X = M[3]
- Stmt(Misc|Tax, 0), // A = X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 42,
- },
- {
- desc: "Load of input length into A",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Len|W, 0), // A = len(input)
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{1, 2, 3},
- expectedRet: 3,
- },
- {
- desc: "Load of input length into X",
- insns: []linux.BPFInstruction{
- Stmt(Ldx|Len|W, 0), // X = len(input)
- Stmt(Misc|Tax, 0), // A = X
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{1, 2, 3},
- expectedRet: 3,
- },
- {
- desc: "Load of MSH (?) into X",
- insns: []linux.BPFInstruction{
- Stmt(Ldx|Msh|B, 0), // X = 4*(input[0]&0xf)
- Stmt(Misc|Tax, 0), // A = X
- Stmt(Ret|A, 0), // return A
- },
- input: []byte{0xf1},
- expectedRet: 4,
- },
- {
- desc: "Addition of immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Stmt(Alu|Add|K, 20), // A += 20
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 30,
- },
- {
- desc: "Addition of X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Stmt(Ldx|Imm|W, 20), // X = 20
- Stmt(Alu|Add|X, 0), // A += X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 30,
- },
- {
- desc: "Subtraction of immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 30), // A = 30
- Stmt(Alu|Sub|K, 20), // A -= 20
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 10,
- },
- {
- desc: "Subtraction of X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 30), // A = 30
- Stmt(Ldx|Imm|W, 20), // X = 20
- Stmt(Alu|Sub|X, 0), // A -= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 10,
- },
- {
- desc: "Multiplication of immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 2), // A = 2
- Stmt(Alu|Mul|K, 3), // A *= 3
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 6,
- },
- {
- desc: "Multiplication of X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 2), // A = 2
- Stmt(Ldx|Imm|W, 3), // X = 3
- Stmt(Alu|Mul|X, 0), // A *= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 6,
- },
- {
- desc: "Division by immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 6), // A = 6
- Stmt(Alu|Div|K, 3), // A /= 3
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 2,
- },
- {
- desc: "Division by X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 6), // A = 6
- Stmt(Ldx|Imm|W, 3), // X = 3
- Stmt(Alu|Div|X, 0), // A /= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 2,
- },
- {
- desc: "Modulo immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 17), // A = 17
- Stmt(Alu|Mod|K, 7), // A %= 7
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 3,
- },
- {
- desc: "Modulo X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 17), // A = 17
- Stmt(Ldx|Imm|W, 7), // X = 7
- Stmt(Alu|Mod|X, 0), // A %= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 3,
- },
- {
- desc: "Arithmetic negation",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 1), // A = 1
- Stmt(Alu|Neg, 0), // A = -A
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 0xffffffff,
- },
- {
- desc: "Bitwise OR with immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
- Stmt(Alu|Or|K, 0xff0055aa), // A |= 0xff0055aa
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 0xff00ffff,
- },
- {
- desc: "Bitwise OR with X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
- Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
- Stmt(Alu|Or|X, 0), // A |= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 0xff00ffff,
- },
- {
- desc: "Bitwise AND with immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
- Stmt(Alu|And|K, 0xff0055aa), // A &= 0xff0055aa
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 0xff000000,
- },
- {
- desc: "Bitwise AND with X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
- Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
- Stmt(Alu|And|X, 0), // A &= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 0xff000000,
- },
- {
- desc: "Bitwise XOR with immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
- Stmt(Alu|Xor|K, 0xff0055aa), // A ^= 0xff0055aa
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 0x0000ffff,
- },
- {
- desc: "Bitwise XOR with X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff00aa55), // A = 0xff00aa55
- Stmt(Ldx|Imm|W, 0xff0055aa), // X = 0xff0055aa
- Stmt(Alu|Xor|X, 0), // A ^= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 0x0000ffff,
- },
- {
- desc: "Left shift by immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 1), // A = 1
- Stmt(Alu|Lsh|K, 5), // A <<= 5
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 32,
- },
- {
- desc: "Left shift by X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 1), // A = 1
- Stmt(Ldx|Imm|W, 5), // X = 5
- Stmt(Alu|Lsh|X, 0), // A <<= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 32,
- },
- {
- desc: "Right shift by immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff
- Stmt(Alu|Rsh|K, 31), // A >>= 31
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 1,
- },
- {
- desc: "Right shift by X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xffffffff), // A = 0xffffffff
- Stmt(Ldx|Imm|W, 31), // X = 31
- Stmt(Alu|Rsh|X, 0), // A >>= X
- Stmt(Ret|A, 0), // return A
- },
- expectedRet: 1,
- },
- {
- desc: "Unconditional jump",
- insns: []linux.BPFInstruction{
- Jump(Jmp|Ja, 1, 0, 0), // jmp nextpc+1
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A == immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 42), // A = 42
- Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A != immediate",
- insns: []linux.BPFInstruction{
- Jump(Jmp|Jeq|K, 42, 1, 2), // if (A == 42) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- {
- desc: "Jump when A == X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 42), // A = 42
- Stmt(Ldx|Imm|W, 42), // X = 42
- Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A != X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 42), // A = 42
- Jump(Jmp|Jeq|X, 0, 1, 2), // if (A == X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- {
- desc: "Jump when A > immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Jump(Jmp|Jgt|K, 9, 1, 2), // if (A > 9) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A <= immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Jump(Jmp|Jgt|K, 10, 1, 2), // if (A > 10) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- {
- desc: "Jump when A > X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Stmt(Ldx|Imm|W, 9), // X = 9
- Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A <= X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Stmt(Ldx|Imm|W, 10), // X = 10
- Jump(Jmp|Jgt|X, 0, 1, 2), // if (A > X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- {
- desc: "Jump when A >= immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Jump(Jmp|Jge|K, 10, 1, 2), // if (A >= 10) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A < immediate",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Jump(Jmp|Jge|K, 11, 1, 2), // if (A >= 11) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- {
- desc: "Jump when A >= X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Stmt(Ldx|Imm|W, 10), // X = 10
- Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A < X",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 10), // A = 10
- Stmt(Ldx|Imm|W, 11), // X = 11
- Jump(Jmp|Jge|X, 0, 1, 2), // if (A >= X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- {
- desc: "Jump when A & immediate != 0",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff), // A = 0xff
- Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A & immediate == 0",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xfe), // A = 0xfe
- Jump(Jmp|Jset|K, 0x101, 1, 2), // if (A & 0x101) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- {
- desc: "Jump when A & X != 0",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xff), // A = 0xff
- Stmt(Ldx|Imm|W, 0x101), // X = 0x101
- Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 1,
- },
- {
- desc: "Jump when A & X == 0",
- insns: []linux.BPFInstruction{
- Stmt(Ld|Imm|W, 0xfe), // A = 0xfe
- Stmt(Ldx|Imm|W, 0x101), // X = 0x101
- Jump(Jmp|Jset|X, 0, 1, 2), // if (A & X) jmp nextpc+1 else jmp nextpc+2
- Stmt(Ret|K, 0), // return 0
- Stmt(Ret|K, 1), // return 1
- Stmt(Ret|K, 2), // return 2
- },
- expectedRet: 2,
- },
- } {
- p, err := Compile(test.insns)
- if err != nil {
- t.Errorf("%s: unexpected compilation error: %v", test.desc, err)
- continue
- }
- ret, err := Exec(p, InputBytes{test.input, binary.BigEndian})
- if err != nil {
- t.Errorf("%s: expected return value of %d, got execution error: %v", test.desc, test.expectedRet, err)
- continue
- }
- if ret != test.expectedRet {
- t.Errorf("%s: expected return value of %d, got value %d", test.desc, test.expectedRet, ret)
- }
- }
-}
-
-func TestSimpleFilter(t *testing.T) {
- // Seccomp filter example given in Linux's
- // Documentation/networking/filter.txt, translated to bytecode using the
- // Linux kernel tree's tools/net/bpf_asm.
- filter := []linux.BPFInstruction{
- {0x20, 0, 0, 0x00000004}, // ld [4] /* offsetof(struct seccomp_data, arch) */
- {0x15, 0, 11, 0xc000003e}, // jne #0xc000003e, bad /* AUDIT_ARCH_X86_64 */
- {0x20, 0, 0, 0000000000}, // ld [0] /* offsetof(struct seccomp_data, nr) */
- {0x15, 10, 0, 0x0000000f}, // jeq #15, good /* __NR_rt_sigreturn */
- {0x15, 9, 0, 0x000000e7}, // jeq #231, good /* __NR_exit_group */
- {0x15, 8, 0, 0x0000003c}, // jeq #60, good /* __NR_exit */
- {0x15, 7, 0, 0000000000}, // jeq #0, good /* __NR_read */
- {0x15, 6, 0, 0x00000001}, // jeq #1, good /* __NR_write */
- {0x15, 5, 0, 0x00000005}, // jeq #5, good /* __NR_fstat */
- {0x15, 4, 0, 0x00000009}, // jeq #9, good /* __NR_mmap */
- {0x15, 3, 0, 0x0000000e}, // jeq #14, good /* __NR_rt_sigprocmask */
- {0x15, 2, 0, 0x0000000d}, // jeq #13, good /* __NR_rt_sigaction */
- {0x15, 1, 0, 0x00000023}, // jeq #35, good /* __NR_nanosleep */
- {0x06, 0, 0, 0000000000}, // bad: ret #0 /* SECCOMP_RET_KILL */
- {0x06, 0, 0, 0x7fff0000}, // good: ret #0x7fff0000 /* SECCOMP_RET_ALLOW */
- }
- p, err := Compile(filter)
- if err != nil {
- t.Fatalf("Unexpected compilation error: %v", err)
- }
-
- for _, test := range []struct {
- // desc is the test's description.
- desc string
-
- // seccompData is the input data.
- seccompData
-
- // expectedRet is the expected return value of the BPF program.
- expectedRet uint32
- }{
- {
- desc: "Invalid arch is rejected",
- seccompData: seccompData{nr: 1 /* x86 exit */, arch: 0x40000003 /* AUDIT_ARCH_I386 */},
- expectedRet: 0,
- },
- {
- desc: "Disallowed syscall is rejected",
- seccompData: seccompData{nr: 105 /* __NR_setuid */, arch: 0xc000003e},
- expectedRet: 0,
- },
- {
- desc: "Allowed syscall is indeed allowed",
- seccompData: seccompData{nr: 231 /* __NR_exit_group */, arch: 0xc000003e},
- expectedRet: 0x7fff0000,
- },
- } {
- ret, err := Exec(p, test.seccompData.asInput())
- if err != nil {
- t.Errorf("%s: expected return value of %d, got execution error: %v", test.desc, test.expectedRet, err)
- continue
- }
- if ret != test.expectedRet {
- t.Errorf("%s: expected return value of %d, got value %d", test.desc, test.expectedRet, ret)
- }
- }
-}
-
-// seccompData is equivalent to struct seccomp_data.
-type seccompData struct {
- nr uint32
- arch uint32
- instructionPointer uint64
- args [6]uint64
-}
-
-// asInput converts a seccompData to a bpf.Input.
-func (d *seccompData) asInput() Input {
- return InputBytes{binary.Marshal(nil, binary.LittleEndian, d), binary.LittleEndian}
-}
diff --git a/pkg/bpf/program_builder_test.go b/pkg/bpf/program_builder_test.go
deleted file mode 100644
index 37f684f25..000000000
--- a/pkg/bpf/program_builder_test.go
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2018 The gVisor Authors.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package bpf
-
-import (
- "fmt"
- "testing"
-
- "gvisor.dev/gvisor/pkg/abi/linux"
-)
-
-func validate(p *ProgramBuilder, expected []linux.BPFInstruction) error {
- instructions, err := p.Instructions()
- if err != nil {
- return fmt.Errorf("Instructions() failed: %v", err)
- }
- got, err := DecodeInstructions(instructions)
- if err != nil {
- return fmt.Errorf("DecodeInstructions('instructions') failed: %v", err)
- }
- expectedDecoded, err := DecodeInstructions(expected)
- if err != nil {
- return fmt.Errorf("DecodeInstructions('expected') failed: %v", err)
- }
- if got != expectedDecoded {
- return fmt.Errorf("DecodeInstructions() failed, expected: %q, got: %q", expectedDecoded, got)
- }
- return nil
-}
-
-func TestProgramBuilderSimple(t *testing.T) {
- p := NewProgramBuilder()
- p.AddStmt(Ld+Abs+W, 10)
- p.AddJump(Jmp+Ja, 10, 0, 0)
-
- expected := []linux.BPFInstruction{
- Stmt(Ld+Abs+W, 10),
- Jump(Jmp+Ja, 10, 0, 0),
- }
-
- if err := validate(p, expected); err != nil {
- t.Errorf("Validate() failed: %v", err)
- }
-}
-
-func TestProgramBuilderLabels(t *testing.T) {
- p := NewProgramBuilder()
- p.AddJumpTrueLabel(Jmp+Jeq+K, 11, "label_1", 0)
- p.AddJumpFalseLabel(Jmp+Jeq+K, 12, 0, "label_2")
- p.AddJumpLabels(Jmp+Jeq+K, 13, "label_3", "label_4")
- if err := p.AddLabel("label_1"); err != nil {
- t.Errorf("AddLabel(label_1) failed: %v", err)
- }
- p.AddStmt(Ld+Abs+W, 1)
- if err := p.AddLabel("label_3"); err != nil {
- t.Errorf("AddLabel(label_3) failed: %v", err)
- }
- p.AddJumpLabels(Jmp+Jeq+K, 14, "label_4", "label_5")
- if err := p.AddLabel("label_2"); err != nil {
- t.Errorf("AddLabel(label_2) failed: %v", err)
- }
- p.AddJumpLabels(Jmp+Jeq+K, 15, "label_4", "label_6")
- if err := p.AddLabel("label_4"); err != nil {
- t.Errorf("AddLabel(label_4) failed: %v", err)
- }
- p.AddStmt(Ld+Abs+W, 4)
- if err := p.AddLabel("label_5"); err != nil {
- t.Errorf("AddLabel(label_5) failed: %v", err)
- }
- if err := p.AddLabel("label_6"); err != nil {
- t.Errorf("AddLabel(label_6) failed: %v", err)
- }
- p.AddStmt(Ld+Abs+W, 5)
-
- expected := []linux.BPFInstruction{
- Jump(Jmp+Jeq+K, 11, 2, 0),
- Jump(Jmp+Jeq+K, 12, 0, 3),
- Jump(Jmp+Jeq+K, 13, 1, 3),
- Stmt(Ld+Abs+W, 1),
- Jump(Jmp+Jeq+K, 14, 1, 2),
- Jump(Jmp+Jeq+K, 15, 0, 1),
- Stmt(Ld+Abs+W, 4),
- Stmt(Ld+Abs+W, 5),
- }
- if err := validate(p, expected); err != nil {
- t.Errorf("Validate() failed: %v", err)
- }
- // Calling validate()=>p.Instructions() again to make sure
- // Instructions can be called multiple times without ruining
- // the program.
- if err := validate(p, expected); err != nil {
- t.Errorf("Validate() failed: %v", err)
- }
-}
-
-func TestProgramBuilderMissingErrorTarget(t *testing.T) {
- p := NewProgramBuilder()
- p.AddJumpTrueLabel(Jmp+Jeq+K, 10, "label_1", 0)
- if _, err := p.Instructions(); err == nil {
- t.Errorf("Instructions() should have failed")
- }
-}
-
-func TestProgramBuilderLabelWithNoInstruction(t *testing.T) {
- p := NewProgramBuilder()
- p.AddJumpTrueLabel(Jmp+Jeq+K, 10, "label_1", 0)
- if err := p.AddLabel("label_1"); err != nil {
- t.Errorf("AddLabel(label_1) failed: %v", err)
- }
- if _, err := p.Instructions(); err == nil {
- t.Errorf("Instructions() should have failed")
- }
-}
-
-// TestProgramBuilderUnusedLabel tests that adding an unused label doesn't
-// cause program generation to fail.
-func TestProgramBuilderUnusedLabel(t *testing.T) {
- p := NewProgramBuilder()
- p.AddStmt(Ld+Abs+W, 10)
- p.AddJump(Jmp+Ja, 10, 0, 0)
-
- expected := []linux.BPFInstruction{
- Stmt(Ld+Abs+W, 10),
- Jump(Jmp+Ja, 10, 0, 0),
- }
-
- if err := p.AddLabel("unused"); err != nil {
- t.Errorf("AddLabel(unused) should have succeeded")
- }
-
- if err := validate(p, expected); err != nil {
- t.Errorf("Validate() failed: %v", err)
- }
-}
-
-// TestProgramBuilderBackwardsReference tests that including a backwards
-// reference to a label in a program causes a failure.
-func TestProgramBuilderBackwardsReference(t *testing.T) {
- p := NewProgramBuilder()
- if err := p.AddLabel("bw_label"); err != nil {
- t.Errorf("failed to add label")
- }
- p.AddStmt(Ld+Abs+W, 10)
- p.AddJumpTrueLabel(Jmp+Jeq+K, 10, "bw_label", 0)
- if _, err := p.Instructions(); err == nil {
- t.Errorf("Instructions() should have failed")
- }
-}
-
-func TestProgramBuilderLabelAddedTwice(t *testing.T) {
- p := NewProgramBuilder()
- p.AddJumpTrueLabel(Jmp+Jeq+K, 10, "label_1", 0)
- if err := p.AddLabel("label_1"); err != nil {
- t.Errorf("AddLabel(label_1) failed: %v", err)
- }
- p.AddStmt(Ld+Abs+W, 0)
- if err := p.AddLabel("label_1"); err == nil {
- t.Errorf("AddLabel(label_1) failed: %v", err)
- }
-}
-
-func TestProgramBuilderJumpBackwards(t *testing.T) {
- p := NewProgramBuilder()
- p.AddJumpTrueLabel(Jmp+Jeq+K, 10, "label_1", 0)
- if err := p.AddLabel("label_1"); err != nil {
- t.Errorf("AddLabel(label_1) failed: %v", err)
- }
- p.AddStmt(Ld+Abs+W, 0)
- p.AddJumpTrueLabel(Jmp+Jeq+K, 10, "label_1", 0)
- if _, err := p.Instructions(); err == nil {
- t.Errorf("Instructions() should have failed")
- }
-}