diff options
Diffstat (limited to 'pkg/bpf')
-rw-r--r-- | pkg/bpf/BUILD | 31 | ||||
-rw-r--r-- | pkg/bpf/bpf_state_autogen.go | 36 | ||||
-rw-r--r-- | pkg/bpf/decoder_test.go | 146 | ||||
-rw-r--r-- | pkg/bpf/interpreter_test.go | 797 | ||||
-rw-r--r-- | pkg/bpf/program_builder_test.go | 185 |
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") - } -} |