forked from rlane/ubpf
-
Notifications
You must be signed in to change notification settings - Fork 129
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
233 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import os | ||
import tempfile | ||
import struct | ||
from subprocess import Popen, PIPE | ||
from nose.plugins.skip import Skip, SkipTest | ||
import ubpf.assembler | ||
import testdata | ||
VM = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "vm", "test") | ||
|
||
def check_datafile(filename): | ||
""" | ||
Given assembly source code and an expected result, run the eBPF program and | ||
verify that the result matches. | ||
""" | ||
data = testdata.read(filename) | ||
if 'asm' not in data and 'raw' not in data: | ||
raise SkipTest("no asm or raw section in datafile") | ||
if 'result' not in data and 'verifier error' not in data: | ||
raise SkipTest("no result or verifier error section in datafile") | ||
if 'error' in data or 'error pattern' in data: | ||
raise SkipTest("non-verifier error section in datafile") | ||
if not os.path.exists(VM): | ||
raise SkipTest("VM not found") | ||
|
||
if 'raw' in data: | ||
code = b''.join(struct.pack("=Q", x) for x in data['raw']) | ||
else: | ||
code = ubpf.assembler.assemble(data['asm']) | ||
|
||
memfile = None | ||
|
||
cmd = [VM] | ||
if 'mem' in data: | ||
memfile = tempfile.NamedTemporaryFile() | ||
memfile.write(data['mem']) | ||
memfile.flush() | ||
cmd.extend(['-m', memfile.name]) | ||
|
||
cmd.extend(['-V', '-']) | ||
|
||
vm = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) | ||
|
||
stdout, stderr = vm.communicate(code) | ||
stdout = stdout.decode("utf-8") | ||
stderr = stderr.decode("utf-8") | ||
stderr = stderr.strip() | ||
|
||
if memfile: | ||
memfile.close() | ||
|
||
if 'verifier error' in data: | ||
if data['verifier error'] + '\nFailed verification' != stderr: | ||
raise AssertionError("Expected error %r, got %r" % (data['verifier error'] + '\nFailed verification', stderr)) | ||
if vm.returncode == 0: | ||
raise AssertionError("Expected VM to exit with an error code") | ||
elif 'error' in data or 'error-pattern' in data: | ||
if vm.returncode == 0: | ||
raise AssertionError("Expected VM to exit with an error code") | ||
else: | ||
if vm.returncode != 0: | ||
raise AssertionError("VM exited with status %d, stderr=%r" % (vm.returncode, stderr)) | ||
|
||
def test_datafiles(): | ||
# Nose test generator | ||
# Creates a testcase for each datafile | ||
for filename in testdata.list_files(): | ||
yield check_datafile, filename |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,3 +5,6 @@ mov r0, 4 | |
exit | ||
-- result | ||
0x3 | ||
-- verifier error | ||
Dead instruction at offset 2 | ||
Dead instruction at offset 3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,3 +25,5 @@ jne r1, 0x0, -3 | |
exit | ||
-- result | ||
0x75db9c97 | ||
-- verifier error | ||
Loop detected at offset 8 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,3 +32,5 @@ jne r4, 0x0, -10 | |
exit | ||
-- result | ||
0x1 | ||
-- verifier error | ||
Loop detected at offset 14 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ | |
-- mem @ pkt-sack.hex | ||
-- result | ||
0x1 | ||
-- verifier error | ||
Loop detected at offset 40 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,3 +2,5 @@ | |
-- mem @ pkt-nosack.hex | ||
-- result | ||
0x0 | ||
-- verifier error | ||
Loop detected at offset 40 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/* | ||
* Copyright 2020 Julian P. Samaroo | ||
* | ||
* 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. | ||
*/ | ||
|
||
#define _GNU_SOURCE | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <stdbool.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <inttypes.h> | ||
#include <sys/mman.h> | ||
#include <errno.h> | ||
#include <assert.h> | ||
#include "ubpf_int.h" | ||
#include "ebpf.h" | ||
|
||
typedef int (*WALKER)(struct ubpf_vm *vm, struct ebpf_inst inst, void *data, int inst_off, char *visited); | ||
|
||
enum ubpf_walk_action | ||
{ | ||
UBPF_WALK_CONTINUE, | ||
UBPF_WALK_STOP, | ||
UBPF_WALK_INVALID, | ||
}; | ||
|
||
int isjmp(struct ebpf_inst inst) | ||
{ | ||
if (((inst.opcode & EBPF_CLS_MASK) == EBPF_CLS_JMP) && (inst.opcode != EBPF_OP_CALL)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
int | ||
ubpf_walk_paths(struct ubpf_vm *vm, WALKER walk_fn, void *data, int inst_off, char *visited) | ||
{ | ||
struct ebpf_inst inst = vm->insts[inst_off]; | ||
int cmd = walk_fn(vm, inst, data, inst_off, visited); | ||
visited[inst_off] = 1; | ||
if (cmd != UBPF_WALK_CONTINUE) | ||
return cmd; | ||
if (inst.opcode == EBPF_OP_EXIT) { | ||
return UBPF_WALK_CONTINUE; | ||
} else if (isjmp(inst)) { | ||
int next_pc = inst_off+1+inst.offset; | ||
if (next_pc == inst_off) { | ||
fprintf(stderr, "Jump to self at offset %d\n", inst_off); | ||
return UBPF_WALK_INVALID; | ||
} else if ((next_pc < 0) || (next_pc > vm->num_insts-1)) { | ||
fprintf(stderr, "Jump out-of-bounds at offset %d to %d\n", inst_off, next_pc); | ||
return UBPF_WALK_INVALID; | ||
} | ||
if (visited[next_pc] == 0) { | ||
cmd = ubpf_walk_paths(vm, walk_fn, data, next_pc, visited); | ||
if (cmd == UBPF_WALK_STOP || cmd == UBPF_WALK_INVALID) | ||
return cmd; | ||
} | ||
} | ||
if (inst_off == vm->num_insts-1) { | ||
return UBPF_WALK_CONTINUE; | ||
} else { | ||
return ubpf_walk_paths(vm, walk_fn, data, inst_off+1, visited); | ||
} | ||
} | ||
|
||
int | ||
ubpf_walk_start(struct ubpf_vm *vm, WALKER walk_fn, void *data) | ||
{ | ||
char visited[vm->num_insts]; | ||
memset((void *)visited, 0, vm->num_insts); | ||
return ubpf_walk_paths(vm, walk_fn, data, 0, visited); | ||
} | ||
|
||
int | ||
_walker_no_dead_insts(struct ubpf_vm *vm, struct ebpf_inst inst, void *data, int inst_off, char *visited) | ||
{ | ||
return UBPF_WALK_CONTINUE; | ||
} | ||
|
||
int | ||
ubpf_verify_no_dead_insts(struct ubpf_vm *vm) | ||
{ | ||
char visited[vm->num_insts]; | ||
memset((void *)visited, 0, vm->num_insts); | ||
int ret = ubpf_walk_paths(vm, _walker_no_dead_insts, NULL, 0, visited); | ||
if (ret) | ||
return ret; | ||
int any_dead = 0; | ||
for (int i = 0; i < vm->num_insts; i++) { | ||
if (visited[i] == 0) { | ||
any_dead = 1; | ||
fprintf(stderr, "Dead instruction at offset %d\n", i); | ||
} | ||
} | ||
return any_dead; | ||
} | ||
|
||
int | ||
_walker_no_loops(struct ubpf_vm *vm, struct ebpf_inst inst, void *data, int inst_off, char *visited) | ||
{ | ||
if (isjmp(inst) && (inst_off+1+inst.offset < inst_off) && visited[inst_off+1+inst.offset]) { | ||
fprintf(stderr, "Loop detected at offset %d\n", inst_off); | ||
return UBPF_WALK_STOP; | ||
} | ||
return UBPF_WALK_CONTINUE; | ||
} | ||
|
||
int | ||
ubpf_verify_no_loops(struct ubpf_vm *vm) | ||
{ | ||
return ubpf_walk_start(vm, _walker_no_loops, NULL); | ||
} | ||
|
||
int | ||
ubpf_verify(struct ubpf_vm *vm) | ||
{ | ||
if (ubpf_verify_no_loops(vm)) | ||
return 1; | ||
if (ubpf_verify_no_dead_insts(vm)) | ||
return 1; | ||
return 0; | ||
} |