├── ubpf ├── __init__.py ├── asm_parser.py ├── assembler.py └── disassembler.py ├── tests ├── tcp-sack │ ├── .gitignore │ ├── match.data │ ├── nomatch.data │ ├── pkt-nosack.hex │ ├── Makefile │ ├── pkt-sack.hex │ ├── tcp-sack.asm │ └── tcp-sack.c ├── elf │ ├── ok.data │ ├── too-many-sections.data │ ├── wrong-version.data │ ├── bad-rel-offset.data │ ├── bad-rel-symbol-name.data │ ├── wrong-class.data │ ├── wrong-type.data │ ├── bad-rel-type.data │ ├── bad-section-size.data │ ├── rel-sym-not-found.data │ ├── wrong-osabi.data │ ├── bad-rel-symbol-index.data │ ├── bad-section-offset.data │ ├── no-text-section.data │ ├── wrong-byte-order.data │ ├── bad-rel-strtab-index.data │ ├── wrong-machine.data │ ├── bad-section-header-offset.data │ ├── bad-section-header-size.data │ ├── bad-rel-symbol-table-section-index.data │ └── ehdr-short.data ├── exit.data ├── ja.data ├── mov.data ├── mul32-imm.data ├── neg.data ├── div32-imm.data ├── early-exit.data ├── mod32.data ├── neg64.data ├── lddw2.data ├── ldxb.data ├── lsh-reg.data ├── mul32-reg.data ├── mul64-imm.data ├── rsh-reg.data ├── be16.data ├── ldxh.data ├── le16.data ├── rsh32.data ├── div32-reg.data ├── div64-imm.data ├── err-infinite-loop.data ├── err-jmp-out.data ├── add.data ├── be32.data ├── ldxw.data ├── le32.data ├── mul64-reg.data ├── arsh32-high-shift.data ├── div32-high-divisor.data ├── mul32-reg-overflow.data ├── stb.data ├── div64-reg.data ├── err-write-r10.dst ├── be16-high.data ├── err-invalid-reg-src.data ├── exit-not-last.data ├── sth.data ├── be32-high.data ├── be64.data ├── err-invalid-reg-dst.data ├── le64.data ├── err-div-by-zero-imm.data ├── ldxdw.data ├── err-incomplete-lddw.data ├── err-incomplete-lddw2.data ├── err-unknown-opcode.data ├── stw.data ├── arsh.data ├── err-jmp-lddw.data ├── ldxh-same-reg.data ├── stxb.data ├── arsh64.data ├── stxh.data ├── arsh-reg.data ├── stdw.data ├── err-endian-size.data ├── mod.data ├── stxw.data ├── jit-bounce.data ├── lddw.data ├── err-div-by-zero-reg.data ├── err-div64-by-zero-reg.data ├── err-mod-by-zero-reg.data ├── err-mod64-by-zero-reg.data ├── call.data ├── err-call-bad-imm.data ├── err-call-unreg.data ├── jgt-imm.data ├── jlt-imm.data ├── stxb-all2.data ├── jle-imm.data ├── jeq-imm.data ├── stxdw.data ├── jgt-reg.data ├── jlt-reg.data ├── err-stack-oob.data ├── jge-imm.data ├── jle-reg.data ├── jset-imm.data ├── jslt-imm.data ├── jeq-reg.data ├── jne-reg.data ├── call-memfrob.data ├── jset-reg.data ├── jsgt-imm.data ├── jsle-imm.data ├── jsgt-reg.data ├── stx.data ├── jslt-reg.data ├── st.data ├── call-save.data ├── stack.data ├── jsge-imm.data ├── jsle-reg.data ├── jsge-reg.data ├── mod64.data ├── tcp-port-80 │ ├── tcp-port-80.asm │ ├── match.data │ ├── nomatch-ethertype.data │ ├── nomatch-proto.data │ └── nomatch.data ├── ldx.data ├── alu64-arith.data ├── stxb-all.data ├── alu-arith.data ├── mul-loop.data ├── stack2.data ├── alu64-bit.data ├── alu-bit.data ├── stxb-chain.data ├── jmp.data ├── prime.data ├── alu64.data ├── ldxb-all.data ├── ldxh-all2.data ├── ldxw-all.data ├── ldxh-all.data ├── alu.data ├── string-stack.data └── subnet.data ├── vm ├── .gitignore ├── ubpf_int.h ├── Makefile ├── inc │ └── ubpf.h ├── test.c ├── ebpf.h ├── ubpf_loader.c ├── ubpf_jit_x86_64.h ├── ubpf_jit_x86_64.c └── ubpf_vm.c ├── requirements.txt ├── .travis.yml ├── bin ├── ubpf-assembler └── ubpf-disassembler ├── test_framework ├── test_disassembler.py ├── test_assembler.py ├── test_roundtrip.py ├── expand-testcase.py ├── test_vm.py ├── testdata.py ├── test_jit.py └── test_elf.py ├── .gitignore ├── README.md └── LICENSE-APACHE /ubpf/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/tcp-sack/.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.bin 3 | -------------------------------------------------------------------------------- /tests/elf/ok.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | -- result 3 | 0x2a 4 | -------------------------------------------------------------------------------- /tests/exit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0 3 | exit 4 | -- result 5 | 0x0 6 | -------------------------------------------------------------------------------- /vm/.gitignore: -------------------------------------------------------------------------------- 1 | libubpf.a 2 | test 3 | *.o 4 | *.gcov 5 | *.gcda 6 | *.gcno 7 | -------------------------------------------------------------------------------- /tests/ja.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 1 3 | ja +1 4 | mov r0, 2 5 | exit 6 | -- result 7 | 0x1 8 | -------------------------------------------------------------------------------- /tests/mov.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r1, 1 3 | mov32 r0, r1 4 | exit 5 | -- result 6 | 0x1 7 | -------------------------------------------------------------------------------- /tests/mul32-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 3 3 | mul32 r0, 4 4 | exit 5 | -- result 6 | 0xc 7 | -------------------------------------------------------------------------------- /tests/neg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 2 3 | neg32 r0 4 | exit 5 | -- result 6 | 0xfffffffe 7 | -------------------------------------------------------------------------------- /tests/div32-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw r0, 0x10000000c 3 | div32 r0, 4 4 | exit 5 | -- result 6 | 0x3 7 | -------------------------------------------------------------------------------- /tests/early-exit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 3 3 | exit 4 | mov r0, 4 5 | exit 6 | -- result 7 | 0x3 8 | -------------------------------------------------------------------------------- /tests/mod32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw r0, 0x100000003 3 | mod32 r0, 3 4 | exit 5 | -- result 6 | 0x0 7 | -------------------------------------------------------------------------------- /tests/neg64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 2 3 | neg r0 4 | exit 5 | -- result 6 | 0xfffffffffffffffe 7 | -------------------------------------------------------------------------------- /tests/tcp-sack/match.data: -------------------------------------------------------------------------------- 1 | -- asm @ tcp-sack.asm 2 | -- mem @ pkt-sack.hex 3 | -- result 4 | 0x1 5 | -------------------------------------------------------------------------------- /tests/tcp-sack/nomatch.data: -------------------------------------------------------------------------------- 1 | -- asm @ tcp-sack.asm 2 | -- mem @ pkt-nosack.hex 3 | -- result 4 | 0x0 5 | -------------------------------------------------------------------------------- /tests/lddw2.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw r0, 0x0000000080000000 3 | exit 4 | -- result 5 | 0x0000000080000000 6 | -------------------------------------------------------------------------------- /tests/ldxb.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxb r0, [r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 cc dd 6 | -- result 7 | 0x11 8 | -------------------------------------------------------------------------------- /tests/lsh-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0x1 3 | mov r7, 4 4 | lsh r0, r7 5 | exit 6 | -- result 7 | 0x10 8 | -------------------------------------------------------------------------------- /tests/mul32-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 3 3 | mov r1, 4 4 | mul32 r0, r1 5 | exit 6 | -- result 7 | 0xc 8 | -------------------------------------------------------------------------------- /tests/mul64-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0x40000001 3 | mul r0, 4 4 | exit 5 | -- result 6 | 0x100000004 7 | -------------------------------------------------------------------------------- /tests/rsh-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0x10 3 | mov r7, 4 4 | rsh r0, r7 5 | exit 6 | -- result 7 | 0x1 8 | -------------------------------------------------------------------------------- /tests/be16.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxh r0, [r1] 3 | be16 r0 4 | exit 5 | -- mem 6 | 11 22 7 | -- result 8 | 0x1122 9 | -------------------------------------------------------------------------------- /tests/ldxh.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxh r0, [r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 22 cc dd 6 | -- result 7 | 0x2211 8 | -------------------------------------------------------------------------------- /tests/le16.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxh r0, [r1] 3 | le16 r0 4 | exit 5 | -- mem 6 | 22 11 7 | -- result 8 | 0x1122 9 | -------------------------------------------------------------------------------- /tests/rsh32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | xor r0, r0 3 | sub r0, 1 4 | rsh32 r0, 8 5 | exit 6 | -- result 7 | 0x00ffffff 8 | -------------------------------------------------------------------------------- /tests/div32-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw r0, 0x10000000c 3 | mov r1, 4 4 | div32 r0, r1 5 | exit 6 | -- result 7 | 0x3 8 | -------------------------------------------------------------------------------- /tests/div64-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0xc 3 | lsh r0, 32 4 | div r0, 4 5 | exit 6 | -- result 7 | 0x300000000 8 | -------------------------------------------------------------------------------- /tests/err-infinite-loop.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja -1 3 | exit 4 | -- error 5 | Failed to load code: infinite loop at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-jmp-out.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja +2 3 | exit 4 | -- error 5 | Failed to load code: jump out of bounds at PC 0 6 | -------------------------------------------------------------------------------- /tests/add.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 2 4 | add32 r0, 1 5 | add32 r0, r1 6 | exit 7 | -- result 8 | 0x3 9 | -------------------------------------------------------------------------------- /tests/be32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw r0, [r1] 3 | be32 r0 4 | exit 5 | -- mem 6 | 11 22 33 44 7 | -- result 8 | 0x11223344 9 | -------------------------------------------------------------------------------- /tests/ldxw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw r0, [r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 22 33 44 cc dd 6 | -- result 7 | 0x44332211 8 | -------------------------------------------------------------------------------- /tests/le32.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw r0, [r1] 3 | le32 r0 4 | exit 5 | -- mem 6 | 44 33 22 11 7 | -- result 8 | 0x11223344 9 | -------------------------------------------------------------------------------- /tests/mul64-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0x40000001 3 | mov r1, 4 4 | mul r0, r1 5 | exit 6 | -- result 7 | 0x100000004 8 | -------------------------------------------------------------------------------- /tests/arsh32-high-shift.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 8 3 | lddw r1, 0x100000001 4 | arsh32 r0, r1 5 | exit 6 | -- result 7 | 0x4 8 | -------------------------------------------------------------------------------- /tests/div32-high-divisor.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 12 3 | lddw r1, 0x100000004 4 | div32 r0, r1 5 | exit 6 | -- result 7 | 0x3 8 | -------------------------------------------------------------------------------- /tests/elf/too-many-sections.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_shnum = 1024 3 | -- error 4 | Failed to load code: too many sections 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-version.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_VERSION = 2 3 | -- error 4 | Failed to load code: wrong version 5 | -------------------------------------------------------------------------------- /tests/mul32-reg-overflow.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0x40000001 3 | mov r1, 4 4 | mul32 r0, r1 5 | exit 6 | -- result 7 | 0x4 8 | -------------------------------------------------------------------------------- /tests/stb.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stb [r1+2], 0x11 3 | ldxb r0, [r1+2] 4 | exit 5 | -- mem 6 | aa bb ff cc dd 7 | -- result 8 | 0x11 9 | -------------------------------------------------------------------------------- /tests/div64-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0xc 3 | lsh r0, 32 4 | mov r1, 4 5 | div r0, r1 6 | exit 7 | -- result 8 | 0x300000000 9 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-offset.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_rel.r_offset = 500 3 | -- error 4 | Failed to load code: bad relocation offset 5 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-symbol-name.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_sym.st_name = 100 3 | -- error 4 | Failed to load code: bad symbol name 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-class.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_CLASS = 'ELFCLASS32' 3 | -- error 4 | Failed to load code: wrong class 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-type.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_type = 0 3 | -- error 4 | Failed to load code: wrong type, expected relocatable 5 | -------------------------------------------------------------------------------- /tests/err-write-r10.dst: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r10, 1 3 | exit 4 | -- error 5 | Failed to load code: invalid destination register at PC 0 6 | -------------------------------------------------------------------------------- /tests/be16-high.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw r0, [r1] 3 | be16 r0 4 | exit 5 | -- mem 6 | 11 22 33 44 55 66 77 88 7 | -- result 8 | 0x1122 9 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-type.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_rel.r_info = (1 << 32) | 3 3 | -- error 4 | Failed to load code: bad relocation type 3 5 | -------------------------------------------------------------------------------- /tests/elf/bad-section-size.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | text_shdr.sh_size = 1024 3 | -- error 4 | Failed to load code: bad section offset or size 5 | -------------------------------------------------------------------------------- /tests/elf/rel-sym-not-found.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_sym.st_name += 1 3 | -- error 4 | Failed to load code: function 'qrti' not found 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-osabi.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_OSABI = 'ELFOSABI_LINUX' 3 | -- error 4 | Failed to load code: wrong OS ABI 5 | -------------------------------------------------------------------------------- /tests/err-invalid-reg-src.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r11 3 | exit 4 | -- error 5 | Failed to load code: invalid source register at PC 0 6 | -------------------------------------------------------------------------------- /tests/exit-not-last.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r1, 0 3 | ja +2 4 | mov r2, 0 5 | exit 6 | mov r0, 0 7 | ja -4 8 | -- result 9 | 0x0 10 | -------------------------------------------------------------------------------- /tests/sth.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | sth [r1+2], 0x2211 3 | ldxh r0, [r1+2] 4 | exit 5 | -- mem 6 | aa bb ff ff cc dd 7 | -- result 8 | 0x2211 9 | -------------------------------------------------------------------------------- /tests/be32-high.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw r0, [r1] 3 | be32 r0 4 | exit 5 | -- mem 6 | 11 22 33 44 55 66 77 88 7 | -- result 8 | 0x11223344 9 | -------------------------------------------------------------------------------- /tests/be64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw r0, [r1] 3 | be64 r0 4 | exit 5 | -- mem 6 | 11 22 33 44 55 66 77 88 7 | -- result 8 | 0x1122334455667788 9 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-symbol-index.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | sqrti_rel.r_info = (10 << 32) | 2 3 | -- error 4 | Failed to load code: bad symbol index 5 | -------------------------------------------------------------------------------- /tests/elf/bad-section-offset.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | text_shdr.sh_offset = 1024 3 | -- error 4 | Failed to load code: bad section offset or size 5 | -------------------------------------------------------------------------------- /tests/elf/no-text-section.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | text_shdr.sh_type = 'SHT_NULL' 3 | -- error 4 | Failed to load code: text section not found 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-byte-order.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_ident.EI_DATA = 'ELFDATA2MSB' 3 | -- error 4 | Failed to load code: wrong byte order 5 | -------------------------------------------------------------------------------- /tests/err-invalid-reg-dst.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r11, 1 3 | exit 4 | -- error 5 | Failed to load code: invalid destination register at PC 0 6 | -------------------------------------------------------------------------------- /tests/le64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw r0, [r1] 3 | le64 r0 4 | exit 5 | -- mem 6 | 88 77 66 55 44 33 22 11 7 | -- result 8 | 0x1122334455667788 9 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-strtab-index.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | symtab_shdr.sh_link = 30 3 | -- error 4 | Failed to load code: bad string table section index 5 | -------------------------------------------------------------------------------- /tests/elf/wrong-machine.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_machine = 1 3 | -- error 4 | Failed to load code: wrong machine, expected none or BPF, got 1 5 | -------------------------------------------------------------------------------- /tests/err-div-by-zero-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 1 3 | div32 r0, 0 4 | exit 5 | -- error 6 | Failed to load code: division by zero at PC 1 7 | -------------------------------------------------------------------------------- /tests/ldxdw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxdw r0, [r1+2] 3 | exit 4 | -- mem 5 | aa bb 11 22 33 44 55 66 77 88 cc dd 6 | -- result 7 | 0x8877665544332211 8 | -------------------------------------------------------------------------------- /tests/elf/bad-section-header-offset.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_shoff = 1024 3 | -- error 4 | Failed to load code: bad section header offset or size 5 | -------------------------------------------------------------------------------- /tests/elf/bad-section-header-size.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | ehdr.e_shentsize = 1024 3 | -- error 4 | Failed to load code: bad section header offset or size 5 | -------------------------------------------------------------------------------- /tests/err-incomplete-lddw.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x5566778800000018 3 | 0x0000000000000095 4 | -- error 5 | Failed to load code: incomplete lddw at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-incomplete-lddw2.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x5566778800000018 3 | 0x0000000000000095 4 | -- error 5 | Failed to load code: incomplete lddw at PC 0 6 | -------------------------------------------------------------------------------- /tests/err-unknown-opcode.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x0000000000000006 3 | 0x0000000000000095 4 | -- error 5 | Failed to load code: unknown opcode 0x06 at PC 0 6 | -------------------------------------------------------------------------------- /tests/stw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stw [r1+2], 0x44332211 3 | ldxw r0, [r1+2] 4 | exit 5 | -- mem 6 | aa bb ff ff ff ff cc dd 7 | -- result 8 | 0x44332211 9 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # pypi version of parcon does not support python3 2 | git+https://github.com/javawizard/parcon 3 | nose ~= 1.3.1 4 | pyelftools ~= 0.27 5 | -------------------------------------------------------------------------------- /tests/arsh.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0xf8 3 | lsh32 r0, 28 4 | # r0 == 0x80000000 5 | arsh32 r0, 16 6 | exit 7 | -- result 8 | 0xffff8000 9 | 10 | -------------------------------------------------------------------------------- /tests/elf/bad-rel-symbol-table-section-index.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | rel_shdr.sh_link = 30 3 | -- error 4 | Failed to load code: bad symbol table section index 5 | -------------------------------------------------------------------------------- /tests/err-jmp-lddw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja +1 3 | lddw r0, 0x1122334455667788 4 | exit 5 | -- error 6 | Failed to load code: jump to middle of lddw at PC 0 7 | -------------------------------------------------------------------------------- /tests/ldxh-same-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r1 3 | sth [r0], 0x1234 4 | ldxh r0, [r0] 5 | exit 6 | -- mem 7 | ff ff 8 | -- result 9 | 0x1234 10 | -------------------------------------------------------------------------------- /tests/stxb.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r2, 0x11 3 | stxb [r1+2], r2 4 | ldxb r0, [r1+2] 5 | exit 6 | -- mem 7 | aa bb ff cc dd 8 | -- result 9 | 0x11 10 | -------------------------------------------------------------------------------- /tests/arsh64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 1 3 | lsh r0, 63 4 | arsh r0, 55 5 | mov32 r1, 5 6 | arsh r0, r1 7 | exit 8 | -- result 9 | 0xfffffffffffffff8 10 | -------------------------------------------------------------------------------- /tests/stxh.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r2, 0x2211 3 | stxh [r1+2], r2 4 | ldxh r0, [r1+2] 5 | exit 6 | -- mem 7 | aa bb ff ff cc dd 8 | -- result 9 | 0x2211 10 | -------------------------------------------------------------------------------- /tests/arsh-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0xf8 3 | mov32 r1, 16 4 | lsh32 r0, 28 5 | # r0 == 0x80000000 6 | arsh32 r0, r1 7 | exit 8 | -- result 9 | 0xffff8000 10 | -------------------------------------------------------------------------------- /tests/elf/ehdr-short.data: -------------------------------------------------------------------------------- 1 | -- pyelf 2 | del order[:] 3 | magic = b'\x7fELF' 4 | order.append('magic') 5 | -- error 6 | Failed to load code: not enough data for ELF header 7 | -------------------------------------------------------------------------------- /tests/stdw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stdw [r1+2], 0x44332211 3 | ldxdw r0, [r1+2] 4 | exit 5 | -- mem 6 | aa bb ff ff ff ff ff ff ff ff cc dd 7 | -- result 8 | 0x0000000044332211 9 | -------------------------------------------------------------------------------- /tests/err-endian-size.data: -------------------------------------------------------------------------------- 1 | -- raw 2 | 0x00000030000001dc 3 | 0x00000000000010b7 4 | 0x0000000000000095 5 | -- error 6 | Failed to load code: invalid endian immediate at PC 0 7 | -------------------------------------------------------------------------------- /tests/mod.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 5748 3 | mod32 r0, 92 4 | # r0 == 44 5 | 6 | mov32 r1, 13 7 | mod32 r0, r1 8 | # r0 == 5 9 | 10 | exit 11 | -- result 12 | 0x5 13 | -------------------------------------------------------------------------------- /tests/stxw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r2, 0x44332211 3 | stxw [r1+2], r2 4 | ldxw r0, [r1+2] 5 | exit 6 | -- mem 7 | aa bb ff ff ff ff cc dd 8 | -- result 9 | 0x44332211 10 | -------------------------------------------------------------------------------- /tests/jit-bounce.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 1 3 | mov r6, r0 4 | mov r7, r6 5 | mov r8, r7 6 | mov r9, r8 7 | # TODO r10 8 | mov r0, r9 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/lddw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | lddw r0, 0x1122334455667788 3 | exit 4 | -- raw 5 | 0x5566778800000018 6 | 0x1122334400000000 7 | 0x0000000000000095 8 | -- result 9 | 0x1122334455667788 10 | -------------------------------------------------------------------------------- /tests/err-div-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 1 3 | mov32 r1, 0 4 | div32 r0, r1 5 | exit 6 | -- result 7 | 0xffffffffffffffff 8 | -- error 9 | uBPF error: division by zero at PC 2 10 | -------------------------------------------------------------------------------- /tests/err-div64-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 1 3 | mov32 r1, 0 4 | div r0, r1 5 | exit 6 | -- result 7 | 0xffffffffffffffff 8 | -- error 9 | uBPF error: division by zero at PC 2 10 | -------------------------------------------------------------------------------- /tests/err-mod-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 1 3 | mov32 r1, 0 4 | mod32 r0, r1 5 | exit 6 | -- result 7 | 0xffffffffffffffff 8 | -- error 9 | uBPF error: division by zero at PC 2 10 | -------------------------------------------------------------------------------- /tests/err-mod64-by-zero-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 1 3 | mov32 r1, 0 4 | mod r0, r1 5 | exit 6 | -- result 7 | 0xffffffffffffffff 8 | -- error 9 | uBPF error: division by zero at PC 2 10 | -------------------------------------------------------------------------------- /tests/call.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r1, 1 3 | mov r2, 2 4 | mov r3, 3 5 | mov r4, 4 6 | mov r5, 5 7 | call 0 8 | exit 9 | -- result 10 | 0x0102030405 11 | -- no register offset 12 | call instruction 13 | -------------------------------------------------------------------------------- /tests/err-call-bad-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r1, 1 3 | mov r2, 2 4 | mov r3, 3 5 | mov r4, 4 6 | mov r5, 5 7 | call 64 8 | exit 9 | -- error 10 | Failed to load code: invalid call immediate at PC 5 11 | -------------------------------------------------------------------------------- /tests/err-call-unreg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r1, 1 3 | mov r2, 2 4 | mov r3, 3 5 | mov r4, 4 6 | mov r5, 5 7 | call 63 8 | exit 9 | -- error 10 | Failed to load code: call to nonexistent function 63 at PC 5 11 | -------------------------------------------------------------------------------- /tests/jgt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 5 4 | jgt r1, 6, +2 # Not taken 5 | jgt r1, 5, +1 # Not taken 6 | jgt r1, 4, +1 # Taken 7 | exit 8 | mov32 r0, 1 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/jlt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 5 4 | jlt r1, 4, +2 # Not taken 5 | jlt r1, 5, +1 # Not taken 6 | jlt r1, 6, +1 # Taken 7 | exit 8 | mov32 r0, 1 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/stxb-all2.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r1 3 | mov r1, 0xf1 4 | mov r9, 0xf9 5 | stxb [r0], r1 6 | stxb [r0+1], r9 7 | ldxh r0, [r0] 8 | be16 r0 9 | exit 10 | -- mem 11 | ff ff 12 | -- result 13 | 0xf1f9 14 | -------------------------------------------------------------------------------- /tests/jle-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 5 4 | jle r1, 4, +1 # Not taken 5 | jle r1, 6, +1 # Taken 6 | exit 7 | jle r1, 5, +1 # Taken 8 | exit 9 | mov32 r0, 1 10 | exit 11 | -- result 12 | 0x1 13 | -------------------------------------------------------------------------------- /tests/jeq-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 0xa 4 | jeq r1, 0xb, +4 # Not taken 5 | 6 | mov32 r0, 1 7 | mov32 r1, 0xb 8 | jeq r1, 0xb, +1 # Taken 9 | 10 | mov32 r0, 2 # Skipped 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/stxdw.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r2, 0x88776655 3 | lsh r2, 32 4 | or r2, 0x44332211 5 | stxdw [r1+2], r2 6 | ldxdw r0, [r1+2] 7 | exit 8 | -- mem 9 | aa bb ff ff ff ff ff ff ff ff cc dd 10 | -- result 11 | 0x8877665544332211 12 | -------------------------------------------------------------------------------- /tests/jgt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0 3 | mov r1, 5 4 | mov r2, 6 5 | mov r3, 4 6 | jgt r1, r2, +2 # Not taken 7 | jgt r1, r1, +1 # Not taken 8 | jgt r1, r3, +1 # Taken 9 | exit 10 | mov r0, 1 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/jlt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0 3 | mov r1, 5 4 | mov r2, 4 5 | mov r3, 6 6 | jlt r1, r2, +2 # Not taken 7 | jlt r1, r1, +1 # Not taken 8 | jlt r1, r3, +1 # Taken 9 | exit 10 | mov r0, 1 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/err-stack-oob.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stb [r10], 0 3 | exit 4 | -- error pattern 5 | uBPF error: out of bounds memory store at PC 0, addr .*, size 1 6 | -- result 7 | 0xffffffffffffffff 8 | -- no jit 9 | stack oob check not implemented 10 | -------------------------------------------------------------------------------- /tests/jge-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 0xa 4 | jge r1, 0xb, +4 # Not taken 5 | 6 | mov32 r0, 1 7 | mov32 r1, 0xc 8 | jge r1, 0xb, +1 # Taken 9 | 10 | mov32 r0, 2 # Skipped 11 | 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jle-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0 3 | mov r1, 5 4 | mov r2, 4 5 | mov r3, 6 6 | jle r1, r2, +2 # Not taken 7 | jle r1, r1, +1 # Taken 8 | exit 9 | jle r1, r3, +1 # Taken 10 | exit 11 | mov r0, 1 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jset-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 0x7 4 | jset r1, 0x8, +4 # Not taken 5 | 6 | mov32 r0, 1 7 | mov32 r1, 0x9 8 | jset r1, 0x8, +1 # Taken 9 | 10 | mov32 r0, 2 # Skipped 11 | 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jslt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xfffffffe 4 | jslt r1, 0xfffffffd, +2 # Not taken 5 | jslt r1, 0xfffffffe, +1 # Not taken 6 | jslt r1, 0xffffffff, +1 # Taken 7 | exit 8 | mov32 r0, 1 9 | exit 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/jeq-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 0xa 4 | mov32 r2, 0xb 5 | jeq r1, r2, +4 # Not taken 6 | 7 | mov32 r0, 1 8 | mov32 r1, 0xb 9 | jeq r1, r2, +1 # Taken 10 | 11 | mov32 r0, 2 # Skipped 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jne-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 0xb 4 | mov32 r2, 0xb 5 | jne r1, r2, +4 # Not taken 6 | 7 | mov32 r0, 1 8 | mov32 r1, 0xa 9 | jne r1, r2, +1 # Taken 10 | 11 | mov32 r0, 2 # Skipped 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/tcp-sack/pkt-nosack.hex: -------------------------------------------------------------------------------- 1 | 0x0000: 0026 622f 4787 001d 60b3 0184 0800 4500 2 | 0x0010: 0040 a8de 4000 4006 9d58 c0a8 0103 3f74 3 | 0x0020: f361 e5c0 0050 e594 3f77 a3c4 c480 8010 4 | 0x0030: 013e 34b6 0000 0101 080a 0017 956f 8d9d 5 | 0x0040: 9e27 6 | -------------------------------------------------------------------------------- /tests/call-memfrob.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r6, r1 3 | add r1, 2 4 | mov r2, 4 5 | call 1 6 | ldxdw r0, [r6] 7 | be64 r0 8 | exit 9 | -- mem 10 | 01 02 03 04 05 06 07 08 11 | -- result 12 | 0x102292e2f2c0708 13 | -- no register offset 14 | call instruction 15 | -------------------------------------------------------------------------------- /tests/jset-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 0x7 4 | mov32 r2, 0x8 5 | jset r1, r2, +4 # Not taken 6 | 7 | mov32 r0, 1 8 | mov32 r1, 0x9 9 | jset r1, r2, +1 # Taken 10 | 11 | mov32 r0, 2 # Skipped 12 | 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/jsgt-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xfffffffe 4 | jsgt r1, 0xffffffff, +4 # Not taken 5 | 6 | mov32 r0, 1 7 | mov32 r1, 0 8 | jsgt r1, 0xffffffff, +1 # Taken 9 | 10 | mov32 r0, 2 # Skipped 11 | 12 | exit 13 | -- result 14 | 0x1 15 | -------------------------------------------------------------------------------- /tests/jsle-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xfffffffe 4 | jsle r1, 0xfffffffd, +1 # Not taken 5 | jsle r1, 0xffffffff, +1 # Taken 6 | exit 7 | mov32 r0, 1 8 | jsle r1, 0xfffffffe, +1 # Taken 9 | mov32 r0, 2 10 | exit 11 | -- result 12 | 0x1 13 | -------------------------------------------------------------------------------- /tests/jsgt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xfffffffe 4 | mov r2, 0xffffffff 5 | jsgt r1, r2, +4 # Not taken 6 | 7 | mov32 r0, 1 8 | mov32 r1, 0 9 | jsgt r1, r2, +1 # Taken 10 | 11 | mov32 r0, 2 # Skipped 12 | 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/stx.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stxw [r1], r2 3 | stxw [r1+1], r2 4 | stxw [r1+32767], r2 5 | stxw [r1-1], r2 6 | stxw [r1-32768], r2 7 | -- raw 8 | 0x0000000000002163 9 | 0x0000000000012163 10 | 0x000000007fff2163 11 | 0x00000000ffff2163 12 | 0x0000000080002163 13 | -------------------------------------------------------------------------------- /tests/jslt-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xfffffffe 4 | mov r2, 0xfffffffd 5 | mov r3, 0xffffffff 6 | jslt r1, r1, +2 # Not taken 7 | jslt r1, r2, +1 # Not taken 8 | jslt r1, r3, +1 # Taken 9 | exit 10 | mov32 r0, 1 11 | exit 12 | -- result 13 | 0x1 14 | -------------------------------------------------------------------------------- /tests/st.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | stw [r1], 0x33 3 | stw [r1+1], 0x33 4 | stw [r1+32767], 0x33 5 | stw [r1-1], 0x33 6 | stw [r1-32768], 0x33 7 | -- raw 8 | 0x0000003300000162 9 | 0x0000003300010162 10 | 0x000000337fff0162 11 | 0x00000033ffff0162 12 | 0x0000003380000162 13 | -------------------------------------------------------------------------------- /tests/tcp-sack/Makefile: -------------------------------------------------------------------------------- 1 | PROG := tcp-sack 2 | 3 | $(PROG).asm: $(PROG).bin 4 | ../../bin/ubpf-disassembler $^ $@ 5 | 6 | $(PROG).bin: $(PROG).o 7 | objcopy -I elf64-little -O binary $^ $@ 8 | 9 | $(PROG).o: $(PROG).c 10 | clang-3.7 -O2 -target bpf -c $^ -o $@ 11 | -------------------------------------------------------------------------------- /tests/call-save.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r6, 0x0001 3 | mov r7, 0x0020 4 | mov r8, 0x0300 5 | mov r9, 0x4000 6 | call 2 7 | mov r0, 0 8 | or r0, r6 9 | or r0, r7 10 | or r0, r8 11 | or r0, r9 12 | exit 13 | -- result 14 | 0x4321 15 | -- no register offset 16 | call instruction 17 | -------------------------------------------------------------------------------- /tests/stack.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r1, 51 3 | 4 | # Create lookup table 5 | stdw [r10-16], 0xab 6 | stdw [r10-8], 0xcd 7 | 8 | # Load lookup[r1 % 2] 9 | and r1, 1 10 | lsh r1, 3 11 | mov r2, r10 12 | add r2, r1 13 | ldxdw r0, [r2-16] 14 | 15 | exit 16 | -- result 17 | 0xcd 18 | -------------------------------------------------------------------------------- /tests/tcp-sack/pkt-sack.hex: -------------------------------------------------------------------------------- 1 | 0x0000: 0026 622f 4787 001d 60b3 0184 0800 4500 2 | 0x0010: 0040 a8de 4000 4006 9d58 c0a8 0103 3f74 3 | 0x0020: f361 e5c0 0050 e594 3f77 a3c4 c480 b010 4 | 0x0030: 013e 34b6 0000 0101 080a 0017 956f 8d9d 5 | 0x0040: 9e27 0101 050a a3c4 ca28 a3c4 cfd0 6 | -------------------------------------------------------------------------------- /tests/jsge-imm.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xfffffffe 4 | jsge r1, 0xffffffff, +5 # Not taken 5 | jsge r1, 0, +4 # Not taken 6 | 7 | mov32 r0, 1 8 | mov r1, 0xffffffff 9 | jsge r1, 0xffffffff, +1 # Taken 10 | 11 | mov32 r0, 2 # Skipped 12 | 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/jsle-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xffffffff 4 | mov r2, 0xfffffffe 5 | mov32 r3, 0 6 | jsle r1, r2, +1 # Not taken 7 | jsle r1, r3, +1 # Taken 8 | exit 9 | mov32 r0, 1 10 | mov r1, r2 11 | jsle r1, r2, +1 # Taken 12 | mov32 r0, 2 13 | exit 14 | -- result 15 | 0x1 16 | -------------------------------------------------------------------------------- /tests/jsge-reg.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov r1, 0xfffffffe 4 | mov r2, 0xffffffff 5 | mov32 r3, 0 6 | jsge r1, r2, +5 # Not taken 7 | jsge r1, r3, +4 # Not taken 8 | 9 | mov32 r0, 1 10 | mov r1, r2 11 | jsge r1, r2, +1 # Taken 12 | 13 | mov32 r0, 2 # Skipped 14 | 15 | exit 16 | -- result 17 | 0x1 18 | -------------------------------------------------------------------------------- /tests/mod64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0xb1858436 3 | lsh r0, 32 4 | or r0, 0x100dc5c8 5 | # r0 == 0xb1858436100dc5c8 6 | 7 | mov32 r1, 0xdde263e 8 | lsh r1, 32 9 | or r1, 0x3cbef7f3 10 | # r1 == 0xdde263e3cbef7f3 11 | 12 | mod r0, r1 13 | # r0 == 0xb1bb94b371a2664 14 | 15 | mod r0, 0x658f1778 16 | # r0 == 0x30ba5a04 17 | 18 | exit 19 | -- result 20 | 0x30ba5a04 21 | -------------------------------------------------------------------------------- /tests/tcp-port-80/tcp-port-80.asm: -------------------------------------------------------------------------------- 1 | ldxb r2, [r1+12] 2 | ldxb r3, [r1+13] 3 | lsh r3, 0x8 4 | or r3, r2 5 | mov r0, 0x0 6 | jne r3, 0x8, +12 7 | ldxb r2, [r1+23] 8 | jne r2, 0x6, +10 9 | ldxb r2, [r1+14] 10 | add r1, 0xe 11 | and r2, 0xf 12 | lsh r2, 0x2 13 | add r1, r2 14 | ldxh r2, [r1+2] 15 | jeq r2, 0x5000, +2 16 | ldxh r1, [r1] 17 | jne r1, 0x5000, +1 18 | mov r0, 0x1 19 | exit 20 | -------------------------------------------------------------------------------- /tests/ldx.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ldxw r1, [r2] 3 | ldxh r1, [r2] 4 | ldxb r1, [r2] 5 | ldxdw r1, [r2] 6 | ldxw r1, [r2+1] 7 | ldxw r1, [r2+32767] 8 | ldxw r1, [r2-1] 9 | ldxw r1, [r2-32768] 10 | -- raw 11 | 0x0000000000002161 12 | 0x0000000000002169 13 | 0x0000000000002171 14 | 0x0000000000002179 15 | 0x0000000000012161 16 | 0x000000007fff2161 17 | 0x00000000ffff2161 18 | 0x0000000080002161 19 | -------------------------------------------------------------------------------- /tests/tcp-port-80/match.data: -------------------------------------------------------------------------------- 1 | -- asm @ tcp-port-80.asm 2 | -- mem 3 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 00 45 00 4 | 00 56 00 01 00 00 40 06 f9 4d c0 a8 00 01 c0 a8 5 | 00 02 27 10 00 50 00 00 00 00 00 00 00 00 50 02 6 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 7 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 8 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 10 | -- result 11 | 0x1 12 | -------------------------------------------------------------------------------- /tests/alu64-arith.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0 3 | mov r1, 1 4 | mov r2, 2 5 | mov r3, 3 6 | mov r4, 4 7 | mov r5, 5 8 | mov r6, 6 9 | mov r7, 7 10 | mov r8, 8 11 | mov r9, 9 12 | # r0 == 0 13 | 14 | add r0, 23 15 | add r0, r7 16 | # r0 == 30 17 | 18 | sub r0, 13 19 | sub r0, r1 20 | # r0 == 16 21 | 22 | mul r0, 7 23 | mul r0, r3 24 | # r0 == 336 25 | 26 | div r0, 2 27 | div r0, r4 28 | # r0 == 42 29 | 30 | exit 31 | -- result 32 | 0x2a 33 | -------------------------------------------------------------------------------- /tests/stxb-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0xf0 3 | mov r2, 0xf2 4 | mov r3, 0xf3 5 | mov r4, 0xf4 6 | mov r5, 0xf5 7 | mov r6, 0xf6 8 | mov r7, 0xf7 9 | mov r8, 0xf8 10 | stxb [r1], r0 11 | stxb [r1+1], r2 12 | stxb [r1+2], r3 13 | stxb [r1+3], r4 14 | stxb [r1+4], r5 15 | stxb [r1+5], r6 16 | stxb [r1+6], r7 17 | stxb [r1+7], r8 18 | ldxdw r0, [r1] 19 | be64 r0 20 | exit 21 | -- mem 22 | ff ff ff ff ff ff ff ff 23 | -- result 24 | 0xf0f2f3f4f5f6f7f8 25 | -------------------------------------------------------------------------------- /tests/tcp-port-80/nomatch-ethertype.data: -------------------------------------------------------------------------------- 1 | -- description 2 | Wrong ethertype 3 | -- asm @ tcp-port-80.asm 4 | -- mem 5 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 01 45 00 6 | 00 56 00 01 00 00 40 06 f9 4d c0 a8 00 01 c0 a8 7 | 00 02 27 10 00 50 00 00 00 00 00 00 00 00 50 02 8 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 10 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 11 | 44 44 44 44 12 | -- result 13 | 0x0 14 | -------------------------------------------------------------------------------- /tests/tcp-port-80/nomatch-proto.data: -------------------------------------------------------------------------------- 1 | -- description 2 | Wrong IP protocol 3 | -- asm @ tcp-port-80.asm 4 | -- mem 5 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 00 45 00 6 | 00 56 00 01 00 00 40 11 f9 4d c0 a8 00 01 c0 a8 7 | 00 02 27 10 00 50 00 00 00 00 00 00 00 00 50 02 8 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 10 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 11 | 44 44 44 44 12 | -- result 13 | 0x0 14 | -------------------------------------------------------------------------------- /tests/tcp-port-80/nomatch.data: -------------------------------------------------------------------------------- 1 | -- description 2 | Incorrect TCP src/dst ports 3 | -- asm @ tcp-port-80.asm 4 | -- mem 5 | 00 01 02 03 04 05 00 06 07 08 09 0a 08 00 45 00 6 | 00 56 00 01 00 00 40 06 f9 4d c0 a8 00 01 c0 a8 7 | 00 02 00 16 27 10 00 00 00 00 00 00 00 00 51 02 8 | 20 00 c5 18 00 00 44 44 44 44 44 44 44 44 44 44 9 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 10 | 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 44 11 | 44 44 44 44 12 | -- result 13 | 0x0 14 | -------------------------------------------------------------------------------- /tests/alu-arith.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 1 4 | mov32 r2, 2 5 | mov32 r3, 3 6 | mov32 r4, 4 7 | mov32 r5, 5 8 | mov32 r6, 6 9 | mov32 r7, 7 10 | mov32 r8, 8 11 | mov32 r9, 9 12 | # r0 == 0 13 | 14 | add32 r0, 23 15 | add32 r0, r7 16 | # r0 == 30 17 | 18 | sub32 r0, 13 19 | sub32 r0, r1 20 | # r0 == 16 21 | 22 | mul32 r0, 7 23 | mul32 r0, r3 24 | # r0 == 336 25 | 26 | div32 r0, 2 27 | div32 r0, r4 28 | # r0 == 42 29 | 30 | exit 31 | -- result 32 | 0x2a 33 | -------------------------------------------------------------------------------- /tests/mul-loop.data: -------------------------------------------------------------------------------- 1 | # Testcase compiled by Clang 2 | -- c 3 | #include 4 | 5 | uint64_t entry(void *ctx) 6 | { 7 | uint64_t n = (uintptr_t)ctx + 10; 8 | uint64_t i; 9 | uint64_t a = 7llu; 10 | for (i = 0; i < n; i++) { 11 | a *= 7llu; 12 | } 13 | return a; 14 | } 15 | -- asm 16 | mov r0, 0x7 17 | add r1, 0xa 18 | lsh r1, 0x20 19 | rsh r1, 0x20 20 | jeq r1, 0x0, +4 21 | mov r0, 0x7 22 | mul r0, 0x7 23 | add r1, 0xffffffff 24 | jne r1, 0x0, -3 25 | exit 26 | -- result 27 | 0x75db9c97 28 | -------------------------------------------------------------------------------- /tests/stack2.data: -------------------------------------------------------------------------------- 1 | # Test that stack data is preserved across function calls 2 | -- asm 3 | stb [r10-4], 0x01 4 | stb [r10-3], 0x02 5 | stb [r10-2], 0x03 6 | stb [r10-1], 0x04 7 | 8 | # memfrob 9 | mov r1, r10 10 | mov r2, 0x4 11 | sub r1, r2 12 | call 1 13 | 14 | mov r1, 0 15 | ldxb r2, [r10-4] 16 | ldxb r3, [r10-3] 17 | ldxb r4, [r10-2] 18 | ldxb r5, [r10-1] 19 | 20 | call 0 # gather_bytes 21 | 22 | xor r0, 0x2a2a2a2a # undo memfrob 23 | 24 | exit 25 | -- result 26 | 0x01020304 27 | -- no register offset 28 | call instruction 29 | -------------------------------------------------------------------------------- /tests/alu64-bit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, 0 3 | mov r1, 1 4 | mov r2, 2 5 | mov r3, 3 6 | mov r4, 4 7 | mov r5, 5 8 | mov r6, 6 9 | mov r7, 7 10 | mov r8, 8 11 | # r0 == 0 12 | 13 | or r0, r5 14 | or r0, 0xa0 15 | # r0 == 0xa5 16 | 17 | and r0, 0xa3 18 | mov r9, 0x91 19 | and r0, r9 20 | # r0 == 0x21 21 | 22 | lsh r0, 32 23 | lsh r0, 22 24 | lsh r0, r8 25 | # r0 == 0x40000000 26 | 27 | rsh r0, 32 28 | rsh r0, 19 29 | rsh r0, r7 30 | # r0 == 0x10 31 | 32 | xor r0, 0x03 33 | xor r0, r2 34 | # r0 == 0x11 35 | 36 | exit 37 | -- result 38 | 0x11 39 | -------------------------------------------------------------------------------- /tests/alu-bit.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov32 r0, 0 3 | mov32 r1, 1 4 | mov32 r2, 2 5 | mov32 r3, 3 6 | mov32 r4, 4 7 | mov32 r5, 5 8 | mov32 r6, 6 9 | mov32 r7, 7 10 | mov32 r8, 8 11 | # r0 == 0 12 | 13 | or32 r0, r5 14 | or32 r0, 0xa0 15 | # r0 == 0xa5 16 | 17 | and32 r0, 0xa3 18 | mov32 r9, 0x91 19 | and32 r0, r9 20 | # r0 == 0x21 21 | 22 | lsh32 r0, 22 23 | lsh32 r0, r8 24 | # r0 == 0x40000000 25 | 26 | rsh32 r0, 19 27 | rsh32 r0, r7 28 | # r0 == 0x10 29 | 30 | xor32 r0, 0x03 31 | xor32 r0, r2 32 | # r0 == 0x11 33 | 34 | exit 35 | -- result 36 | 0x11 37 | -------------------------------------------------------------------------------- /tests/stxb-chain.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r1 3 | 4 | ldxb r9, [r0+0] 5 | stxb [r0+1], r9 6 | 7 | ldxb r8, [r0+1] 8 | stxb [r0+2], r8 9 | 10 | ldxb r7, [r0+2] 11 | stxb [r0+3], r7 12 | 13 | ldxb r6, [r0+3] 14 | stxb [r0+4], r6 15 | 16 | ldxb r5, [r0+4] 17 | stxb [r0+5], r5 18 | 19 | ldxb r4, [r0+5] 20 | stxb [r0+6], r4 21 | 22 | ldxb r3, [r0+6] 23 | stxb [r0+7], r3 24 | 25 | ldxb r2, [r0+7] 26 | stxb [r0+8], r2 27 | 28 | ldxb r1, [r0+8] 29 | stxb [r0+9], r1 30 | 31 | ldxb r0, [r0+9] 32 | exit 33 | -- mem 34 | 2a 00 00 00 00 00 00 00 00 00 35 | -- result 36 | 0x2a 37 | -------------------------------------------------------------------------------- /tests/jmp.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | ja +1 3 | ja +32767 4 | ja -1 5 | ja -32768 6 | jeq r1, 0x33, +1 7 | jeq r1, r2, +1 8 | jgt r1, r2, +1 9 | jge r1, r2, +1 10 | jset r1, r2, +1 11 | jne r1, r2, +1 12 | jsgt r1, r2, +1 13 | jsge r1, r2, +1 14 | call 0x1 15 | exit 16 | -- raw 17 | 0x0000000000010005 18 | 0x000000007fff0005 19 | 0x00000000ffff0005 20 | 0x0000000080000005 21 | 0x0000003300010115 22 | 0x000000000001211d 23 | 0x000000000001212d 24 | 0x000000000001213d 25 | 0x000000000001214d 26 | 0x000000000001215d 27 | 0x000000000001216d 28 | 0x000000000001217d 29 | 0x0000000100000085 30 | 0x0000000000000095 31 | -------------------------------------------------------------------------------- /tests/prime.data: -------------------------------------------------------------------------------- 1 | # Compiled by Clang 2 | -- c 3 | #include 4 | #include 5 | 6 | uint64_t entry(uint64_t arg) 7 | { 8 | int i = 0; 9 | for (i = 2; i < arg; i++) { 10 | if (arg % i == 0) { 11 | return false; 12 | } 13 | } 14 | return true; 15 | } 16 | -- asm 17 | mov r1, 67 18 | mov r0, 0x1 19 | mov r2, 0x2 20 | jgt r1, 0x2, +4 21 | ja +10 22 | add r2, 0x1 23 | mov r0, 0x1 24 | jge r2, r1, +7 25 | mov r3, r1 26 | div r3, r2 27 | mul r3, r2 28 | mov r4, r1 29 | sub r4, r3 30 | mov r0, 0x0 31 | jne r4, 0x0, -10 32 | exit 33 | -- result 34 | 0x1 35 | -------------------------------------------------------------------------------- /tests/alu64.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | add r1, 0x2 3 | add r9, 0xffffffff 4 | add r1, r2 5 | sub r1, r2 6 | mul r1, r2 7 | div r1, r2 8 | or r1, r2 9 | and r1, r2 10 | lsh r1, r2 11 | rsh r1, r2 12 | neg r1 13 | mod r1, r2 14 | xor r1, r2 15 | mov r1, r2 16 | arsh r1, r2 17 | -- raw 18 | 0x0000000200000107 19 | 0xffffffff00000907 20 | 0x000000000000210f 21 | 0x000000000000211f 22 | 0x000000000000212f 23 | 0x000000000000213f 24 | 0x000000000000214f 25 | 0x000000000000215f 26 | 0x000000000000216f 27 | 0x000000000000217f 28 | 0x0000000000000187 29 | 0x000000000000219f 30 | 0x00000000000021af 31 | 0x00000000000021bf 32 | 0x00000000000021cf 33 | -------------------------------------------------------------------------------- /tests/ldxb-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r1 3 | 4 | ldxb r9, [r0+0] 5 | lsh r9, 0 6 | 7 | ldxb r8, [r0+1] 8 | lsh r8, 4 9 | 10 | ldxb r7, [r0+2] 11 | lsh r7, 8 12 | 13 | ldxb r6, [r0+3] 14 | lsh r6, 12 15 | 16 | ldxb r5, [r0+4] 17 | lsh r5, 16 18 | 19 | ldxb r4, [r0+5] 20 | lsh r4, 20 21 | 22 | ldxb r3, [r0+6] 23 | lsh r3, 24 24 | 25 | ldxb r2, [r0+7] 26 | lsh r2, 28 27 | 28 | ldxb r1, [r0+8] 29 | lsh r1, 32 30 | 31 | ldxb r0, [r0+9] 32 | lsh r0, 36 33 | 34 | or r0, r1 35 | or r0, r2 36 | or r0, r3 37 | or r0, r4 38 | or r0, r5 39 | or r0, r6 40 | or r0, r7 41 | or r0, r8 42 | or r0, r9 43 | 44 | exit 45 | -- result 46 | 0x9876543210 47 | -- mem 48 | 00 01 02 03 04 05 06 07 08 09 49 | -------------------------------------------------------------------------------- /tests/ldxh-all2.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r1 3 | 4 | ldxh r9, [r0+0] 5 | be16 r9 6 | 7 | ldxh r8, [r0+2] 8 | be16 r8 9 | 10 | ldxh r7, [r0+4] 11 | be16 r7 12 | 13 | ldxh r6, [r0+6] 14 | be16 r6 15 | 16 | ldxh r5, [r0+8] 17 | be16 r5 18 | 19 | ldxh r4, [r0+10] 20 | be16 r4 21 | 22 | ldxh r3, [r0+12] 23 | be16 r3 24 | 25 | ldxh r2, [r0+14] 26 | be16 r2 27 | 28 | ldxh r1, [r0+16] 29 | be16 r1 30 | 31 | ldxh r0, [r0+18] 32 | be16 r0 33 | 34 | or r0, r1 35 | or r0, r2 36 | or r0, r3 37 | or r0, r4 38 | or r0, r5 39 | or r0, r6 40 | or r0, r7 41 | or r0, r8 42 | or r0, r9 43 | 44 | exit 45 | -- result 46 | 0x3ff 47 | -- mem 48 | 00 01 49 | 00 02 50 | 00 04 51 | 00 08 52 | 00 10 53 | 00 20 54 | 00 40 55 | 00 80 56 | 01 00 57 | 02 00 58 | -------------------------------------------------------------------------------- /tests/ldxw-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r1 3 | 4 | ldxw r9, [r0+0] 5 | be32 r9 6 | 7 | ldxw r8, [r0+4] 8 | be32 r8 9 | 10 | ldxw r7, [r0+8] 11 | be32 r7 12 | 13 | ldxw r6, [r0+12] 14 | be32 r6 15 | 16 | ldxw r5, [r0+16] 17 | be32 r5 18 | 19 | ldxw r4, [r0+20] 20 | be32 r4 21 | 22 | ldxw r3, [r0+24] 23 | be32 r3 24 | 25 | ldxw r2, [r0+28] 26 | be32 r2 27 | 28 | ldxw r1, [r0+32] 29 | be32 r1 30 | 31 | ldxw r0, [r0+36] 32 | be32 r0 33 | 34 | or r0, r1 35 | or r0, r2 36 | or r0, r3 37 | or r0, r4 38 | or r0, r5 39 | or r0, r6 40 | or r0, r7 41 | or r0, r8 42 | or r0, r9 43 | 44 | exit 45 | -- result 46 | 0x030f0f 47 | -- mem 48 | 00 00 00 01 49 | 00 00 00 02 50 | 00 00 00 04 51 | 00 00 00 08 52 | 00 00 01 00 53 | 00 00 02 00 54 | 00 00 04 00 55 | 00 00 08 00 56 | 00 01 00 00 57 | 00 02 00 00 58 | -------------------------------------------------------------------------------- /tests/tcp-sack/tcp-sack.asm: -------------------------------------------------------------------------------- 1 | ldxb r2, [r1+12] 2 | ldxb r3, [r1+13] 3 | lsh r3, 0x8 4 | or r3, r2 5 | mov r0, 0x0 6 | jne r3, 0x8, +37 7 | ldxb r2, [r1+23] 8 | jne r2, 0x6, +35 9 | ldxb r2, [r1+14] 10 | add r1, 0xe 11 | and r2, 0xf 12 | lsh r2, 0x2 13 | add r1, r2 14 | mov r0, 0x0 15 | ldxh r4, [r1+12] 16 | add r1, 0x14 17 | rsh r4, 0x2 18 | and r4, 0x3c 19 | mov r2, r4 20 | add r2, 0xffffffec 21 | mov r5, 0x15 22 | mov r3, 0x0 23 | jgt r5, r4, +20 24 | mov r5, r3 25 | lsh r5, 0x20 26 | arsh r5, 0x20 27 | mov r4, r1 28 | add r4, r5 29 | ldxb r5, [r4] 30 | jeq r5, 0x1, +4 31 | jeq r5, 0x0, +12 32 | mov r6, r3 33 | jeq r5, 0x5, +9 34 | ja +2 35 | add r3, 0x1 36 | mov r6, r3 37 | ldxb r3, [r4+1] 38 | add r3, r6 39 | lsh r3, 0x20 40 | arsh r3, 0x20 41 | jsgt r2, r3, -18 42 | ja +1 43 | mov r0, 0x1 44 | exit 45 | -------------------------------------------------------------------------------- /tests/ldxh-all.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | mov r0, r1 3 | 4 | ldxh r9, [r0+0] 5 | be16 r9 6 | lsh r9, 0 7 | 8 | ldxh r8, [r0+2] 9 | be16 r8 10 | lsh r8, 4 11 | 12 | ldxh r7, [r0+4] 13 | be16 r7 14 | lsh r7, 8 15 | 16 | ldxh r6, [r0+6] 17 | be16 r6 18 | lsh r6, 12 19 | 20 | ldxh r5, [r0+8] 21 | be16 r5 22 | lsh r5, 16 23 | 24 | ldxh r4, [r0+10] 25 | be16 r4 26 | lsh r4, 20 27 | 28 | ldxh r3, [r0+12] 29 | be16 r3 30 | lsh r3, 24 31 | 32 | ldxh r2, [r0+14] 33 | be16 r2 34 | lsh r2, 28 35 | 36 | ldxh r1, [r0+16] 37 | be16 r1 38 | lsh r1, 32 39 | 40 | ldxh r0, [r0+18] 41 | be16 r0 42 | lsh r0, 36 43 | 44 | or r0, r1 45 | or r0, r2 46 | or r0, r3 47 | or r0, r4 48 | or r0, r5 49 | or r0, r6 50 | or r0, r7 51 | or r0, r8 52 | or r0, r9 53 | 54 | exit 55 | -- result 56 | 0x9876543210 57 | -- mem 58 | 00 00 00 01 00 02 00 03 00 04 00 05 00 06 00 07 00 08 00 09 59 | -------------------------------------------------------------------------------- /tests/alu.data: -------------------------------------------------------------------------------- 1 | -- asm 2 | add32 r1, 0x2 3 | add32 r9, 0xffffffff 4 | add32 r1, r2 5 | sub32 r1, r2 6 | mul32 r1, r2 7 | div32 r1, r2 8 | or32 r1, r2 9 | and32 r1, r2 10 | lsh32 r1, r2 11 | rsh32 r1, r2 12 | neg32 r1 13 | mod32 r1, r2 14 | xor32 r1, r2 15 | mov32 r1, r2 16 | arsh32 r1, r2 17 | le16 r1 18 | le32 r1 19 | le64 r1 20 | be16 r1 21 | be32 r1 22 | be64 r1 23 | -- raw 24 | 0x0000000200000104 25 | 0xffffffff00000904 26 | 0x000000000000210c 27 | 0x000000000000211c 28 | 0x000000000000212c 29 | 0x000000000000213c 30 | 0x000000000000214c 31 | 0x000000000000215c 32 | 0x000000000000216c 33 | 0x000000000000217c 34 | 0x0000000000000184 35 | 0x000000000000219c 36 | 0x00000000000021ac 37 | 0x00000000000021bc 38 | 0x00000000000021cc 39 | 0x00000010000001d4 40 | 0x00000020000001d4 41 | 0x00000040000001d4 42 | 0x00000010000001dc 43 | 0x00000020000001dc 44 | 0x00000040000001dc 45 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | compiler: 3 | - gcc 4 | os: linux 5 | dist: xenial 6 | jobs: 7 | include: 8 | - name: python 2.7 9 | env: PYTHON=python2 10 | before_install: 11 | - sudo apt-get update 12 | - sudo apt-get -y install python python-pip python-setuptools python-wheel 13 | after_success: 14 | - coveralls --gcov-options '\-lp' -i $PWD/vm/ubpf_vm.c -i $PWD/vm/ubpf_jit_x86_64.c -i $PWD/vm/ubpf_loader.c 15 | - name: python 3.5 16 | env: PYTHON=python3 17 | before_install: 18 | - sudo apt-get update 19 | - sudo apt-get -y install python3 python3-pip python3-setuptools python3-wheel 20 | # command to install dependencies 21 | install: 22 | - $PYTHON -m pip install --upgrade "pip<21.0" 23 | - $PYTHON -m pip install -r requirements.txt 24 | - $PYTHON -m pip install cpp-coveralls 25 | # command to run tests 26 | script: 27 | - make -C vm COVERAGE=1 28 | - nosetests -v 29 | -------------------------------------------------------------------------------- /tests/string-stack.data: -------------------------------------------------------------------------------- 1 | -- c 2 | extern int strcmp_ext(const char *a, const char *b); 3 | 4 | int entry(int *mem) 5 | { 6 | char a[] = "abcx"; 7 | char b[] = "abcy"; 8 | 9 | if (strcmp_ext(a, a) != 0) { 10 | return 1; 11 | } 12 | 13 | if (strcmp_ext(a, b) == 0) { 14 | return 1; 15 | } 16 | 17 | return 0; 18 | } 19 | -- asm 20 | mov r1, 0x78636261 21 | stxw [r10-8], r1 22 | mov r6, 0x0 23 | stxb [r10-4], r6 24 | stxb [r10-12], r6 25 | mov r1, 0x79636261 26 | stxw [r10-16], r1 27 | mov r1, r10 28 | add r1, 0xfffffff8 29 | mov r2, r1 30 | call 0x4 31 | mov r1, r0 32 | mov r0, 0x1 33 | lsh r1, 0x20 34 | rsh r1, 0x20 35 | jne r1, 0x0, +11 36 | mov r1, r10 37 | add r1, 0xfffffff8 38 | mov r2, r10 39 | add r2, 0xfffffff0 40 | call 0x4 41 | mov r1, r0 42 | lsh r1, 0x20 43 | rsh r1, 0x20 44 | mov r0, 0x1 45 | jeq r1, r6, +1 46 | mov r0, 0x0 47 | exit 48 | -- result 49 | 0x0 50 | -- no register offset 51 | call instruction 52 | -------------------------------------------------------------------------------- /bin/ubpf-assembler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | eBPF assembler 4 | 5 | Very simple single-pass assembler. Only exists to assemble testcases 6 | for the interpreter. 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | 13 | ROOT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") 14 | if os.path.exists(os.path.join(ROOT_DIR, "ubpf")): 15 | # Running from source tree 16 | sys.path.insert(0, ROOT_DIR) 17 | 18 | import ubpf.assembler 19 | 20 | def main(): 21 | parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 22 | parser.add_argument('input', type=argparse.FileType('r'), default='-', nargs='?') 23 | parser.add_argument('output', type=argparse.FileType('wb'), default='-', nargs='?') 24 | args = parser.parse_args() 25 | 26 | if args.output.name == "" and hasattr(args.output, "buffer"): 27 | # python 3 28 | args.output.buffer.write(ubpf.assembler.assemble(args.input.read())) 29 | else: 30 | args.output.write(ubpf.assembler.assemble(args.input.read())) 31 | 32 | if __name__ == "__main__": 33 | main() 34 | -------------------------------------------------------------------------------- /bin/ubpf-disassembler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | eBPF disassembler 4 | 5 | Reads the given file or stdin. The input should be raw eBPF 6 | instructions (not an ELF object file). 7 | """ 8 | 9 | import argparse 10 | import os 11 | import sys 12 | 13 | ROOT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") 14 | if os.path.exists(os.path.join(ROOT_DIR, "ubpf")): 15 | # Running from source tree 16 | sys.path.insert(0, ROOT_DIR) 17 | 18 | import ubpf.disassembler 19 | 20 | def main(): 21 | parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 22 | parser.add_argument('input', type=argparse.FileType('rb'), default='-', nargs='?') 23 | parser.add_argument('output', type=argparse.FileType('w'), default='-', nargs='?') 24 | args = parser.parse_args() 25 | 26 | if args.input.name == "" and hasattr(args.input, "buffer"): 27 | # python 3 28 | input_ = args.input.buffer.read() 29 | else: 30 | input_ = args.input.read() 31 | 32 | args.output.write(ubpf.disassembler.disassemble(input_)) 33 | 34 | if __name__ == "__main__": 35 | main() 36 | -------------------------------------------------------------------------------- /test_framework/test_disassembler.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import difflib 3 | from nose.plugins.skip import Skip, SkipTest 4 | import ubpf.disassembler 5 | import testdata 6 | 7 | def check_datafile(filename): 8 | """ 9 | Verify that the result of disassembling the 'raw' section matches the 10 | 'asm' section. 11 | """ 12 | data = testdata.read(filename) 13 | if 'asm' not in data: 14 | raise SkipTest("no asm section in datafile") 15 | if 'raw' not in data: 16 | raise SkipTest("no raw section in datafile") 17 | 18 | binary = b''.join(struct.pack("=Q", x) for x in data['raw']) 19 | result = ubpf.disassembler.disassemble(binary) 20 | 21 | # TODO strip whitespace and comments from asm 22 | if result.strip() != data['asm'].strip(): 23 | diff = difflib.unified_diff(data['asm'].splitlines(), result.splitlines(), lineterm="") 24 | formatted = ''.join(' %s\n' % x for x in diff) 25 | raise AssertionError("Assembly differs:\n%s" % formatted) 26 | 27 | def test_datafiles(): 28 | # Nose test generator 29 | # Creates a testcase for each datafile 30 | for filename in testdata.list_files(): 31 | yield check_datafile, filename 32 | -------------------------------------------------------------------------------- /tests/tcp-sack/tcp-sack.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | struct tcp_option { 7 | uint8_t kind; 8 | uint8_t length; 9 | }; 10 | 11 | uint64_t entry(void *pkt) 12 | { 13 | struct ether_header *ether_header = (void *)pkt; 14 | 15 | if (ether_header->ether_type != __builtin_bswap16(0x0800)) { 16 | return 0; 17 | } 18 | 19 | struct iphdr *iphdr = (void *)(ether_header + 1); 20 | if (iphdr->protocol != 6) { 21 | return 0; 22 | } 23 | 24 | struct tcphdr *tcphdr = (void *)iphdr + iphdr->ihl*4; 25 | 26 | void *options_start = (void *)(tcphdr + 1); 27 | int options_length = tcphdr->doff * 4 - sizeof(*tcphdr); 28 | int offset = 0; 29 | 30 | while (offset < options_length) { 31 | struct tcp_option *option = options_start + offset; 32 | if (option->kind == 0) { 33 | /* End of options */ 34 | break; 35 | } else if (option->kind == 1) { 36 | /* NOP */ 37 | offset++; 38 | } else if (option->kind == 5) { 39 | /* SACK */ 40 | return 1; 41 | } 42 | 43 | offset += option->length; 44 | } 45 | 46 | return 0; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /vm/ubpf_int.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef UBPF_INT_H 18 | #define UBPF_INT_H 19 | 20 | #include 21 | #include "ebpf.h" 22 | 23 | struct ebpf_inst; 24 | typedef uint64_t (*ext_func)(uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4); 25 | 26 | struct ubpf_vm { 27 | struct ebpf_inst *insts; 28 | uint16_t num_insts; 29 | ubpf_jit_fn jitted; 30 | size_t jitted_size; 31 | ext_func *ext_funcs; 32 | const char **ext_func_names; 33 | bool bounds_check_enabled; 34 | int (*error_printf)(FILE* stream, const char* format, ...); 35 | }; 36 | 37 | char *ubpf_error(const char *fmt, ...); 38 | unsigned int ubpf_lookup_registered_function(struct ubpf_vm *vm, const char *name); 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /vm/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Big Switch Networks, Inc 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 | CFLAGS := -Wall -Werror -Iinc -O2 -g -Wunused-parameter 16 | LDLIBS := -lm 17 | 18 | INSTALL ?= install 19 | DESTDIR = 20 | PREFIX ?= /usr/local 21 | 22 | ifeq ($(COVERAGE),1) 23 | CFLAGS += -fprofile-arcs -ftest-coverage 24 | LDFLAGS += -fprofile-arcs 25 | endif 26 | 27 | ifeq ($(ASAN),1) 28 | CFLAGS += -fsanitize=address 29 | LDFLAGS += -fsanitize=address 30 | endif 31 | 32 | all: libubpf.a test 33 | 34 | ubpf_jit_x86_64.o: ubpf_jit_x86_64.c ubpf_jit_x86_64.h 35 | 36 | libubpf.a: ubpf_vm.o ubpf_jit_x86_64.o ubpf_loader.o 37 | ar rc $@ $^ 38 | 39 | test: test.o libubpf.a 40 | 41 | install: 42 | $(INSTALL) -d $(DESTDIR)$(PREFIX)/lib 43 | $(INSTALL) -m 644 libubpf.a $(DESTDIR)$(PREFIX)/lib 44 | $(INSTALL) -d $(DESTDIR)$(PREFIX)/include 45 | $(INSTALL) -m 644 inc/ubpf.h $(DESTDIR)$(PREFIX)/include 46 | 47 | clean: 48 | rm -f test libubpf.a *.o 49 | -------------------------------------------------------------------------------- /test_framework/test_assembler.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from nose.plugins.skip import Skip, SkipTest 3 | import ubpf.assembler 4 | import ubpf.disassembler 5 | import testdata 6 | try: 7 | xrange 8 | except NameError: 9 | xrange = range 10 | 11 | # Just for assertion messages 12 | def try_disassemble(inst): 13 | data = struct.pack("=Q", inst) 14 | try: 15 | return ubpf.disassembler.disassemble(data).strip() 16 | except ValueError: 17 | return "" 18 | 19 | def check_datafile(filename): 20 | """ 21 | Verify that the result of assembling the 'asm' section matches the 22 | 'raw' section. 23 | """ 24 | data = testdata.read(filename) 25 | if 'asm' not in data: 26 | raise SkipTest("no asm section in datafile") 27 | if 'raw' not in data: 28 | raise SkipTest("no raw section in datafile") 29 | 30 | bin_result = ubpf.assembler.assemble(data['asm']) 31 | assert len(bin_result) % 8 == 0 32 | assert len(bin_result) / 8 == len(data['raw']) 33 | 34 | for i in xrange(0, len(bin_result), 8): 35 | j = int(i/8) 36 | inst, = struct.unpack_from("=Q", bin_result[i:i+8]) 37 | exp = data['raw'][j] 38 | if exp != inst: 39 | raise AssertionError("Expected instruction %d to be %#x (%s), but was %#x (%s)" % 40 | (j, exp, try_disassemble(exp), inst, try_disassemble(inst))) 41 | 42 | def test_datafiles(): 43 | # Nose test generator 44 | # Creates a testcase for each datafile 45 | for filename in testdata.list_files(): 46 | yield check_datafile, filename 47 | -------------------------------------------------------------------------------- /test_framework/test_roundtrip.py: -------------------------------------------------------------------------------- 1 | import struct 2 | import difflib 3 | from nose.plugins.skip import Skip, SkipTest 4 | import ubpf.assembler 5 | import ubpf.disassembler 6 | import testdata 7 | 8 | # Just for assertion messages 9 | def try_disassemble(inst): 10 | data = struct.pack("=Q", inst) 11 | try: 12 | return ubpf.disassembler.disassemble(data).strip() 13 | except ValueError: 14 | return "" 15 | 16 | def check_datafile(filename): 17 | """ 18 | Verify that the reassembling the output of the disassembler produces 19 | the same binary, and that disassembling the output of the assembler 20 | produces the same text. 21 | """ 22 | data = testdata.read(filename) 23 | 24 | if 'asm' not in data: 25 | raise SkipTest("no asm section in datafile") 26 | 27 | assembled = ubpf.assembler.assemble(data['asm']) 28 | disassembled = ubpf.disassembler.disassemble(assembled) 29 | reassembled = ubpf.assembler.assemble(disassembled) 30 | disassembled2 = ubpf.disassembler.disassemble(reassembled) 31 | 32 | if disassembled != disassembled2: 33 | diff = difflib.unified_diff(disassembled.splitlines(), disassembled2.splitlines(), lineterm="") 34 | formatted = ''.join(' %s\n' % x for x in diff) 35 | raise AssertionError("Assembly differs:\n%s" % formatted) 36 | 37 | if assembled != reassembled: 38 | raise AssertionError("binary differs") 39 | 40 | def test_datafiles(): 41 | # Nose test generator 42 | # Creates a testcase for each datafile 43 | for filename in testdata.list_files(): 44 | yield check_datafile, filename 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | share/python-wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | MANIFEST 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .nox/ 43 | .coverage 44 | .coverage.* 45 | .cache 46 | nosetests.xml 47 | coverage.xml 48 | *.cover 49 | .hypothesis/ 50 | .pytest_cache/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | db.sqlite3 60 | 61 | # Flask stuff: 62 | instance/ 63 | .webassets-cache 64 | 65 | # Scrapy stuff: 66 | .scrapy 67 | 68 | # Sphinx documentation 69 | docs/_build/ 70 | 71 | # PyBuilder 72 | target/ 73 | 74 | # Jupyter Notebook 75 | .ipynb_checkpoints 76 | 77 | # IPython 78 | profile_default/ 79 | ipython_config.py 80 | 81 | # pyenv 82 | .python-version 83 | 84 | # celery beat schedule file 85 | celerybeat-schedule 86 | 87 | # SageMath parsed files 88 | *.sage.py 89 | 90 | # Environments 91 | .env 92 | .venv 93 | env/ 94 | venv/ 95 | ENV/ 96 | env.bak/ 97 | venv.bak/ 98 | 99 | # Spyder project settings 100 | .spyderproject 101 | .spyproject 102 | 103 | # Rope project settings 104 | .ropeproject 105 | 106 | # mkdocs documentation 107 | /site 108 | 109 | # mypy 110 | .mypy_cache/ 111 | .dmypy.json 112 | dmypy.json 113 | 114 | # Pyre type checker 115 | .pyre/ 116 | 117 | -------------------------------------------------------------------------------- /tests/subnet.data: -------------------------------------------------------------------------------- 1 | # Compiled by Clang 2 | # Check that the ipv4_dst is in 192.168.1.0/24 3 | -- c 4 | #include 5 | 6 | #define NETMASK 0xffffff00 7 | #define SUBNET 0xc0a80100 8 | 9 | struct eth_hdr { 10 | uint8_t eth_src[6]; 11 | uint8_t eth_dst[6]; 12 | uint16_t eth_type; 13 | }; 14 | 15 | struct vlan_hdr { 16 | uint16_t vlan; 17 | uint16_t eth_type; 18 | }; 19 | 20 | struct ipv4_hdr { 21 | uint8_t ver_ihl; 22 | uint8_t tos; 23 | uint16_t total_length; 24 | uint16_t id; 25 | uint16_t frag; 26 | uint8_t ttl; 27 | uint8_t proto; 28 | uint16_t csum; 29 | uint32_t src; 30 | uint32_t dst; 31 | }; 32 | 33 | uint64_t entry(void *mem) 34 | { 35 | struct eth_hdr *eth_hdr = (void *)mem; 36 | uint16_t eth_type; 37 | void *next = eth_hdr; 38 | 39 | if (eth_hdr->eth_type == __builtin_bswap16(0x8100)) { 40 | struct vlan_hdr *vlan_hdr = (void *)(eth_hdr + 1); 41 | eth_type = vlan_hdr->eth_type; 42 | next = vlan_hdr + 1; 43 | } else { 44 | eth_type = eth_hdr->eth_type; 45 | next = eth_hdr + 1; 46 | } 47 | 48 | if (eth_type == __builtin_bswap16(0x0800)) { 49 | struct ipv4_hdr *ipv4_hdr = next; 50 | if ((ipv4_hdr->dst & __builtin_bswap32(NETMASK)) == __builtin_bswap32(SUBNET)) { 51 | return 1; 52 | } 53 | } 54 | 55 | return 0; 56 | } 57 | -- asm 58 | mov r2, 0xe 59 | ldxh r3, [r1+12] 60 | jne r3, 0x81, +2 61 | mov r2, 0x12 62 | ldxh r3, [r1+16] 63 | and r3, 0xffff 64 | jne r3, 0x8, +5 65 | add r1, r2 66 | mov r0, 0x1 67 | ldxw r1, [r1+16] 68 | and r1, 0xffffff 69 | jeq r1, 0x1a8c0, +1 70 | mov r0, 0x0 71 | exit 72 | -- mem 73 | 00 00 c0 9f a0 97 00 a0 74 | cc 3b bf fa 08 00 45 10 75 | 00 3c 46 3c 40 00 40 06 76 | 73 1c c0 a8 01 02 c0 a8 77 | 01 01 06 0e 00 17 99 c5 78 | a0 ec 00 00 00 00 a0 02 79 | 7d 78 e0 a3 00 00 02 04 80 | 05 b4 04 02 08 0a 00 9c 81 | 27 24 00 00 00 00 01 03 82 | 03 00 83 | -- result 84 | 0x1 85 | -------------------------------------------------------------------------------- /test_framework/expand-testcase.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """ 3 | Expand testcase into individual files 4 | """ 5 | import os 6 | import sys 7 | import struct 8 | import testdata 9 | import argparse 10 | 11 | ROOT_DIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..") 12 | if os.path.exists(os.path.join(ROOT_DIR, "ubpf")): 13 | # Running from source tree 14 | sys.path.insert(0, ROOT_DIR) 15 | 16 | import ubpf.assembler 17 | import ubpf.disassembler 18 | 19 | def main(): 20 | parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 21 | parser.add_argument('name') 22 | parser.add_argument('path') 23 | args = parser.parse_args() 24 | 25 | data = testdata.read(args.name + '.data') 26 | assert data 27 | 28 | if not os.path.isdir(args.path): 29 | os.makedirs(args.path) 30 | 31 | def writefile(name, contents): 32 | open("%s/%s" % (args.path, name), "wb").write(contents) 33 | 34 | if 'mem' in data: 35 | writefile('mem', data['mem']) 36 | 37 | # Probably a packet, so write out a pcap file 38 | writefile('pcap', 39 | struct.pack('=IHHIIIIIIII', 40 | 0xa1b2c3d4, # magic 41 | 2, 4, # version 42 | 0, # time zone offset 43 | 0, # time stamp accuracy 44 | 65535, # snapshot length 45 | 1, # link layer type 46 | 0, 0, # timestamp 47 | len(data['mem']), # length 48 | len(data['mem'])) # length 49 | + data['mem']) 50 | 51 | if 'raw' in data: 52 | code = b''.join(struct.pack("=Q", x) for x in data['raw']) 53 | elif 'asm' in data: 54 | code = ubpf.assembler.assemble(data['asm']) 55 | else: 56 | code = None 57 | 58 | if code: 59 | writefile('code', code) 60 | 61 | if 'asm' in data: 62 | writefile('asm', data['asm'].encode()) 63 | elif code: 64 | writefile('asm', ubpf.disassembler.disassemble(code)) 65 | 66 | if 'pyelf' in data: 67 | from test_elf import generate_elf 68 | elf = generate_elf(data['pyelf']) 69 | writefile('elf', elf) 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uBPF 2 | 3 | Userspace eBPF VM 4 | 5 | [![Build Status](https://travis-ci.org/iovisor/ubpf.svg?branch=master)](https://travis-ci.org/iovisor/ubpf) 6 | [![Coverage Status](https://coveralls.io/repos/iovisor/ubpf/badge.svg?branch=master&service=github)](https://coveralls.io/github/iovisor/ubpf?branch=master) 7 | 8 | ## About 9 | 10 | This project aims to create an Apache-licensed library for executing eBPF programs. The primary implementation of eBPF lives in the Linux kernel, but due to its GPL license it can't be used in many projects. 11 | 12 | [Linux documentation for the eBPF instruction set](https://www.kernel.org/doc/Documentation/networking/filter.txt) 13 | 14 | [Instruction set reference](https://github.com/iovisor/bpf-docs/blob/master/eBPF.md) 15 | 16 | This project includes an eBPF assembler, disassembler, interpreter, 17 | and JIT compiler for x86-64. 18 | 19 | ## Building 20 | 21 | Run `make -C vm` to build the VM. This produces a static library `libubpf.a` 22 | and a simple executable used by the testsuite. After building the 23 | library you can install using `make -C vm install` via either root or 24 | sudo. 25 | 26 | ## Running the tests 27 | To run the tests, you first need to build the vm code then use nosetests to execute the tests. Note: The tests have some dependencies that need to be present. See the [.travis.yml](https://github.com/iovisor/ubpf/blob/master/.travis.yml) for details. 28 | 29 | ### Before running the test (assuming Debian derived distro) 30 | ``` 31 | sudo apt-get update 32 | sudo apt-get -y install python python-pip python-setuptools python-wheel python-nose 33 | python2 -m pip install --upgrade "pip<21.0" 34 | python2 -m pip install -r requirements.txt 35 | python2 -m pip install cpp-coveralls 36 | ``` 37 | 38 | ### Running the test 39 | ``` 40 | make -C vm COVERAGE=1 41 | nosetests -v # run tests 42 | ``` 43 | 44 | ### After running the test 45 | ``` 46 | coveralls --gcov-options '\-lp' -i $PWD/vm/ubpf_vm.c -i $PWD/vm/ubpf_jit_x86_64.c -i $PWD/vm/ubpf_loader.c 47 | ``` 48 | 49 | ## Compiling C to eBPF 50 | 51 | You'll need [Clang 3.7](http://llvm.org/releases/download.html#3.7.0). 52 | 53 | clang-3.7 -O2 -target bpf -c prog.c -o prog.o 54 | 55 | You can then pass the contents of `prog.o` to `ubpf_load_elf`, or to the stdin of 56 | the `vm/test` binary. 57 | 58 | ## Contributing 59 | 60 | Please fork the project on GitHub and open a pull request. You can run all the 61 | tests with `nosetests`. 62 | 63 | ## License 64 | 65 | Copyright 2015, Big Switch Networks, Inc. Licensed under the Apache License, Version 2.0 66 | . 67 | -------------------------------------------------------------------------------- /ubpf/asm_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from __future__ import print_function 3 | from parcon import * 4 | from collections import namedtuple 5 | 6 | hexchars = '0123456789abcdefABCDEF' 7 | 8 | Reg = namedtuple("Reg", ["num"]) 9 | Imm = namedtuple("Imm", ["value"]) 10 | MemRef = namedtuple("MemRef", ["reg", "offset"]) 11 | 12 | def keywords(vs): 13 | return First(*[Keyword(SignificantLiteral(v)) for v in vs]) 14 | 15 | hexnum = SignificantLiteral('0x') + +CharIn(hexchars) 16 | decnum = +Digit() 17 | offset = (CharIn("+-") + Exact(hexnum | decnum))[flatten]["".join][lambda x: int(x, 0)] 18 | imm = (-CharIn("+-") + Exact(hexnum | decnum))[flatten]["".join][lambda x: int(x, 0)][Imm] 19 | 20 | reg = Literal('r') + integer[int][Reg] 21 | memref = (Literal('[') + reg + Optional(offset, 0) + Literal(']'))[lambda x: MemRef(*x)] 22 | 23 | unary_alu_ops = ['neg', 'neg32', 'le16', 'le32', 'le64', 'be16', 'be32', 'be64'] 24 | binary_alu_ops = ['add', 'sub', 'mul', 'div', 'or', 'and', 'lsh', 'rsh', 25 | 'mod', 'xor', 'mov', 'arsh'] 26 | binary_alu_ops.extend([x + '32' for x in binary_alu_ops]) 27 | 28 | alu_instruction = \ 29 | (keywords(unary_alu_ops) + reg) | \ 30 | (keywords(binary_alu_ops) + reg + "," + (reg | imm)) 31 | 32 | mem_sizes = ['w', 'h', 'b', 'dw'] 33 | mem_store_reg_ops = ['stx' + s for s in mem_sizes] 34 | mem_store_imm_ops = ['st' + s for s in mem_sizes] 35 | mem_load_ops = ['ldx' + s for s in mem_sizes] 36 | 37 | mem_instruction = \ 38 | (keywords(mem_store_reg_ops) + memref + "," + reg) | \ 39 | (keywords(mem_store_imm_ops) + memref + "," + imm) | \ 40 | (keywords(mem_load_ops) + reg + "," + memref) | \ 41 | (keywords(["lddw"]) + reg + "," + imm) 42 | 43 | jmp_cmp_ops = ['jeq', 'jgt', 'jge', 'jlt', 'jle', 'jset', 'jne', 'jsgt', 'jsge', 'jslt', 'jsle'] 44 | jmp_instruction = \ 45 | (keywords(jmp_cmp_ops) + reg + "," + (reg | imm) + "," + offset) | \ 46 | (keywords(['ja']) + offset) | \ 47 | (keywords(['call']) + imm) | \ 48 | (keywords(['exit'])[lambda x: (x, )]) 49 | 50 | instruction = alu_instruction | mem_instruction | jmp_instruction 51 | 52 | start = ZeroOrMore(instruction + Optional(Literal(';'))) + End() 53 | 54 | def parse(source): 55 | return start.parse_string(source) 56 | 57 | if __name__ == "__main__": 58 | import argparse 59 | parser = argparse.ArgumentParser(description="Assembly parser", formatter_class=argparse.RawDescriptionHelpFormatter) 60 | parser.add_argument('file', type=argparse.FileType('r'), default='-') 61 | args = parser.parse_args() 62 | result = parse(args.file.read()) 63 | for inst in result: 64 | print(repr(inst)) 65 | -------------------------------------------------------------------------------- /test_framework/test_vm.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import struct 4 | import re 5 | from subprocess import Popen, PIPE 6 | from nose.plugins.skip import Skip, SkipTest 7 | import ubpf.assembler 8 | import testdata 9 | VM = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "vm", "test") 10 | 11 | def check_datafile(filename): 12 | """ 13 | Given assembly source code and an expected result, run the eBPF program and 14 | verify that the result matches. 15 | """ 16 | data = testdata.read(filename) 17 | if 'asm' not in data and 'raw' not in data: 18 | raise SkipTest("no asm or raw section in datafile") 19 | if 'result' not in data and 'error' not in data and 'error pattern' not in data: 20 | raise SkipTest("no result or error section in datafile") 21 | if not os.path.exists(VM): 22 | raise SkipTest("VM not found") 23 | 24 | if 'raw' in data: 25 | code = b''.join(struct.pack("=Q", x) for x in data['raw']) 26 | else: 27 | code = ubpf.assembler.assemble(data['asm']) 28 | 29 | memfile = None 30 | 31 | cmd = [VM] 32 | if 'mem' in data: 33 | memfile = tempfile.NamedTemporaryFile() 34 | memfile.write(data['mem']) 35 | memfile.flush() 36 | cmd.extend(['-m', memfile.name]) 37 | 38 | cmd.append('-') 39 | 40 | vm = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 41 | 42 | stdout, stderr = vm.communicate(code) 43 | stdout = stdout.decode("utf-8") 44 | stderr = stderr.decode("utf-8") 45 | stderr = stderr.strip() 46 | 47 | if memfile: 48 | memfile.close() 49 | 50 | if 'error' in data: 51 | if data['error'] != stderr: 52 | raise AssertionError("Expected error %r, got %r" % (data['error'], stderr)) 53 | elif 'error pattern' in data: 54 | if not re.search(data['error pattern'], stderr): 55 | raise AssertionError("Expected error matching %r, got %r" % (data['error pattern'], stderr)) 56 | else: 57 | if stderr: 58 | raise AssertionError("Unexpected error %r" % stderr) 59 | 60 | if 'result' in data: 61 | if vm.returncode != 0: 62 | raise AssertionError("VM exited with status %d, stderr=%r" % (vm.returncode, stderr)) 63 | expected = int(data['result'], 0) 64 | result = int(stdout, 0) 65 | if expected != result: 66 | raise AssertionError("Expected result 0x%x, got 0x%x, stderr=%r" % (expected, result, stderr)) 67 | else: 68 | if vm.returncode == 0: 69 | raise AssertionError("Expected VM to exit with an error code") 70 | 71 | def test_datafiles(): 72 | # Nose test generator 73 | # Creates a testcase for each datafile 74 | for filename in testdata.list_files(): 75 | yield check_datafile, filename 76 | -------------------------------------------------------------------------------- /test_framework/testdata.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | _test_data_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../tests") 5 | 6 | def list_files(): 7 | """ 8 | Return a list of data files under tests/data 9 | 10 | These strings are suitable to be passed to read(). 11 | """ 12 | 13 | result = [] 14 | for dirname, dirnames, filenames in os.walk(_test_data_dir): 15 | dirname = (os.path.relpath(dirname, _test_data_dir) + '/').replace('./', '') 16 | for filename in filenames: 17 | if filename.endswith('.data') and not filename.startswith('.'): 18 | result.append(dirname + filename) 19 | return sorted(result) 20 | 21 | def read(name): 22 | """ 23 | Read, parse, and return a test data file 24 | 25 | @param name Filename relative to the tests/data directory 26 | @returns A hash from section to the string contents 27 | """ 28 | 29 | section_lines = {} 30 | cur_section = None 31 | 32 | with open(os.path.join(_test_data_dir, name)) as f: 33 | for line in f: 34 | line = line.rstrip().partition('#')[0].rstrip() 35 | if line == '': 36 | continue 37 | elif line.startswith('--'): 38 | cur_section = line[2:].strip() 39 | if cur_section in section_lines: 40 | raise Exception("section %s already exists in the test data file") 41 | section_lines[cur_section] = [] 42 | elif cur_section: 43 | section_lines[cur_section].append(line) 44 | data = { section: '\n'.join(lines) for (section, lines) in list(section_lines.items()) } 45 | 46 | 47 | # Resolve links 48 | for k in list(data): 49 | if '@' in k: 50 | del data[k] 51 | section, path = k.split('@') 52 | section = section.strip() 53 | path = path.strip() 54 | fullpath = os.path.join(_test_data_dir, os.path.dirname(name), path) 55 | with open(fullpath) as f: 56 | data[section] = f.read() 57 | 58 | # Special case: convert 'raw' section into binary 59 | # Each line is parsed as an integer representing an instruction. 60 | if 'raw' in data: 61 | insts = [] 62 | for line in data['raw'].splitlines(): 63 | num, _, _ = line.partition("#") 64 | insts.append(int(num, 0)) 65 | data['raw'] = insts 66 | # 67 | # Special case: convert 'mem' section into binary 68 | # The string '00 11\n22 33' results in "\x00\x11\x22\x33" 69 | # Ignores hexdump prefix ending with a colon. 70 | if 'mem' in data: 71 | hex_strs = [] 72 | for line in data['mem'].splitlines(): 73 | if ':' in line: 74 | line = line[(line.rindex(':')+1):] 75 | hex_strs.extend(re.findall(r"[0-9A-Fa-f]{2}", line)) 76 | data['mem'] = bytes(bytearray([(int(x, 16)) for x in hex_strs])) 77 | 78 | return data 79 | -------------------------------------------------------------------------------- /test_framework/test_jit.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import struct 4 | import re 5 | from subprocess import Popen, PIPE 6 | from nose.plugins.skip import Skip, SkipTest 7 | import ubpf.assembler 8 | import testdata 9 | VM = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "vm", "test") 10 | try: 11 | xrange 12 | except NameError: 13 | xrange = range 14 | 15 | def check_datafile(filename): 16 | """ 17 | Given assembly source code and an expected result, run the eBPF program and 18 | verify that the result matches. Uses the JIT compiler. 19 | """ 20 | data = testdata.read(filename) 21 | if 'asm' not in data and 'raw' not in data: 22 | raise SkipTest("no asm or raw section in datafile") 23 | if 'result' not in data and 'error' not in data and 'error pattern' not in data: 24 | raise SkipTest("no result or error section in datafile") 25 | if not os.path.exists(VM): 26 | raise SkipTest("VM not found") 27 | if 'no jit' in data: 28 | raise SkipTest("JIT disabled for this testcase (%s)" % data['no jit']) 29 | 30 | if 'raw' in data: 31 | code = b''.join(struct.pack("=Q", x) for x in data['raw']) 32 | else: 33 | code = ubpf.assembler.assemble(data['asm']) 34 | 35 | memfile = None 36 | 37 | if 'mem' in data: 38 | memfile = tempfile.NamedTemporaryFile() 39 | memfile.write(data['mem']) 40 | memfile.flush() 41 | 42 | num_register_offsets = 20 43 | if 'no register offset' in data: 44 | # The JIT relies on a fixed register mapping for the call instruction 45 | num_register_offsets = 1 46 | 47 | try: 48 | for register_offset in xrange(0, num_register_offsets): 49 | cmd = [VM] 50 | if memfile: 51 | cmd.extend(['-m', memfile.name]) 52 | cmd.extend(['-j', '-r', str(register_offset), '-']) 53 | 54 | vm = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 55 | 56 | stdout, stderr = vm.communicate(code) 57 | stdout = stdout.decode("utf-8") 58 | stderr = stderr.decode("utf-8") 59 | stderr = stderr.strip() 60 | 61 | if 'error' in data: 62 | if data['error'] != stderr: 63 | raise AssertionError("Expected error %r, got %r" % (data['error'], stderr)) 64 | elif 'error pattern' in data: 65 | if not re.search(data['error pattern'], stderr): 66 | raise AssertionError("Expected error matching %r, got %r" % (data['error pattern'], stderr)) 67 | else: 68 | if stderr: 69 | raise AssertionError("Unexpected error %r" % stderr) 70 | 71 | if 'result' in data: 72 | if vm.returncode != 0: 73 | raise AssertionError("VM exited with status %d, stderr=%r" % (vm.returncode, stderr)) 74 | expected = int(data['result'], 0) 75 | result = int(stdout, 0) 76 | if expected != result: 77 | raise AssertionError("Expected result 0x%x, got 0x%x, stderr=%r" % (expected, result, stderr)) 78 | else: 79 | if vm.returncode == 0: 80 | raise AssertionError("Expected VM to exit with an error code") 81 | finally: 82 | if memfile: 83 | memfile.close() 84 | 85 | def test_datafiles(): 86 | # Nose test generator 87 | # Creates a testcase for each datafile 88 | for filename in testdata.list_files(): 89 | yield check_datafile, filename 90 | -------------------------------------------------------------------------------- /ubpf/assembler.py: -------------------------------------------------------------------------------- 1 | from .asm_parser import parse, Reg, Imm, MemRef 2 | import struct 3 | try: 4 | from StringIO import StringIO as io 5 | except ImportError: 6 | from io import BytesIO as io 7 | 8 | Inst = struct.Struct("BBHI") 9 | 10 | MEM_SIZES = { 11 | 'w': 0, 12 | 'h': 1, 13 | 'b': 2, 14 | 'dw': 3, 15 | } 16 | 17 | MEM_LOAD_OPS = { 'ldx' + k: (0x61 | (v << 3)) for k, v in list(MEM_SIZES.items()) } 18 | MEM_STORE_IMM_OPS = { 'st' + k: (0x62 | (v << 3)) for k, v in list(MEM_SIZES.items()) } 19 | MEM_STORE_REG_OPS = { 'stx' + k: (0x63 | (v << 3)) for k, v in list(MEM_SIZES.items()) } 20 | 21 | UNARY_ALU_OPS = { 22 | 'neg': 8, 23 | } 24 | 25 | BINARY_ALU_OPS = { 26 | 'add': 0, 27 | 'sub': 1, 28 | 'mul': 2, 29 | 'div': 3, 30 | 'or': 4, 31 | 'and': 5, 32 | 'lsh': 6, 33 | 'rsh': 7, 34 | 'mod': 9, 35 | 'xor': 10, 36 | 'mov': 11, 37 | 'arsh': 12, 38 | } 39 | 40 | UNARY_ALU32_OPS = { k + '32': v for k, v in list(UNARY_ALU_OPS.items()) } 41 | BINARY_ALU32_OPS = { k + '32': v for k, v in list(BINARY_ALU_OPS.items()) } 42 | 43 | END_OPS = { 44 | 'le16': (0xd4, 16), 45 | 'le32': (0xd4, 32), 46 | 'le64': (0xd4, 64), 47 | 'be16': (0xdc, 16), 48 | 'be32': (0xdc, 32), 49 | 'be64': (0xdc, 64), 50 | } 51 | 52 | JMP_CMP_OPS = { 53 | 'jeq': 1, 54 | 'jgt': 2, 55 | 'jge': 3, 56 | 'jset': 4, 57 | 'jne': 5, 58 | 'jsgt': 6, 59 | 'jsge': 7, 60 | 'jlt': 10, 61 | 'jle': 11, 62 | 'jslt': 12, 63 | 'jsle': 13, 64 | } 65 | 66 | JMP_MISC_OPS = { 67 | 'ja': 0, 68 | 'call': 8, 69 | 'exit': 9, 70 | } 71 | 72 | def pack(opcode, dst, src, offset, imm): 73 | return Inst.pack(opcode & 0xff, (dst | (src << 4)) & 0xff, offset & 0xffff, imm & 0xffffffff) 74 | 75 | def assemble_binop(op, cls, ops, dst, src, offset): 76 | opcode = cls | (ops[op] << 4) 77 | if isinstance(src, Imm): 78 | return pack(opcode, dst.num, 0, offset, src.value) 79 | else: 80 | return pack(opcode | 0x08, dst.num, src.num, offset, 0) 81 | 82 | def assemble_one(inst): 83 | op = inst[0] 84 | if op in MEM_LOAD_OPS: 85 | opcode = MEM_LOAD_OPS[op] 86 | return pack(opcode, inst[1].num, inst[2].reg.num, inst[2].offset, 0) 87 | elif op == "lddw": 88 | a = pack(0x18, inst[1].num, 0, 0, inst[2].value) 89 | b = pack(0, 0, 0, 0, inst[2].value >> 32) 90 | return a + b 91 | elif op in MEM_STORE_IMM_OPS: 92 | opcode = MEM_STORE_IMM_OPS[op] 93 | return pack(opcode, inst[1].reg.num, 0, inst[1].offset, inst[2].value) 94 | elif op in MEM_STORE_REG_OPS: 95 | opcode = MEM_STORE_REG_OPS[op] 96 | return pack(opcode, inst[1].reg.num, inst[2].num, inst[1].offset, 0) 97 | elif op in UNARY_ALU_OPS: 98 | opcode = 0x07 | (UNARY_ALU_OPS[op] << 4) 99 | return pack(opcode, inst[1].num, 0, 0, 0) 100 | elif op in UNARY_ALU32_OPS: 101 | opcode = 0x04 | (UNARY_ALU32_OPS[op] << 4) 102 | return pack(opcode, inst[1].num, 0, 0, 0) 103 | elif op in BINARY_ALU_OPS: 104 | return assemble_binop(op, 0x07, BINARY_ALU_OPS, inst[1], inst[2], 0) 105 | elif op in BINARY_ALU32_OPS: 106 | return assemble_binop(op, 0x04, BINARY_ALU32_OPS, inst[1], inst[2], 0) 107 | elif op in END_OPS: 108 | opcode, imm = END_OPS[op] 109 | return pack(opcode, inst[1].num, 0, 0, imm) 110 | elif op in JMP_CMP_OPS: 111 | return assemble_binop(op, 0x05, JMP_CMP_OPS, inst[1], inst[2], inst[3]) 112 | elif op in JMP_MISC_OPS: 113 | opcode = 0x05 | (JMP_MISC_OPS[op] << 4) 114 | if op == 'ja': 115 | return pack(opcode, 0, 0, inst[1], 0) 116 | elif op == 'call': 117 | return pack(opcode, 0, 0, 0, inst[1].value) 118 | elif op == 'exit': 119 | return pack(opcode, 0, 0, 0, 0) 120 | else: 121 | raise ValueError("unexpected instruction %r" % op) 122 | 123 | def assemble(source): 124 | insts = parse(source) 125 | output = io() 126 | for inst in insts: 127 | output.write(assemble_one(inst)) 128 | return output.getvalue() 129 | -------------------------------------------------------------------------------- /vm/inc/ubpf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef UBPF_H 18 | #define UBPF_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | // Default values for maximum instruction count and stack size. 25 | #if !defined(UBPF_MAX_INSTS) 26 | #define UBPF_MAX_INSTS 65536 27 | #endif 28 | 29 | #if !defined(UBPF_STACK_SIZE) 30 | #define UBPF_STACK_SIZE 128 31 | #endif 32 | 33 | struct ubpf_vm; 34 | typedef uint64_t (*ubpf_jit_fn)(void *mem, size_t mem_len); 35 | 36 | struct ubpf_vm *ubpf_create(void); 37 | void ubpf_destroy(struct ubpf_vm *vm); 38 | 39 | /* 40 | * Enable / disable bounds_check 41 | * 42 | * Bounds check is enabled by default, but it may be too restrictive 43 | * Pass true to enable, false to disable 44 | * Returns previous state 45 | */ 46 | bool ubpf_toggle_bounds_check(struct ubpf_vm *vm, bool enable); 47 | 48 | 49 | /* 50 | * Set the function to be invoked if the jitted program hits divide by zero. 51 | * 52 | * fprintf is the default function to be invoked on division by zero. 53 | */ 54 | void ubpf_set_error_print(struct ubpf_vm *vm, int (*error_printf)(FILE* stream, const char* format, ...)); 55 | 56 | /* 57 | * Register an external function 58 | * 59 | * The immediate field of a CALL instruction is an index into an array of 60 | * functions registered by the user. This API associates a function with 61 | * an index. 62 | * 63 | * 'name' should be a string with a lifetime longer than the VM. 64 | * 65 | * Returns 0 on success, -1 on error. 66 | */ 67 | int ubpf_register(struct ubpf_vm *vm, unsigned int idx, const char *name, void *fn); 68 | 69 | /* 70 | * Load code into a VM 71 | * 72 | * This must be done before calling ubpf_exec or ubpf_compile and after 73 | * registering all functions. 74 | * 75 | * 'code' should point to eBPF bytecodes and 'code_len' should be the size in 76 | * bytes of that buffer. 77 | * 78 | * Returns 0 on success, -1 on error. In case of error a pointer to the error 79 | * message will be stored in 'errmsg' and should be freed by the caller. 80 | */ 81 | int ubpf_load(struct ubpf_vm *vm, const void *code, uint32_t code_len, char **errmsg); 82 | 83 | /* 84 | * Load code from an ELF file 85 | * 86 | * This must be done before calling ubpf_exec or ubpf_compile and after 87 | * registering all functions. 88 | * 89 | * 'elf' should point to a copy of an ELF file in memory and 'elf_len' should 90 | * be the size in bytes of that buffer. 91 | * 92 | * The ELF file must be 64-bit little-endian with a single text section 93 | * containing the eBPF bytecodes. This is compatible with the output of 94 | * Clang. 95 | * 96 | * Returns 0 on success, -1 on error. In case of error a pointer to the error 97 | * message will be stored in 'errmsg' and should be freed by the caller. 98 | */ 99 | int ubpf_load_elf(struct ubpf_vm *vm, const void *elf, size_t elf_len, char **errmsg); 100 | 101 | uint64_t ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len); 102 | 103 | ubpf_jit_fn ubpf_compile(struct ubpf_vm *vm, char **errmsg); 104 | 105 | /* 106 | * Translate the eBPF byte code to x64 machine code, store in buffer, and 107 | * write the resulting count of bytes to size. 108 | * 109 | * This must be called after registering all functions. 110 | * 111 | * Returns 0 on success, -1 on error. In case of error a pointer to the error 112 | * message will be stored in 'errmsg' and should be freed by the caller. 113 | */ 114 | int ubpf_translate(struct ubpf_vm *vm, uint8_t *buffer, size_t *size, char **errmsg); 115 | #endif 116 | -------------------------------------------------------------------------------- /ubpf/disassembler.py: -------------------------------------------------------------------------------- 1 | import struct 2 | try: 3 | from StringIO import StringIO as io 4 | except ImportError: 5 | from io import StringIO as io 6 | 7 | Inst = struct.Struct("BBHI") 8 | 9 | CLASSES = { 10 | 0: "ld", 11 | 1: "ldx", 12 | 2: "st", 13 | 3: "stx", 14 | 4: "alu", 15 | 5: "jmp", 16 | 7: "alu64", 17 | } 18 | 19 | ALU_OPCODES = { 20 | 0: 'add', 21 | 1: 'sub', 22 | 2: 'mul', 23 | 3: 'div', 24 | 4: 'or', 25 | 5: 'and', 26 | 6: 'lsh', 27 | 7: 'rsh', 28 | 8: 'neg', 29 | 9: 'mod', 30 | 10: 'xor', 31 | 11: 'mov', 32 | 12: 'arsh', 33 | 13: '(endian)', 34 | } 35 | 36 | JMP_OPCODES = { 37 | 0: 'ja', 38 | 1: 'jeq', 39 | 2: 'jgt', 40 | 3: 'jge', 41 | 4: 'jset', 42 | 5: 'jne', 43 | 6: 'jsgt', 44 | 7: 'jsge', 45 | 8: 'call', 46 | 9: 'exit', 47 | 10: 'jlt', 48 | 11: 'jle', 49 | 12: 'jslt', 50 | 13: 'jsle', 51 | } 52 | 53 | MODES = { 54 | 0: 'imm', 55 | 1: 'abs', 56 | 2: 'ind', 57 | 3: 'mem', 58 | 6: 'xadd', 59 | } 60 | 61 | SIZES = { 62 | 0: 'w', 63 | 1: 'h', 64 | 2: 'b', 65 | 3: 'dw', 66 | } 67 | 68 | BPF_CLASS_LD = 0 69 | BPF_CLASS_LDX = 1 70 | BPF_CLASS_ST = 2 71 | BPF_CLASS_STX = 3 72 | BPF_CLASS_ALU = 4 73 | BPF_CLASS_JMP = 5 74 | BPF_CLASS_ALU64 = 7 75 | 76 | BPF_ALU_NEG = 8 77 | BPF_ALU_END = 13 78 | 79 | def R(reg): 80 | return "r" + str(reg) 81 | 82 | def I(imm): 83 | return "%#x" % imm 84 | 85 | def M(base, off): 86 | if off != 0: 87 | return "[%s%s]" % (base, O(off)) 88 | else: 89 | return "[%s]" % base 90 | 91 | def O(off): 92 | if off <= 32767: 93 | return "+" + str(off) 94 | else: 95 | return "-" + str(65536-off) 96 | 97 | def disassemble_one(data, offset): 98 | code, regs, off, imm = Inst.unpack_from(data, offset) 99 | dst_reg = regs & 0xf 100 | src_reg = (regs >> 4) & 0xf 101 | cls = code & 7 102 | 103 | class_name = CLASSES.get(cls) 104 | 105 | if cls == BPF_CLASS_ALU or cls == BPF_CLASS_ALU64: 106 | source = (code >> 3) & 1 107 | opcode = (code >> 4) & 0xf 108 | opcode_name = ALU_OPCODES.get(opcode) 109 | if cls == BPF_CLASS_ALU: 110 | opcode_name += "32" 111 | 112 | if opcode == BPF_ALU_END: 113 | opcode_name = source == 1 and "be" or "le" 114 | return "%s%d %s" % (opcode_name, imm, R(dst_reg)) 115 | elif opcode == BPF_ALU_NEG: 116 | return "%s %s" % (opcode_name, R(dst_reg)) 117 | elif source == 0: 118 | return "%s %s, %s" % (opcode_name, R(dst_reg), I(imm)) 119 | else: 120 | return "%s %s, %s" % (opcode_name, R(dst_reg), R(src_reg)) 121 | elif cls == BPF_CLASS_JMP: 122 | source = (code >> 3) & 1 123 | opcode = (code >> 4) & 0xf 124 | opcode_name = JMP_OPCODES.get(opcode) 125 | 126 | if opcode_name == "exit": 127 | return opcode_name 128 | elif opcode_name == "call": 129 | return "%s %s" % (opcode_name, I(imm)) 130 | elif opcode_name == "ja": 131 | return "%s %s" % (opcode_name, O(off)) 132 | elif source == 0: 133 | return "%s %s, %s, %s" % (opcode_name, R(dst_reg), I(imm), O(off)) 134 | else: 135 | return "%s %s, %s, %s" % (opcode_name, R(dst_reg), R(src_reg), O(off)) 136 | elif cls == BPF_CLASS_LD or cls == BPF_CLASS_LDX or cls == BPF_CLASS_ST or cls == BPF_CLASS_STX: 137 | size = (code >> 3) & 3 138 | mode = (code >> 5) & 7 139 | mode_name = MODES.get(mode, str(mode)) 140 | # TODO use different syntax for non-MEM instructions 141 | size_name = SIZES.get(size, str(size)) 142 | if code == 0x18: # lddw 143 | _, _, _, imm2 = Inst.unpack_from(data, offset+8) 144 | imm = (imm2 << 32) | imm 145 | return "%s %s, %s" % (class_name + size_name, R(dst_reg), I(imm)) 146 | elif code == 0x00: 147 | # Second instruction of lddw 148 | return None 149 | elif cls == BPF_CLASS_LDX: 150 | return "%s %s, %s" % (class_name + size_name, R(dst_reg), M(R(src_reg), off)) 151 | elif cls == BPF_CLASS_ST: 152 | return "%s %s, %s" % (class_name + size_name, M(R(dst_reg), off), I(imm)) 153 | elif cls == BPF_CLASS_STX: 154 | return "%s %s, %s" % (class_name + size_name, M(R(dst_reg), off), R(src_reg)) 155 | else: 156 | return "unknown mem instruction %#x" % code 157 | else: 158 | return "unknown instruction %#x" % code 159 | 160 | def disassemble(data): 161 | output = io() 162 | offset = 0 163 | while offset < len(data): 164 | s = disassemble_one(data, offset) 165 | if s: 166 | output.write(s + "\n") 167 | offset += 8 168 | return output.getvalue() 169 | -------------------------------------------------------------------------------- /test_framework/test_elf.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | import tempfile 4 | import struct 5 | import re 6 | import elftools.elf.structs 7 | from subprocess import Popen, PIPE 8 | from nose.plugins.skip import Skip, SkipTest 9 | from elftools.construct import Container 10 | from elftools.elf.constants import SH_FLAGS 11 | import testdata 12 | import ubpf.assembler 13 | VM = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "vm", "test") 14 | 15 | def template(): 16 | parts = {} 17 | parts['order'] = [] 18 | def add(name, value): 19 | parts[name] = value 20 | parts['order'].append(name) 21 | 22 | add('ehdr', Container( 23 | e_ident=Container( 24 | EI_MAG=[0x7f, ord('E'), ord('L'), ord('F')], 25 | EI_CLASS='ELFCLASS64', 26 | EI_DATA='ELFDATA2LSB', 27 | EI_VERSION='EV_CURRENT', 28 | EI_OSABI='ELFOSABI_SYSV', 29 | EI_ABIVERSION=0), 30 | e_type='ET_REL', 31 | e_machine='EM_NONE', 32 | e_version=0, 33 | e_entry=0, 34 | e_phoff=0, 35 | e_shoff=64, 36 | e_flags=0, 37 | e_ehsize=64, 38 | e_phentsize=0, 39 | e_phnum=0, 40 | e_shentsize=64, 41 | e_shnum=5, 42 | e_shstrndx=2)) 43 | 44 | add('first_shdr', Container( 45 | sh_name=0, 46 | sh_type='SHT_NULL', 47 | sh_flags=0, 48 | sh_addr=0, 49 | sh_offset=0, 50 | sh_size=0, 51 | sh_link=0, 52 | sh_info=0, 53 | sh_addralign=0, 54 | sh_entsize=0)) 55 | 56 | add('text_shdr', Container( 57 | sh_name=1, 58 | sh_type='SHT_PROGBITS', 59 | sh_flags=SH_FLAGS.SHF_ALLOC|SH_FLAGS.SHF_EXECINSTR, 60 | sh_addr=0, 61 | sh_offset=384, 62 | sh_size=24, 63 | sh_link=0, 64 | sh_info=0, 65 | sh_addralign=8, 66 | sh_entsize=0)) 67 | 68 | add('strtab_shdr', Container( 69 | sh_name=7, 70 | sh_type='SHT_STRTAB', 71 | sh_flags=0, 72 | sh_addr=0, 73 | sh_offset=408, 74 | sh_size=34, 75 | sh_link=0, 76 | sh_info=0, 77 | sh_addralign=1, 78 | sh_entsize=0)) 79 | 80 | add('symtab_shdr', Container( 81 | sh_name=15, 82 | sh_type='SHT_SYMTAB', 83 | sh_flags=0, 84 | sh_addr=0, 85 | sh_offset=442, 86 | sh_size=48, 87 | sh_link=2, 88 | sh_info=0, 89 | sh_addralign=8, 90 | sh_entsize=24)) 91 | 92 | add('rel_shdr', Container( 93 | sh_name=23, 94 | sh_type='SHT_REL', 95 | sh_flags=0, 96 | sh_addr=0, 97 | sh_offset=490, 98 | sh_size=16, 99 | sh_link=3, 100 | sh_info=1, 101 | sh_addralign=8, 102 | sh_entsize=16)) 103 | 104 | # return sqrti(42*42) 105 | asm = """ 106 | mov r1, 1764 107 | call 0xffffffff 108 | exit 109 | """ 110 | 111 | text = ubpf.assembler.assemble(asm) 112 | 113 | add("text", text) 114 | add("strtab", b"\0.text\0.strtab\0.symtab\0.rel\0sqrti\0") 115 | add("first_sym", Container( 116 | st_name=0, 117 | st_value=0, 118 | st_size=0, 119 | st_info=Container(bind='STB_WEAK', type='STT_FUNC'), 120 | st_other=Container(visibility='STV_DEFAULT'), 121 | st_shndx=0)) 122 | add("sqrti_sym", Container( 123 | st_name=28, 124 | st_value=0, 125 | st_size=0, 126 | st_info=Container(bind='STB_WEAK', type='STT_FUNC'), 127 | st_other=Container(visibility='STV_DEFAULT'), 128 | st_shndx=0)) 129 | add("sqrti_rel", Container( 130 | r_info=(1 << 32) | 2, 131 | r_info_sym=0, 132 | r_info_type=0, 133 | r_offset=8)) 134 | 135 | return parts 136 | 137 | def serialize(parts): 138 | s = elftools.elf.structs.ELFStructs(elfclass=64) 139 | s.create_basic_structs() 140 | ehdr = parts['ehdr'] 141 | s.create_advanced_structs(ehdr['e_type'], ehdr['e_machine'], ehdr['e_ident']['EI_OSABI']) 142 | tmp = [] 143 | offset = 0 144 | 145 | for name in parts['order']: 146 | part = parts[name] 147 | serializer = lambda x: x 148 | if name == 'ehdr': 149 | serializer = s.Elf_Ehdr.build 150 | elif name.endswith('shdr'): 151 | serializer = s.Elf_Shdr.build 152 | elif name.endswith('rel'): 153 | serializer = s.Elf_Rel.build 154 | elif name.endswith('sym'): 155 | serializer = s.Elf_Sym.build 156 | 157 | data = serializer(part) 158 | tmp.append(data) 159 | #sys.stderr.write("Wrote %s size %d at offset %d\n" % (name, len(data), offset)) 160 | offset += len(data) 161 | 162 | return b''.join(tmp) 163 | 164 | def generate_elf(pyelf): 165 | parts = template() 166 | exec(pyelf, parts) 167 | return serialize(parts) 168 | 169 | def check_datafile(filename): 170 | """ 171 | """ 172 | data = testdata.read(filename) 173 | if 'pyelf' not in data: 174 | raise SkipTest("no pyelf section in datafile") 175 | if 'result' not in data and 'error' not in data and 'error pattern' not in data: 176 | raise SkipTest("no result or error section in datafile") 177 | if not os.path.exists(VM): 178 | raise SkipTest("VM not found") 179 | 180 | elf = generate_elf(data['pyelf']) 181 | 182 | cmd = [VM] 183 | 184 | cmd.append('-') 185 | 186 | vm = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE) 187 | 188 | stdout, stderr = vm.communicate(elf) 189 | stdout = stdout.decode("utf-8") 190 | stderr = stderr.decode("utf-8") 191 | stderr = stderr.strip() 192 | 193 | if 'error' in data: 194 | if data['error'] != stderr: 195 | raise AssertionError("Expected error %r, got %r" % (data['error'], stderr)) 196 | elif 'error pattern' in data: 197 | if not re.search(data['error pattern'], stderr): 198 | raise AssertionError("Expected error matching %r, got %r" % (data['error pattern'], stderr)) 199 | else: 200 | if stderr: 201 | raise AssertionError("Unexpected error %r" % stderr) 202 | 203 | if 'result' in data: 204 | if vm.returncode != 0: 205 | raise AssertionError("VM exited with status %d, stderr=%r" % (vm.returncode, stderr)) 206 | expected = int(data['result'], 0) 207 | result = int(stdout, 0) 208 | if expected != result: 209 | raise AssertionError("Expected result 0x%x, got 0x%x, stderr=%r" % (expected, result, stderr)) 210 | else: 211 | if vm.returncode == 0: 212 | raise AssertionError("Expected VM to exit with an error code") 213 | 214 | def test_datafiles(): 215 | # Nose test generator 216 | # Creates a testcase for each datafile 217 | for filename in testdata.list_files(): 218 | yield check_datafile, filename 219 | -------------------------------------------------------------------------------- /vm/test.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * Copyright 2017 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #define _GNU_SOURCE 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "ubpf.h" 30 | 31 | void ubpf_set_register_offset(int x); 32 | static void *readfile(const char *path, size_t maxlen, size_t *len); 33 | static void register_functions(struct ubpf_vm *vm); 34 | 35 | static void usage(const char *name) 36 | { 37 | fprintf(stderr, "usage: %s [-h] [-j|--jit] [-m|--mem PATH] BINARY\n", name); 38 | fprintf(stderr, "\nExecutes the eBPF code in BINARY and prints the result to stdout.\n"); 39 | fprintf(stderr, "If --mem is given then the specified file will be read and a pointer\nto its data passed in r1.\n"); 40 | fprintf(stderr, "If --jit is given then the JIT compiler will be used.\n"); 41 | fprintf(stderr, "\nOther options:\n"); 42 | fprintf(stderr, " -r, --register-offset NUM: Change the mapping from eBPF to x86 registers\n"); 43 | } 44 | 45 | int main(int argc, char **argv) 46 | { 47 | struct option longopts[] = { 48 | { .name = "help", .val = 'h', }, 49 | { .name = "mem", .val = 'm', .has_arg=1 }, 50 | { .name = "jit", .val = 'j' }, 51 | { .name = "register-offset", .val = 'r', .has_arg=1 }, 52 | { } 53 | }; 54 | 55 | const char *mem_filename = NULL; 56 | bool jit = false; 57 | 58 | int opt; 59 | while ((opt = getopt_long(argc, argv, "hm:jr:", longopts, NULL)) != -1) { 60 | switch (opt) { 61 | case 'm': 62 | mem_filename = optarg; 63 | break; 64 | case 'j': 65 | jit = true; 66 | break; 67 | case 'r': 68 | ubpf_set_register_offset(atoi(optarg)); 69 | break; 70 | case 'h': 71 | usage(argv[0]); 72 | return 0; 73 | default: 74 | usage(argv[0]); 75 | return 1; 76 | } 77 | } 78 | 79 | if (argc != optind + 1) { 80 | usage(argv[0]); 81 | return 1; 82 | } 83 | 84 | const char *code_filename = argv[optind]; 85 | size_t code_len; 86 | void *code = readfile(code_filename, 1024*1024, &code_len); 87 | if (code == NULL) { 88 | return 1; 89 | } 90 | 91 | size_t mem_len = 0; 92 | void *mem = NULL; 93 | if (mem_filename != NULL) { 94 | mem = readfile(mem_filename, 1024*1024, &mem_len); 95 | if (mem == NULL) { 96 | return 1; 97 | } 98 | } 99 | 100 | struct ubpf_vm *vm = ubpf_create(); 101 | if (!vm) { 102 | fprintf(stderr, "Failed to create VM\n"); 103 | return 1; 104 | } 105 | 106 | register_functions(vm); 107 | 108 | /* 109 | * The ELF magic corresponds to an RSH instruction with an offset, 110 | * which is invalid. 111 | */ 112 | bool elf = code_len >= SELFMAG && !memcmp(code, ELFMAG, SELFMAG); 113 | 114 | char *errmsg; 115 | int rv; 116 | if (elf) { 117 | rv = ubpf_load_elf(vm, code, code_len, &errmsg); 118 | } else { 119 | rv = ubpf_load(vm, code, code_len, &errmsg); 120 | } 121 | 122 | free(code); 123 | 124 | if (rv < 0) { 125 | fprintf(stderr, "Failed to load code: %s\n", errmsg); 126 | free(errmsg); 127 | ubpf_destroy(vm); 128 | return 1; 129 | } 130 | 131 | uint64_t ret; 132 | 133 | if (jit) { 134 | ubpf_jit_fn fn = ubpf_compile(vm, &errmsg); 135 | if (fn == NULL) { 136 | fprintf(stderr, "Failed to compile: %s\n", errmsg); 137 | free(errmsg); 138 | return 1; 139 | } 140 | ret = fn(mem, mem_len); 141 | } else { 142 | ret = ubpf_exec(vm, mem, mem_len); 143 | } 144 | 145 | printf("0x%"PRIx64"\n", ret); 146 | 147 | ubpf_destroy(vm); 148 | 149 | return 0; 150 | } 151 | 152 | static void *readfile(const char *path, size_t maxlen, size_t *len) 153 | { 154 | FILE *file; 155 | if (!strcmp(path, "-")) { 156 | file = fdopen(STDIN_FILENO, "r"); 157 | } else { 158 | file = fopen(path, "r"); 159 | } 160 | 161 | if (file == NULL) { 162 | fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno)); 163 | return NULL; 164 | } 165 | 166 | void *data = calloc(maxlen, 1); 167 | size_t offset = 0; 168 | size_t rv; 169 | while ((rv = fread(data+offset, 1, maxlen-offset, file)) > 0) { 170 | offset += rv; 171 | } 172 | 173 | if (ferror(file)) { 174 | fprintf(stderr, "Failed to read %s: %s\n", path, strerror(errno)); 175 | fclose(file); 176 | free(data); 177 | return NULL; 178 | } 179 | 180 | if (!feof(file)) { 181 | fprintf(stderr, "Failed to read %s because it is too large (max %u bytes)\n", 182 | path, (unsigned)maxlen); 183 | fclose(file); 184 | free(data); 185 | return NULL; 186 | } 187 | 188 | fclose(file); 189 | if (len) { 190 | *len = offset; 191 | } 192 | return data; 193 | } 194 | 195 | static uint64_t 196 | gather_bytes(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e) 197 | { 198 | return ((uint64_t)a << 32) | 199 | ((uint32_t)b << 24) | 200 | ((uint32_t)c << 16) | 201 | ((uint16_t)d << 8) | 202 | e; 203 | } 204 | 205 | static void 206 | trash_registers(void) 207 | { 208 | /* Overwrite all caller-save registers */ 209 | asm( 210 | "mov $0xf0, %rax;" 211 | "mov $0xf1, %rcx;" 212 | "mov $0xf2, %rdx;" 213 | "mov $0xf3, %rsi;" 214 | "mov $0xf4, %rdi;" 215 | "mov $0xf5, %r8;" 216 | "mov $0xf6, %r9;" 217 | "mov $0xf7, %r10;" 218 | "mov $0xf8, %r11;" 219 | ); 220 | } 221 | 222 | static uint32_t 223 | sqrti(uint32_t x) 224 | { 225 | return sqrt(x); 226 | } 227 | 228 | static void 229 | register_functions(struct ubpf_vm *vm) 230 | { 231 | ubpf_register(vm, 0, "gather_bytes", gather_bytes); 232 | ubpf_register(vm, 1, "memfrob", memfrob); 233 | ubpf_register(vm, 2, "trash_registers", trash_registers); 234 | ubpf_register(vm, 3, "sqrti", sqrti); 235 | ubpf_register(vm, 4, "strcmp_ext", strcmp); 236 | } 237 | -------------------------------------------------------------------------------- /vm/ebpf.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #ifndef EBPF_H 18 | #define EBPF_H 19 | 20 | #include 21 | 22 | /* eBPF definitions */ 23 | 24 | struct ebpf_inst { 25 | uint8_t opcode; 26 | uint8_t dst : 4; 27 | uint8_t src : 4; 28 | int16_t offset; 29 | int32_t imm; 30 | }; 31 | 32 | #define EBPF_CLS_MASK 0x07 33 | #define EBPF_ALU_OP_MASK 0xf0 34 | 35 | #define EBPF_CLS_LD 0x00 36 | #define EBPF_CLS_LDX 0x01 37 | #define EBPF_CLS_ST 0x02 38 | #define EBPF_CLS_STX 0x03 39 | #define EBPF_CLS_ALU 0x04 40 | #define EBPF_CLS_JMP 0x05 41 | #define EBPF_CLS_ALU64 0x07 42 | 43 | #define EBPF_SRC_IMM 0x00 44 | #define EBPF_SRC_REG 0x08 45 | 46 | #define EBPF_SIZE_W 0x00 47 | #define EBPF_SIZE_H 0x08 48 | #define EBPF_SIZE_B 0x10 49 | #define EBPF_SIZE_DW 0x18 50 | 51 | /* Other memory modes are not yet supported */ 52 | #define EBPF_MODE_IMM 0x00 53 | #define EBPF_MODE_MEM 0x60 54 | 55 | #define EBPF_OP_ADD_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x00) 56 | #define EBPF_OP_ADD_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x00) 57 | #define EBPF_OP_SUB_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x10) 58 | #define EBPF_OP_SUB_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x10) 59 | #define EBPF_OP_MUL_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x20) 60 | #define EBPF_OP_MUL_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x20) 61 | #define EBPF_OP_DIV_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x30) 62 | #define EBPF_OP_DIV_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x30) 63 | #define EBPF_OP_OR_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x40) 64 | #define EBPF_OP_OR_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x40) 65 | #define EBPF_OP_AND_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x50) 66 | #define EBPF_OP_AND_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x50) 67 | #define EBPF_OP_LSH_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x60) 68 | #define EBPF_OP_LSH_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x60) 69 | #define EBPF_OP_RSH_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x70) 70 | #define EBPF_OP_RSH_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x70) 71 | #define EBPF_OP_NEG (EBPF_CLS_ALU|0x80) 72 | #define EBPF_OP_MOD_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0x90) 73 | #define EBPF_OP_MOD_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0x90) 74 | #define EBPF_OP_XOR_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0xa0) 75 | #define EBPF_OP_XOR_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0xa0) 76 | #define EBPF_OP_MOV_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0xb0) 77 | #define EBPF_OP_MOV_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0xb0) 78 | #define EBPF_OP_ARSH_IMM (EBPF_CLS_ALU|EBPF_SRC_IMM|0xc0) 79 | #define EBPF_OP_ARSH_REG (EBPF_CLS_ALU|EBPF_SRC_REG|0xc0) 80 | #define EBPF_OP_LE (EBPF_CLS_ALU|EBPF_SRC_IMM|0xd0) 81 | #define EBPF_OP_BE (EBPF_CLS_ALU|EBPF_SRC_REG|0xd0) 82 | 83 | #define EBPF_OP_ADD64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x00) 84 | #define EBPF_OP_ADD64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x00) 85 | #define EBPF_OP_SUB64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x10) 86 | #define EBPF_OP_SUB64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x10) 87 | #define EBPF_OP_MUL64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x20) 88 | #define EBPF_OP_MUL64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x20) 89 | #define EBPF_OP_DIV64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x30) 90 | #define EBPF_OP_DIV64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x30) 91 | #define EBPF_OP_OR64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x40) 92 | #define EBPF_OP_OR64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x40) 93 | #define EBPF_OP_AND64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x50) 94 | #define EBPF_OP_AND64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x50) 95 | #define EBPF_OP_LSH64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x60) 96 | #define EBPF_OP_LSH64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x60) 97 | #define EBPF_OP_RSH64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x70) 98 | #define EBPF_OP_RSH64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x70) 99 | #define EBPF_OP_NEG64 (EBPF_CLS_ALU64|0x80) 100 | #define EBPF_OP_MOD64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0x90) 101 | #define EBPF_OP_MOD64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0x90) 102 | #define EBPF_OP_XOR64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0xa0) 103 | #define EBPF_OP_XOR64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0xa0) 104 | #define EBPF_OP_MOV64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0xb0) 105 | #define EBPF_OP_MOV64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0xb0) 106 | #define EBPF_OP_ARSH64_IMM (EBPF_CLS_ALU64|EBPF_SRC_IMM|0xc0) 107 | #define EBPF_OP_ARSH64_REG (EBPF_CLS_ALU64|EBPF_SRC_REG|0xc0) 108 | 109 | #define EBPF_OP_LDXW (EBPF_CLS_LDX|EBPF_MODE_MEM|EBPF_SIZE_W) 110 | #define EBPF_OP_LDXH (EBPF_CLS_LDX|EBPF_MODE_MEM|EBPF_SIZE_H) 111 | #define EBPF_OP_LDXB (EBPF_CLS_LDX|EBPF_MODE_MEM|EBPF_SIZE_B) 112 | #define EBPF_OP_LDXDW (EBPF_CLS_LDX|EBPF_MODE_MEM|EBPF_SIZE_DW) 113 | #define EBPF_OP_STW (EBPF_CLS_ST|EBPF_MODE_MEM|EBPF_SIZE_W) 114 | #define EBPF_OP_STH (EBPF_CLS_ST|EBPF_MODE_MEM|EBPF_SIZE_H) 115 | #define EBPF_OP_STB (EBPF_CLS_ST|EBPF_MODE_MEM|EBPF_SIZE_B) 116 | #define EBPF_OP_STDW (EBPF_CLS_ST|EBPF_MODE_MEM|EBPF_SIZE_DW) 117 | #define EBPF_OP_STXW (EBPF_CLS_STX|EBPF_MODE_MEM|EBPF_SIZE_W) 118 | #define EBPF_OP_STXH (EBPF_CLS_STX|EBPF_MODE_MEM|EBPF_SIZE_H) 119 | #define EBPF_OP_STXB (EBPF_CLS_STX|EBPF_MODE_MEM|EBPF_SIZE_B) 120 | #define EBPF_OP_STXDW (EBPF_CLS_STX|EBPF_MODE_MEM|EBPF_SIZE_DW) 121 | #define EBPF_OP_LDDW (EBPF_CLS_LD|EBPF_MODE_IMM|EBPF_SIZE_DW) 122 | 123 | #define EBPF_OP_JA (EBPF_CLS_JMP|0x00) 124 | #define EBPF_OP_JEQ_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0x10) 125 | #define EBPF_OP_JEQ_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x10) 126 | #define EBPF_OP_JGT_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0x20) 127 | #define EBPF_OP_JGT_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x20) 128 | #define EBPF_OP_JGE_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0x30) 129 | #define EBPF_OP_JGE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x30) 130 | #define EBPF_OP_JSET_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x40) 131 | #define EBPF_OP_JSET_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0x40) 132 | #define EBPF_OP_JNE_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0x50) 133 | #define EBPF_OP_JNE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x50) 134 | #define EBPF_OP_JSGT_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0x60) 135 | #define EBPF_OP_JSGT_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x60) 136 | #define EBPF_OP_JSGE_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0x70) 137 | #define EBPF_OP_JSGE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x70) 138 | #define EBPF_OP_CALL (EBPF_CLS_JMP|0x80) 139 | #define EBPF_OP_EXIT (EBPF_CLS_JMP|0x90) 140 | #define EBPF_OP_JLT_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xa0) 141 | #define EBPF_OP_JLT_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xa0) 142 | #define EBPF_OP_JLE_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xb0) 143 | #define EBPF_OP_JLE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xb0) 144 | #define EBPF_OP_JSLT_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xc0) 145 | #define EBPF_OP_JSLT_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xc0) 146 | #define EBPF_OP_JSLE_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xd0) 147 | #define EBPF_OP_JSLE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xd0) 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /vm/ubpf_loader.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define _GNU_SOURCE 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include "ubpf_int.h" 25 | #include 26 | 27 | #define MAX_SECTIONS 32 28 | 29 | #ifndef EM_BPF 30 | #define EM_BPF 247 31 | #endif 32 | 33 | struct bounds { 34 | const void *base; 35 | uint64_t size; 36 | }; 37 | 38 | struct section { 39 | const Elf64_Shdr *shdr; 40 | const void *data; 41 | uint64_t size; 42 | }; 43 | 44 | static const void * 45 | bounds_check(struct bounds *bounds, uint64_t offset, uint64_t size) 46 | { 47 | if (offset + size > bounds->size || offset + size < offset) { 48 | return NULL; 49 | } 50 | return bounds->base + offset; 51 | } 52 | 53 | int 54 | ubpf_load_elf(struct ubpf_vm *vm, const void *elf, size_t elf_size, char **errmsg) 55 | { 56 | struct bounds b = { .base=elf, .size=elf_size }; 57 | void *text_copy = NULL; 58 | int i; 59 | 60 | const Elf64_Ehdr *ehdr = bounds_check(&b, 0, sizeof(*ehdr)); 61 | if (!ehdr) { 62 | *errmsg = ubpf_error("not enough data for ELF header"); 63 | goto error; 64 | } 65 | 66 | if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { 67 | *errmsg = ubpf_error("wrong magic"); 68 | goto error; 69 | } 70 | 71 | if (ehdr->e_ident[EI_CLASS] != ELFCLASS64) { 72 | *errmsg = ubpf_error("wrong class"); 73 | goto error; 74 | } 75 | 76 | if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { 77 | *errmsg = ubpf_error("wrong byte order"); 78 | goto error; 79 | } 80 | 81 | if (ehdr->e_ident[EI_VERSION] != 1) { 82 | *errmsg = ubpf_error("wrong version"); 83 | goto error; 84 | } 85 | 86 | if (ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) { 87 | *errmsg = ubpf_error("wrong OS ABI"); 88 | goto error; 89 | } 90 | 91 | if (ehdr->e_type != ET_REL) { 92 | *errmsg = ubpf_error("wrong type, expected relocatable"); 93 | goto error; 94 | } 95 | 96 | if (ehdr->e_machine != EM_NONE && ehdr->e_machine != EM_BPF) { 97 | *errmsg = ubpf_error("wrong machine, expected none or BPF, got %d", 98 | ehdr->e_machine); 99 | goto error; 100 | } 101 | 102 | if (ehdr->e_shnum > MAX_SECTIONS) { 103 | *errmsg = ubpf_error("too many sections"); 104 | goto error; 105 | } 106 | 107 | /* Parse section headers into an array */ 108 | struct section sections[MAX_SECTIONS]; 109 | for (i = 0; i < ehdr->e_shnum; i++) { 110 | const Elf64_Shdr *shdr = bounds_check(&b, ehdr->e_shoff + i*ehdr->e_shentsize, sizeof(*shdr)); 111 | if (!shdr) { 112 | *errmsg = ubpf_error("bad section header offset or size"); 113 | goto error; 114 | } 115 | 116 | const void *data = bounds_check(&b, shdr->sh_offset, shdr->sh_size); 117 | if (!data) { 118 | *errmsg = ubpf_error("bad section offset or size"); 119 | goto error; 120 | } 121 | 122 | sections[i].shdr = shdr; 123 | sections[i].data = data; 124 | sections[i].size = shdr->sh_size; 125 | } 126 | 127 | /* Find first text section */ 128 | int text_shndx = 0; 129 | for (i = 0; i < ehdr->e_shnum; i++) { 130 | const Elf64_Shdr *shdr = sections[i].shdr; 131 | if (shdr->sh_type == SHT_PROGBITS && 132 | shdr->sh_flags == (SHF_ALLOC|SHF_EXECINSTR)) { 133 | text_shndx = i; 134 | break; 135 | } 136 | } 137 | 138 | if (!text_shndx) { 139 | *errmsg = ubpf_error("text section not found"); 140 | goto error; 141 | } 142 | 143 | struct section *text = §ions[text_shndx]; 144 | 145 | /* May need to modify text for relocations, so make a copy */ 146 | text_copy = malloc(text->size); 147 | if (!text_copy) { 148 | *errmsg = ubpf_error("failed to allocate memory"); 149 | goto error; 150 | } 151 | memcpy(text_copy, text->data, text->size); 152 | 153 | /* Process each relocation section */ 154 | for (i = 0; i < ehdr->e_shnum; i++) { 155 | struct section *rel = §ions[i]; 156 | if (rel->shdr->sh_type != SHT_REL) { 157 | continue; 158 | } else if (rel->shdr->sh_info != text_shndx) { 159 | continue; 160 | } 161 | 162 | const Elf64_Rel *rs = rel->data; 163 | 164 | if (rel->shdr->sh_link >= ehdr->e_shnum) { 165 | *errmsg = ubpf_error("bad symbol table section index"); 166 | goto error; 167 | } 168 | 169 | struct section *symtab = §ions[rel->shdr->sh_link]; 170 | const Elf64_Sym *syms = symtab->data; 171 | uint32_t num_syms = symtab->size/sizeof(syms[0]); 172 | 173 | if (symtab->shdr->sh_link >= ehdr->e_shnum) { 174 | *errmsg = ubpf_error("bad string table section index"); 175 | goto error; 176 | } 177 | 178 | struct section *strtab = §ions[symtab->shdr->sh_link]; 179 | const char *strings = strtab->data; 180 | 181 | int j; 182 | for (j = 0; j < rel->size/sizeof(Elf64_Rel); j++) { 183 | const Elf64_Rel *r = &rs[j]; 184 | 185 | if (ELF64_R_TYPE(r->r_info) != 2) { 186 | *errmsg = ubpf_error("bad relocation type %u", ELF64_R_TYPE(r->r_info)); 187 | goto error; 188 | } 189 | 190 | uint32_t sym_idx = ELF64_R_SYM(r->r_info); 191 | if (sym_idx >= num_syms) { 192 | *errmsg = ubpf_error("bad symbol index"); 193 | goto error; 194 | } 195 | 196 | const Elf64_Sym *sym = &syms[sym_idx]; 197 | 198 | if (sym->st_name >= strtab->size) { 199 | *errmsg = ubpf_error("bad symbol name"); 200 | goto error; 201 | } 202 | 203 | const char *sym_name = strings + sym->st_name; 204 | 205 | if (r->r_offset + 8 > text->size) { 206 | *errmsg = ubpf_error("bad relocation offset"); 207 | goto error; 208 | } 209 | 210 | unsigned int imm = ubpf_lookup_registered_function(vm, sym_name); 211 | if (imm == -1) { 212 | *errmsg = ubpf_error("function '%s' not found", sym_name); 213 | goto error; 214 | } 215 | 216 | *(uint32_t *)(text_copy + r->r_offset + 4) = imm; 217 | } 218 | } 219 | 220 | int rv = ubpf_load(vm, text_copy, sections[text_shndx].size, errmsg); 221 | free(text_copy); 222 | return rv; 223 | 224 | error: 225 | free(text_copy); 226 | return -1; 227 | } 228 | -------------------------------------------------------------------------------- /vm/ubpf_jit_x86_64.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | * Generic x86-64 code generation functions 19 | */ 20 | 21 | #ifndef UBPF_JIT_X86_64_H 22 | #define UBPF_JIT_X86_64_H 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #define RAX 0 29 | #define RCX 1 30 | #define RDX 2 31 | #define RBX 3 32 | #define RSP 4 33 | #define RBP 5 34 | #define RSI 6 35 | #define RDI 7 36 | #define R8 8 37 | #define R9 9 38 | #define R10 10 39 | #define R11 11 40 | #define R12 12 41 | #define R13 13 42 | #define R14 14 43 | #define R15 15 44 | 45 | enum operand_size { 46 | S8, 47 | S16, 48 | S32, 49 | S64, 50 | }; 51 | 52 | struct jump { 53 | uint32_t offset_loc; 54 | uint32_t target_pc; 55 | }; 56 | 57 | struct jit_state { 58 | uint8_t *buf; 59 | uint32_t offset; 60 | uint32_t size; 61 | uint32_t *pc_locs; 62 | uint32_t exit_loc; 63 | uint32_t div_by_zero_loc; 64 | struct jump *jumps; 65 | int num_jumps; 66 | }; 67 | 68 | static inline void 69 | emit_bytes(struct jit_state *state, void *data, uint32_t len) 70 | { 71 | assert(state->offset <= state->size - len); 72 | memcpy(state->buf + state->offset, data, len); 73 | state->offset += len; 74 | } 75 | 76 | static inline void 77 | emit1(struct jit_state *state, uint8_t x) 78 | { 79 | emit_bytes(state, &x, sizeof(x)); 80 | } 81 | 82 | static inline void 83 | emit2(struct jit_state *state, uint16_t x) 84 | { 85 | emit_bytes(state, &x, sizeof(x)); 86 | } 87 | 88 | static inline void 89 | emit4(struct jit_state *state, uint32_t x) 90 | { 91 | emit_bytes(state, &x, sizeof(x)); 92 | } 93 | 94 | static inline void 95 | emit8(struct jit_state *state, uint64_t x) 96 | { 97 | emit_bytes(state, &x, sizeof(x)); 98 | } 99 | 100 | static inline void 101 | emit_jump_offset(struct jit_state *state, int32_t target_pc) 102 | { 103 | struct jump *jump = &state->jumps[state->num_jumps++]; 104 | jump->offset_loc = state->offset; 105 | jump->target_pc = target_pc; 106 | emit4(state, 0); 107 | } 108 | 109 | static inline void 110 | emit_modrm(struct jit_state *state, int mod, int r, int m) 111 | { 112 | assert(!(mod & ~0xc0)); 113 | emit1(state, (mod & 0xc0) | ((r & 7) << 3) | (m & 7)); 114 | } 115 | 116 | static inline void 117 | emit_modrm_reg2reg(struct jit_state *state, int r, int m) 118 | { 119 | emit_modrm(state, 0xc0, r, m); 120 | } 121 | 122 | static inline void 123 | emit_modrm_and_displacement(struct jit_state *state, int r, int m, int32_t d) 124 | { 125 | if (d == 0 && (m & 7) != RBP) { 126 | emit_modrm(state, 0x00, r, m); 127 | } else if (d >= -128 && d <= 127) { 128 | emit_modrm(state, 0x40, r, m); 129 | emit1(state, d); 130 | } else { 131 | emit_modrm(state, 0x80, r, m); 132 | emit4(state, d); 133 | } 134 | } 135 | 136 | static inline void 137 | emit_rex(struct jit_state *state, int w, int r, int x, int b) 138 | { 139 | assert(!(w & ~1)); 140 | assert(!(r & ~1)); 141 | assert(!(x & ~1)); 142 | assert(!(b & ~1)); 143 | emit1(state, 0x40 | (w << 3) | (r << 2) | (x << 1) | b); 144 | } 145 | 146 | /* 147 | * Emits a REX prefix with the top bit of src and dst. 148 | * Skipped if no bits would be set. 149 | */ 150 | static inline void 151 | emit_basic_rex(struct jit_state *state, int w, int src, int dst) 152 | { 153 | if (w || (src & 8) || (dst & 8)) { 154 | emit_rex(state, w, !!(src & 8), 0, !!(dst & 8)); 155 | } 156 | } 157 | 158 | static inline void 159 | emit_push(struct jit_state *state, int r) 160 | { 161 | emit_basic_rex(state, 0, 0, r); 162 | emit1(state, 0x50 | (r & 7)); 163 | } 164 | 165 | static inline void 166 | emit_pop(struct jit_state *state, int r) 167 | { 168 | emit_basic_rex(state, 0, 0, r); 169 | emit1(state, 0x58 | (r & 7)); 170 | } 171 | 172 | /* REX prefix and ModRM byte */ 173 | /* We use the MR encoding when there is a choice */ 174 | /* 'src' is often used as an opcode extension */ 175 | static inline void 176 | emit_alu32(struct jit_state *state, int op, int src, int dst) 177 | { 178 | emit_basic_rex(state, 0, src, dst); 179 | emit1(state, op); 180 | emit_modrm_reg2reg(state, src, dst); 181 | } 182 | 183 | /* REX prefix, ModRM byte, and 32-bit immediate */ 184 | static inline void 185 | emit_alu32_imm32(struct jit_state *state, int op, int src, int dst, int32_t imm) 186 | { 187 | emit_alu32(state, op, src, dst); 188 | emit4(state, imm); 189 | } 190 | 191 | /* REX prefix, ModRM byte, and 8-bit immediate */ 192 | static inline void 193 | emit_alu32_imm8(struct jit_state *state, int op, int src, int dst, int8_t imm) 194 | { 195 | emit_alu32(state, op, src, dst); 196 | emit1(state, imm); 197 | } 198 | 199 | /* REX.W prefix and ModRM byte */ 200 | /* We use the MR encoding when there is a choice */ 201 | /* 'src' is often used as an opcode extension */ 202 | static inline void 203 | emit_alu64(struct jit_state *state, int op, int src, int dst) 204 | { 205 | emit_basic_rex(state, 1, src, dst); 206 | emit1(state, op); 207 | emit_modrm_reg2reg(state, src, dst); 208 | } 209 | 210 | /* REX.W prefix, ModRM byte, and 32-bit immediate */ 211 | static inline void 212 | emit_alu64_imm32(struct jit_state *state, int op, int src, int dst, int32_t imm) 213 | { 214 | emit_alu64(state, op, src, dst); 215 | emit4(state, imm); 216 | } 217 | 218 | /* REX.W prefix, ModRM byte, and 8-bit immediate */ 219 | static inline void 220 | emit_alu64_imm8(struct jit_state *state, int op, int src, int dst, int8_t imm) 221 | { 222 | emit_alu64(state, op, src, dst); 223 | emit1(state, imm); 224 | } 225 | 226 | /* Register to register mov */ 227 | static inline void 228 | emit_mov(struct jit_state *state, int src, int dst) 229 | { 230 | emit_alu64(state, 0x89, src, dst); 231 | } 232 | 233 | static inline void 234 | emit_cmp_imm32(struct jit_state *state, int dst, int32_t imm) 235 | { 236 | emit_alu64_imm32(state, 0x81, 7, dst, imm); 237 | } 238 | 239 | static inline void 240 | emit_cmp(struct jit_state *state, int src, int dst) 241 | { 242 | emit_alu64(state, 0x39, src, dst); 243 | } 244 | 245 | static inline void 246 | emit_jcc(struct jit_state *state, int code, int32_t target_pc) 247 | { 248 | emit1(state, 0x0f); 249 | emit1(state, code); 250 | emit_jump_offset(state, target_pc); 251 | } 252 | 253 | /* Load [src + offset] into dst */ 254 | static inline void 255 | emit_load(struct jit_state *state, enum operand_size size, int src, int dst, int32_t offset) 256 | { 257 | emit_basic_rex(state, size == S64, dst, src); 258 | 259 | if (size == S8 || size == S16) { 260 | /* movzx */ 261 | emit1(state, 0x0f); 262 | emit1(state, size == S8 ? 0xb6 : 0xb7); 263 | } else if (size == S32 || size == S64) { 264 | /* mov */ 265 | emit1(state, 0x8b); 266 | } 267 | 268 | emit_modrm_and_displacement(state, dst, src, offset); 269 | } 270 | 271 | /* Load sign-extended immediate into register */ 272 | static inline void 273 | emit_load_imm(struct jit_state *state, int dst, int64_t imm) 274 | { 275 | if (imm >= INT32_MIN && imm <= INT32_MAX) { 276 | emit_alu64_imm32(state, 0xc7, 0, dst, imm); 277 | } else { 278 | /* movabs $imm,dst */ 279 | emit_basic_rex(state, 1, 0, dst); 280 | emit1(state, 0xb8 | (dst & 7)); 281 | emit8(state, imm); 282 | } 283 | } 284 | 285 | /* Store register src to [dst + offset] */ 286 | static inline void 287 | emit_store(struct jit_state *state, enum operand_size size, int src, int dst, int32_t offset) 288 | { 289 | if (size == S16) { 290 | emit1(state, 0x66); /* 16-bit override */ 291 | } 292 | int rexw = size == S64; 293 | if (rexw || src & 8 || dst & 8 || size == S8) { 294 | emit_rex(state, rexw, !!(src & 8), 0, !!(dst & 8)); 295 | } 296 | emit1(state, size == S8 ? 0x88 : 0x89); 297 | emit_modrm_and_displacement(state, src, dst, offset); 298 | } 299 | 300 | /* Store immediate to [dst + offset] */ 301 | static inline void 302 | emit_store_imm32(struct jit_state *state, enum operand_size size, int dst, int32_t offset, int32_t imm) 303 | { 304 | if (size == S16) { 305 | emit1(state, 0x66); /* 16-bit override */ 306 | } 307 | emit_basic_rex(state, size == S64, 0, dst); 308 | emit1(state, size == S8 ? 0xc6 : 0xc7); 309 | emit_modrm_and_displacement(state, 0, dst, offset); 310 | if (size == S32 || size == S64) { 311 | emit4(state, imm); 312 | } else if (size == S16) { 313 | emit2(state, imm); 314 | } else if (size == S8) { 315 | emit1(state, imm); 316 | } 317 | } 318 | 319 | static inline void 320 | emit_call(struct jit_state *state, void *target) 321 | { 322 | /* TODO use direct call when possible */ 323 | emit_load_imm(state, RAX, (uintptr_t)target); 324 | /* callq *%rax */ 325 | emit1(state, 0xff); 326 | emit1(state, 0xd0); 327 | } 328 | 329 | static inline void 330 | emit_jmp(struct jit_state *state, uint32_t target_pc) 331 | { 332 | emit1(state, 0xe9); 333 | emit_jump_offset(state, target_pc); 334 | } 335 | 336 | #endif 337 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /vm/ubpf_jit_x86_64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * Copyright 2017 Google Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #define _GNU_SOURCE 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include "ubpf_int.h" 28 | #include "ubpf_jit_x86_64.h" 29 | 30 | #if !defined(_countof) 31 | #define _countof(array) (sizeof(array) / sizeof(array[0])) 32 | #endif 33 | 34 | /* Special values for target_pc in struct jump */ 35 | #define TARGET_PC_EXIT -1 36 | #define TARGET_PC_DIV_BY_ZERO -2 37 | 38 | static void muldivmod(struct jit_state *state, uint16_t pc, uint8_t opcode, int src, int dst, int32_t imm); 39 | 40 | #define REGISTER_MAP_SIZE 11 41 | 42 | /* 43 | * There are two common x86-64 calling conventions, as discussed at 44 | * https://en.wikipedia.org/wiki/X86_calling_conventions#x86-64_calling_conventions 45 | */ 46 | 47 | #if defined(_WIN32) 48 | static int platform_nonvolatile_registers[] = { 49 | RBP, RBX, RDI, RSI, R12, R13, R14, R15 50 | }; 51 | static int platform_parameter_registers[] = { 52 | RCX, RDX, R8, R9 53 | }; 54 | #define RCX_ALT R15 55 | static int register_map[REGISTER_MAP_SIZE] = { 56 | RAX, 57 | R15, 58 | RDX, 59 | R8, 60 | R9, 61 | R10, 62 | R11, 63 | R12, 64 | R13, 65 | R14, 66 | RBP, 67 | }; 68 | #else 69 | #define RCX_ALT R9 70 | static int platform_nonvolatile_registers[] = { 71 | RBP, RBX, R13, R14, R15 72 | }; 73 | static int platform_parameter_registers[] = { 74 | RDI, RSI, RDX, RCX, R8, R9 75 | }; 76 | static int register_map[REGISTER_MAP_SIZE] = { 77 | RAX, 78 | RDI, 79 | RSI, 80 | RDX, 81 | R9, 82 | R8, 83 | RBX, 84 | R13, 85 | R14, 86 | R15, 87 | RBP, 88 | }; 89 | #endif 90 | 91 | /* Return the x86 register for the given eBPF register */ 92 | static int 93 | map_register(int r) 94 | { 95 | assert(r < REGISTER_MAP_SIZE); 96 | return register_map[r % REGISTER_MAP_SIZE]; 97 | } 98 | 99 | /* For testing, this changes the mapping between x86 and eBPF registers */ 100 | void 101 | ubpf_set_register_offset(int x) 102 | { 103 | int i; 104 | if (x < REGISTER_MAP_SIZE) { 105 | int tmp[REGISTER_MAP_SIZE]; 106 | memcpy(tmp, register_map, sizeof(register_map)); 107 | for (i = 0; i < REGISTER_MAP_SIZE; i++) { 108 | register_map[i] = tmp[(i+x)%REGISTER_MAP_SIZE]; 109 | } 110 | } else { 111 | /* Shuffle array */ 112 | unsigned int seed = x; 113 | for (i = 0; i < REGISTER_MAP_SIZE-1; i++) { 114 | int j = i + (rand_r(&seed) % (REGISTER_MAP_SIZE-i)); 115 | int tmp = register_map[j]; 116 | register_map[j] = register_map[i]; 117 | register_map[i] = tmp; 118 | } 119 | } 120 | } 121 | 122 | static int 123 | translate(struct ubpf_vm *vm, struct jit_state *state, char **errmsg) 124 | { 125 | int i; 126 | 127 | /* Save platform non-volatile registers */ 128 | for (i = 0; i < _countof(platform_nonvolatile_registers); i++) 129 | { 130 | emit_push(state, platform_nonvolatile_registers[i]); 131 | } 132 | 133 | /* Move first platform parameter register into register 1 */ 134 | if (map_register(1) != platform_parameter_registers[0]) { 135 | emit_mov(state, platform_parameter_registers[0], map_register(1)); 136 | } 137 | 138 | /* Copy stack pointer to R10 */ 139 | emit_mov(state, RSP, map_register(10)); 140 | 141 | /* Allocate stack space */ 142 | emit_alu64_imm32(state, 0x81, 5, RSP, UBPF_STACK_SIZE); 143 | 144 | for (i = 0; i < vm->num_insts; i++) { 145 | struct ebpf_inst inst = vm->insts[i]; 146 | state->pc_locs[i] = state->offset; 147 | 148 | int dst = map_register(inst.dst); 149 | int src = map_register(inst.src); 150 | uint32_t target_pc = i + inst.offset + 1; 151 | 152 | switch (inst.opcode) { 153 | case EBPF_OP_ADD_IMM: 154 | emit_alu32_imm32(state, 0x81, 0, dst, inst.imm); 155 | break; 156 | case EBPF_OP_ADD_REG: 157 | emit_alu32(state, 0x01, src, dst); 158 | break; 159 | case EBPF_OP_SUB_IMM: 160 | emit_alu32_imm32(state, 0x81, 5, dst, inst.imm); 161 | break; 162 | case EBPF_OP_SUB_REG: 163 | emit_alu32(state, 0x29, src, dst); 164 | break; 165 | case EBPF_OP_MUL_IMM: 166 | case EBPF_OP_MUL_REG: 167 | case EBPF_OP_DIV_IMM: 168 | case EBPF_OP_DIV_REG: 169 | case EBPF_OP_MOD_IMM: 170 | case EBPF_OP_MOD_REG: 171 | muldivmod(state, i, inst.opcode, src, dst, inst.imm); 172 | break; 173 | case EBPF_OP_OR_IMM: 174 | emit_alu32_imm32(state, 0x81, 1, dst, inst.imm); 175 | break; 176 | case EBPF_OP_OR_REG: 177 | emit_alu32(state, 0x09, src, dst); 178 | break; 179 | case EBPF_OP_AND_IMM: 180 | emit_alu32_imm32(state, 0x81, 4, dst, inst.imm); 181 | break; 182 | case EBPF_OP_AND_REG: 183 | emit_alu32(state, 0x21, src, dst); 184 | break; 185 | case EBPF_OP_LSH_IMM: 186 | emit_alu32_imm8(state, 0xc1, 4, dst, inst.imm); 187 | break; 188 | case EBPF_OP_LSH_REG: 189 | emit_mov(state, src, RCX); 190 | emit_alu32(state, 0xd3, 4, dst); 191 | break; 192 | case EBPF_OP_RSH_IMM: 193 | emit_alu32_imm8(state, 0xc1, 5, dst, inst.imm); 194 | break; 195 | case EBPF_OP_RSH_REG: 196 | emit_mov(state, src, RCX); 197 | emit_alu32(state, 0xd3, 5, dst); 198 | break; 199 | case EBPF_OP_NEG: 200 | emit_alu32(state, 0xf7, 3, dst); 201 | break; 202 | case EBPF_OP_XOR_IMM: 203 | emit_alu32_imm32(state, 0x81, 6, dst, inst.imm); 204 | break; 205 | case EBPF_OP_XOR_REG: 206 | emit_alu32(state, 0x31, src, dst); 207 | break; 208 | case EBPF_OP_MOV_IMM: 209 | emit_alu32_imm32(state, 0xc7, 0, dst, inst.imm); 210 | break; 211 | case EBPF_OP_MOV_REG: 212 | emit_mov(state, src, dst); 213 | break; 214 | case EBPF_OP_ARSH_IMM: 215 | emit_alu32_imm8(state, 0xc1, 7, dst, inst.imm); 216 | break; 217 | case EBPF_OP_ARSH_REG: 218 | emit_mov(state, src, RCX); 219 | emit_alu32(state, 0xd3, 7, dst); 220 | break; 221 | 222 | case EBPF_OP_LE: 223 | /* No-op */ 224 | break; 225 | case EBPF_OP_BE: 226 | if (inst.imm == 16) { 227 | /* rol */ 228 | emit1(state, 0x66); /* 16-bit override */ 229 | emit_alu32_imm8(state, 0xc1, 0, dst, 8); 230 | /* and */ 231 | emit_alu32_imm32(state, 0x81, 4, dst, 0xffff); 232 | } else if (inst.imm == 32 || inst.imm == 64) { 233 | /* bswap */ 234 | emit_basic_rex(state, inst.imm == 64, 0, dst); 235 | emit1(state, 0x0f); 236 | emit1(state, 0xc8 | (dst & 7)); 237 | } 238 | break; 239 | 240 | case EBPF_OP_ADD64_IMM: 241 | emit_alu64_imm32(state, 0x81, 0, dst, inst.imm); 242 | break; 243 | case EBPF_OP_ADD64_REG: 244 | emit_alu64(state, 0x01, src, dst); 245 | break; 246 | case EBPF_OP_SUB64_IMM: 247 | emit_alu64_imm32(state, 0x81, 5, dst, inst.imm); 248 | break; 249 | case EBPF_OP_SUB64_REG: 250 | emit_alu64(state, 0x29, src, dst); 251 | break; 252 | case EBPF_OP_MUL64_IMM: 253 | case EBPF_OP_MUL64_REG: 254 | case EBPF_OP_DIV64_IMM: 255 | case EBPF_OP_DIV64_REG: 256 | case EBPF_OP_MOD64_IMM: 257 | case EBPF_OP_MOD64_REG: 258 | muldivmod(state, i, inst.opcode, src, dst, inst.imm); 259 | break; 260 | case EBPF_OP_OR64_IMM: 261 | emit_alu64_imm32(state, 0x81, 1, dst, inst.imm); 262 | break; 263 | case EBPF_OP_OR64_REG: 264 | emit_alu64(state, 0x09, src, dst); 265 | break; 266 | case EBPF_OP_AND64_IMM: 267 | emit_alu64_imm32(state, 0x81, 4, dst, inst.imm); 268 | break; 269 | case EBPF_OP_AND64_REG: 270 | emit_alu64(state, 0x21, src, dst); 271 | break; 272 | case EBPF_OP_LSH64_IMM: 273 | emit_alu64_imm8(state, 0xc1, 4, dst, inst.imm); 274 | break; 275 | case EBPF_OP_LSH64_REG: 276 | emit_mov(state, src, RCX); 277 | emit_alu64(state, 0xd3, 4, dst); 278 | break; 279 | case EBPF_OP_RSH64_IMM: 280 | emit_alu64_imm8(state, 0xc1, 5, dst, inst.imm); 281 | break; 282 | case EBPF_OP_RSH64_REG: 283 | emit_mov(state, src, RCX); 284 | emit_alu64(state, 0xd3, 5, dst); 285 | break; 286 | case EBPF_OP_NEG64: 287 | emit_alu64(state, 0xf7, 3, dst); 288 | break; 289 | case EBPF_OP_XOR64_IMM: 290 | emit_alu64_imm32(state, 0x81, 6, dst, inst.imm); 291 | break; 292 | case EBPF_OP_XOR64_REG: 293 | emit_alu64(state, 0x31, src, dst); 294 | break; 295 | case EBPF_OP_MOV64_IMM: 296 | emit_load_imm(state, dst, inst.imm); 297 | break; 298 | case EBPF_OP_MOV64_REG: 299 | emit_mov(state, src, dst); 300 | break; 301 | case EBPF_OP_ARSH64_IMM: 302 | emit_alu64_imm8(state, 0xc1, 7, dst, inst.imm); 303 | break; 304 | case EBPF_OP_ARSH64_REG: 305 | emit_mov(state, src, RCX); 306 | emit_alu64(state, 0xd3, 7, dst); 307 | break; 308 | 309 | /* TODO use 8 bit immediate when possible */ 310 | case EBPF_OP_JA: 311 | emit_jmp(state, target_pc); 312 | break; 313 | case EBPF_OP_JEQ_IMM: 314 | emit_cmp_imm32(state, dst, inst.imm); 315 | emit_jcc(state, 0x84, target_pc); 316 | break; 317 | case EBPF_OP_JEQ_REG: 318 | emit_cmp(state, src, dst); 319 | emit_jcc(state, 0x84, target_pc); 320 | break; 321 | case EBPF_OP_JGT_IMM: 322 | emit_cmp_imm32(state, dst, inst.imm); 323 | emit_jcc(state, 0x87, target_pc); 324 | break; 325 | case EBPF_OP_JGT_REG: 326 | emit_cmp(state, src, dst); 327 | emit_jcc(state, 0x87, target_pc); 328 | break; 329 | case EBPF_OP_JGE_IMM: 330 | emit_cmp_imm32(state, dst, inst.imm); 331 | emit_jcc(state, 0x83, target_pc); 332 | break; 333 | case EBPF_OP_JGE_REG: 334 | emit_cmp(state, src, dst); 335 | emit_jcc(state, 0x83, target_pc); 336 | break; 337 | case EBPF_OP_JLT_IMM: 338 | emit_cmp_imm32(state, dst, inst.imm); 339 | emit_jcc(state, 0x82, target_pc); 340 | break; 341 | case EBPF_OP_JLT_REG: 342 | emit_cmp(state, src, dst); 343 | emit_jcc(state, 0x82, target_pc); 344 | break; 345 | case EBPF_OP_JLE_IMM: 346 | emit_cmp_imm32(state, dst, inst.imm); 347 | emit_jcc(state, 0x86, target_pc); 348 | break; 349 | case EBPF_OP_JLE_REG: 350 | emit_cmp(state, src, dst); 351 | emit_jcc(state, 0x86, target_pc); 352 | break; 353 | case EBPF_OP_JSET_IMM: 354 | emit_alu64_imm32(state, 0xf7, 0, dst, inst.imm); 355 | emit_jcc(state, 0x85, target_pc); 356 | break; 357 | case EBPF_OP_JSET_REG: 358 | emit_alu64(state, 0x85, src, dst); 359 | emit_jcc(state, 0x85, target_pc); 360 | break; 361 | case EBPF_OP_JNE_IMM: 362 | emit_cmp_imm32(state, dst, inst.imm); 363 | emit_jcc(state, 0x85, target_pc); 364 | break; 365 | case EBPF_OP_JNE_REG: 366 | emit_cmp(state, src, dst); 367 | emit_jcc(state, 0x85, target_pc); 368 | break; 369 | case EBPF_OP_JSGT_IMM: 370 | emit_cmp_imm32(state, dst, inst.imm); 371 | emit_jcc(state, 0x8f, target_pc); 372 | break; 373 | case EBPF_OP_JSGT_REG: 374 | emit_cmp(state, src, dst); 375 | emit_jcc(state, 0x8f, target_pc); 376 | break; 377 | case EBPF_OP_JSGE_IMM: 378 | emit_cmp_imm32(state, dst, inst.imm); 379 | emit_jcc(state, 0x8d, target_pc); 380 | break; 381 | case EBPF_OP_JSGE_REG: 382 | emit_cmp(state, src, dst); 383 | emit_jcc(state, 0x8d, target_pc); 384 | break; 385 | case EBPF_OP_JSLT_IMM: 386 | emit_cmp_imm32(state, dst, inst.imm); 387 | emit_jcc(state, 0x8c, target_pc); 388 | break; 389 | case EBPF_OP_JSLT_REG: 390 | emit_cmp(state, src, dst); 391 | emit_jcc(state, 0x8c, target_pc); 392 | break; 393 | case EBPF_OP_JSLE_IMM: 394 | emit_cmp_imm32(state, dst, inst.imm); 395 | emit_jcc(state, 0x8e, target_pc); 396 | break; 397 | case EBPF_OP_JSLE_REG: 398 | emit_cmp(state, src, dst); 399 | emit_jcc(state, 0x8e, target_pc); 400 | break; 401 | case EBPF_OP_CALL: 402 | /* We reserve RCX for shifts */ 403 | emit_mov(state, RCX_ALT, RCX); 404 | emit_call(state, vm->ext_funcs[inst.imm]); 405 | break; 406 | case EBPF_OP_EXIT: 407 | if (i != vm->num_insts - 1) { 408 | emit_jmp(state, TARGET_PC_EXIT); 409 | } 410 | break; 411 | 412 | case EBPF_OP_LDXW: 413 | emit_load(state, S32, src, dst, inst.offset); 414 | break; 415 | case EBPF_OP_LDXH: 416 | emit_load(state, S16, src, dst, inst.offset); 417 | break; 418 | case EBPF_OP_LDXB: 419 | emit_load(state, S8, src, dst, inst.offset); 420 | break; 421 | case EBPF_OP_LDXDW: 422 | emit_load(state, S64, src, dst, inst.offset); 423 | break; 424 | 425 | case EBPF_OP_STW: 426 | emit_store_imm32(state, S32, dst, inst.offset, inst.imm); 427 | break; 428 | case EBPF_OP_STH: 429 | emit_store_imm32(state, S16, dst, inst.offset, inst.imm); 430 | break; 431 | case EBPF_OP_STB: 432 | emit_store_imm32(state, S8, dst, inst.offset, inst.imm); 433 | break; 434 | case EBPF_OP_STDW: 435 | emit_store_imm32(state, S64, dst, inst.offset, inst.imm); 436 | break; 437 | 438 | case EBPF_OP_STXW: 439 | emit_store(state, S32, src, dst, inst.offset); 440 | break; 441 | case EBPF_OP_STXH: 442 | emit_store(state, S16, src, dst, inst.offset); 443 | break; 444 | case EBPF_OP_STXB: 445 | emit_store(state, S8, src, dst, inst.offset); 446 | break; 447 | case EBPF_OP_STXDW: 448 | emit_store(state, S64, src, dst, inst.offset); 449 | break; 450 | 451 | case EBPF_OP_LDDW: { 452 | struct ebpf_inst inst2 = vm->insts[++i]; 453 | uint64_t imm = (uint32_t)inst.imm | ((uint64_t)inst2.imm << 32); 454 | emit_load_imm(state, dst, imm); 455 | break; 456 | } 457 | 458 | default: 459 | *errmsg = ubpf_error("Unknown instruction at PC %d: opcode %02x", i, inst.opcode); 460 | return -1; 461 | } 462 | } 463 | 464 | /* Epilogue */ 465 | state->exit_loc = state->offset; 466 | 467 | /* Move register 0 into rax */ 468 | if (map_register(0) != RAX) { 469 | emit_mov(state, map_register(0), RAX); 470 | } 471 | 472 | /* Deallocate stack space */ 473 | emit_alu64_imm32(state, 0x81, 0, RSP, UBPF_STACK_SIZE); 474 | 475 | /* Restore platform non-volatile registers */ 476 | for (i = 0; i < _countof(platform_nonvolatile_registers); i++) 477 | { 478 | emit_pop(state, platform_nonvolatile_registers[_countof(platform_nonvolatile_registers) - i - 1]); 479 | } 480 | 481 | emit1(state, 0xc3); /* ret */ 482 | 483 | /* Division by zero handler */ 484 | state->div_by_zero_loc = state->offset; 485 | const char *div_by_zero_fmt = "uBPF error: division by zero at PC %u\n"; 486 | // RCX is the first parameter register for Windows, so first save the value. 487 | emit_mov(state, RCX, platform_parameter_registers[2]); /* muldivmod stored pc in RCX */ 488 | emit_load_imm(state, platform_parameter_registers[0], (uintptr_t)stderr); 489 | emit_load_imm(state, platform_parameter_registers[1], (uintptr_t)div_by_zero_fmt); 490 | emit_call(state, vm->error_printf); 491 | 492 | emit_load_imm(state, map_register(0), -1); 493 | emit_jmp(state, TARGET_PC_EXIT); 494 | 495 | return 0; 496 | } 497 | 498 | static void 499 | muldivmod(struct jit_state *state, uint16_t pc, uint8_t opcode, int src, int dst, int32_t imm) 500 | { 501 | bool mul = (opcode & EBPF_ALU_OP_MASK) == (EBPF_OP_MUL_IMM & EBPF_ALU_OP_MASK); 502 | bool div = (opcode & EBPF_ALU_OP_MASK) == (EBPF_OP_DIV_IMM & EBPF_ALU_OP_MASK); 503 | bool mod = (opcode & EBPF_ALU_OP_MASK) == (EBPF_OP_MOD_IMM & EBPF_ALU_OP_MASK); 504 | bool is64 = (opcode & EBPF_CLS_MASK) == EBPF_CLS_ALU64; 505 | 506 | if (div || mod) { 507 | emit_load_imm(state, RCX, pc); 508 | 509 | /* test src,src */ 510 | if (is64) { 511 | emit_alu64(state, 0x85, src, src); 512 | } else { 513 | emit_alu32(state, 0x85, src, src); 514 | } 515 | 516 | /* jz div_by_zero */ 517 | emit_jcc(state, 0x84, TARGET_PC_DIV_BY_ZERO); 518 | } 519 | 520 | if (dst != RAX) { 521 | emit_push(state, RAX); 522 | } 523 | if (dst != RDX) { 524 | emit_push(state, RDX); 525 | } 526 | if (imm) { 527 | emit_load_imm(state, RCX, imm); 528 | } else { 529 | emit_mov(state, src, RCX); 530 | } 531 | 532 | emit_mov(state, dst, RAX); 533 | 534 | if (div || mod) { 535 | /* xor %edx,%edx */ 536 | emit_alu32(state, 0x31, RDX, RDX); 537 | } 538 | 539 | if (is64) { 540 | emit_rex(state, 1, 0, 0, 0); 541 | } 542 | 543 | /* mul %ecx or div %ecx */ 544 | emit_alu32(state, 0xf7, mul ? 4 : 6, RCX); 545 | 546 | if (dst != RDX) { 547 | if (mod) { 548 | emit_mov(state, RDX, dst); 549 | } 550 | emit_pop(state, RDX); 551 | } 552 | if (dst != RAX) { 553 | if (div || mul) { 554 | emit_mov(state, RAX, dst); 555 | } 556 | emit_pop(state, RAX); 557 | } 558 | } 559 | 560 | static void 561 | resolve_jumps(struct jit_state *state) 562 | { 563 | int i; 564 | for (i = 0; i < state->num_jumps; i++) { 565 | struct jump jump = state->jumps[i]; 566 | 567 | int target_loc; 568 | if (jump.target_pc == TARGET_PC_EXIT) { 569 | target_loc = state->exit_loc; 570 | } else if (jump.target_pc == TARGET_PC_DIV_BY_ZERO) { 571 | target_loc = state->div_by_zero_loc; 572 | } else { 573 | target_loc = state->pc_locs[jump.target_pc]; 574 | } 575 | 576 | /* Assumes jump offset is at end of instruction */ 577 | uint32_t rel = target_loc - (jump.offset_loc + sizeof(uint32_t)); 578 | 579 | uint8_t *offset_ptr = &state->buf[jump.offset_loc]; 580 | memcpy(offset_ptr, &rel, sizeof(uint32_t)); 581 | } 582 | } 583 | 584 | int 585 | ubpf_translate(struct ubpf_vm *vm, uint8_t * buffer, size_t * size, char **errmsg) 586 | { 587 | struct jit_state state; 588 | int result = -1; 589 | 590 | state.offset = 0; 591 | state.size = *size; 592 | state.buf = buffer; 593 | state.pc_locs = calloc(UBPF_MAX_INSTS+1, sizeof(state.pc_locs[0])); 594 | state.jumps = calloc(UBPF_MAX_INSTS, sizeof(state.jumps[0])); 595 | state.num_jumps = 0; 596 | 597 | if (translate(vm, &state, errmsg) < 0) { 598 | goto out; 599 | } 600 | 601 | resolve_jumps(&state); 602 | result = 0; 603 | 604 | *size = state.offset; 605 | 606 | out: 607 | free(state.pc_locs); 608 | free(state.jumps); 609 | return result; 610 | } 611 | 612 | ubpf_jit_fn 613 | ubpf_compile(struct ubpf_vm *vm, char **errmsg) 614 | { 615 | void *jitted = NULL; 616 | uint8_t *buffer = NULL; 617 | size_t jitted_size; 618 | 619 | if (vm->jitted) { 620 | return vm->jitted; 621 | } 622 | 623 | *errmsg = NULL; 624 | 625 | if (!vm->insts) { 626 | *errmsg = ubpf_error("code has not been loaded into this VM"); 627 | return NULL; 628 | } 629 | 630 | jitted_size = 65536; 631 | buffer = calloc(jitted_size, 1); 632 | 633 | if (ubpf_translate(vm, buffer, &jitted_size, errmsg) < 0) { 634 | goto out; 635 | } 636 | 637 | jitted = mmap(0, jitted_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 638 | if (jitted == MAP_FAILED) { 639 | *errmsg = ubpf_error("internal uBPF error: mmap failed: %s\n", strerror(errno)); 640 | goto out; 641 | } 642 | 643 | memcpy(jitted, buffer, jitted_size); 644 | 645 | if (mprotect(jitted, jitted_size, PROT_READ | PROT_EXEC) < 0) { 646 | *errmsg = ubpf_error("internal uBPF error: mprotect failed: %s\n", strerror(errno)); 647 | goto out; 648 | } 649 | 650 | vm->jitted = jitted; 651 | vm->jitted_size = jitted_size; 652 | 653 | out: 654 | free(buffer); 655 | if (jitted && vm->jitted == NULL) { 656 | munmap(jitted, jitted_size); 657 | } 658 | return vm->jitted; 659 | } 660 | -------------------------------------------------------------------------------- /vm/ubpf_vm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Big Switch Networks, Inc 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #define _GNU_SOURCE 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "ubpf_int.h" 26 | 27 | #define MAX_EXT_FUNCS 64 28 | 29 | static bool validate(const struct ubpf_vm *vm, const struct ebpf_inst *insts, uint32_t num_insts, char **errmsg); 30 | static bool bounds_check(const struct ubpf_vm *vm, void *addr, int size, const char *type, uint16_t cur_pc, void *mem, size_t mem_len, void *stack); 31 | 32 | bool ubpf_toggle_bounds_check(struct ubpf_vm *vm, bool enable) 33 | { 34 | bool old = vm->bounds_check_enabled; 35 | vm->bounds_check_enabled = enable; 36 | return old; 37 | } 38 | 39 | void ubpf_set_error_print(struct ubpf_vm *vm, int (*error_printf)(FILE* stream, const char* format, ...)) 40 | { 41 | if (error_printf) 42 | vm->error_printf = error_printf; 43 | else 44 | vm->error_printf = fprintf; 45 | } 46 | 47 | struct ubpf_vm * 48 | ubpf_create(void) 49 | { 50 | struct ubpf_vm *vm = calloc(1, sizeof(*vm)); 51 | if (vm == NULL) { 52 | return NULL; 53 | } 54 | 55 | vm->ext_funcs = calloc(MAX_EXT_FUNCS, sizeof(*vm->ext_funcs)); 56 | if (vm->ext_funcs == NULL) { 57 | ubpf_destroy(vm); 58 | return NULL; 59 | } 60 | 61 | vm->ext_func_names = calloc(MAX_EXT_FUNCS, sizeof(*vm->ext_func_names)); 62 | if (vm->ext_func_names == NULL) { 63 | ubpf_destroy(vm); 64 | return NULL; 65 | } 66 | 67 | vm->bounds_check_enabled = true; 68 | vm->error_printf = fprintf; 69 | return vm; 70 | } 71 | 72 | void 73 | ubpf_destroy(struct ubpf_vm *vm) 74 | { 75 | if (vm->jitted) { 76 | munmap(vm->jitted, vm->jitted_size); 77 | } 78 | free(vm->insts); 79 | free(vm->ext_funcs); 80 | free(vm->ext_func_names); 81 | free(vm); 82 | } 83 | 84 | int 85 | ubpf_register(struct ubpf_vm *vm, unsigned int idx, const char *name, void *fn) 86 | { 87 | if (idx >= MAX_EXT_FUNCS) { 88 | return -1; 89 | } 90 | 91 | vm->ext_funcs[idx] = (ext_func)fn; 92 | vm->ext_func_names[idx] = name; 93 | return 0; 94 | } 95 | 96 | unsigned int 97 | ubpf_lookup_registered_function(struct ubpf_vm *vm, const char *name) 98 | { 99 | int i; 100 | for (i = 0; i < MAX_EXT_FUNCS; i++) { 101 | const char *other = vm->ext_func_names[i]; 102 | if (other && !strcmp(other, name)) { 103 | return i; 104 | } 105 | } 106 | return -1; 107 | } 108 | 109 | int 110 | ubpf_load(struct ubpf_vm *vm, const void *code, uint32_t code_len, char **errmsg) 111 | { 112 | *errmsg = NULL; 113 | 114 | if (vm->insts) { 115 | *errmsg = ubpf_error("code has already been loaded into this VM"); 116 | return -1; 117 | } 118 | 119 | if (code_len % 8 != 0) { 120 | *errmsg = ubpf_error("code_len must be a multiple of 8"); 121 | return -1; 122 | } 123 | 124 | if (!validate(vm, code, code_len/8, errmsg)) { 125 | return -1; 126 | } 127 | 128 | vm->insts = malloc(code_len); 129 | if (vm->insts == NULL) { 130 | *errmsg = ubpf_error("out of memory"); 131 | return -1; 132 | } 133 | 134 | memcpy(vm->insts, code, code_len); 135 | vm->num_insts = code_len/sizeof(vm->insts[0]); 136 | 137 | return 0; 138 | } 139 | 140 | static uint32_t 141 | u32(uint64_t x) 142 | { 143 | return x; 144 | } 145 | 146 | uint64_t 147 | ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len) 148 | { 149 | uint16_t pc = 0; 150 | const struct ebpf_inst *insts = vm->insts; 151 | uint64_t reg[16]; 152 | uint64_t stack[(UBPF_STACK_SIZE+7)/8]; 153 | 154 | if (!insts) { 155 | /* Code must be loaded before we can execute */ 156 | return UINT64_MAX; 157 | } 158 | 159 | reg[1] = (uintptr_t)mem; 160 | reg[10] = (uintptr_t)stack + sizeof(stack); 161 | 162 | while (1) { 163 | const uint16_t cur_pc = pc; 164 | struct ebpf_inst inst = insts[pc++]; 165 | 166 | switch (inst.opcode) { 167 | case EBPF_OP_ADD_IMM: 168 | reg[inst.dst] += inst.imm; 169 | reg[inst.dst] &= UINT32_MAX; 170 | break; 171 | case EBPF_OP_ADD_REG: 172 | reg[inst.dst] += reg[inst.src]; 173 | reg[inst.dst] &= UINT32_MAX; 174 | break; 175 | case EBPF_OP_SUB_IMM: 176 | reg[inst.dst] -= inst.imm; 177 | reg[inst.dst] &= UINT32_MAX; 178 | break; 179 | case EBPF_OP_SUB_REG: 180 | reg[inst.dst] -= reg[inst.src]; 181 | reg[inst.dst] &= UINT32_MAX; 182 | break; 183 | case EBPF_OP_MUL_IMM: 184 | reg[inst.dst] *= inst.imm; 185 | reg[inst.dst] &= UINT32_MAX; 186 | break; 187 | case EBPF_OP_MUL_REG: 188 | reg[inst.dst] *= reg[inst.src]; 189 | reg[inst.dst] &= UINT32_MAX; 190 | break; 191 | case EBPF_OP_DIV_IMM: 192 | reg[inst.dst] = u32(reg[inst.dst]) / u32(inst.imm); 193 | reg[inst.dst] &= UINT32_MAX; 194 | break; 195 | case EBPF_OP_DIV_REG: 196 | if (reg[inst.src] == 0) { 197 | vm->error_printf(stderr, "uBPF error: division by zero at PC %u\n", cur_pc); 198 | return UINT64_MAX; 199 | } 200 | reg[inst.dst] = u32(reg[inst.dst]) / u32(reg[inst.src]); 201 | reg[inst.dst] &= UINT32_MAX; 202 | break; 203 | case EBPF_OP_OR_IMM: 204 | reg[inst.dst] |= inst.imm; 205 | reg[inst.dst] &= UINT32_MAX; 206 | break; 207 | case EBPF_OP_OR_REG: 208 | reg[inst.dst] |= reg[inst.src]; 209 | reg[inst.dst] &= UINT32_MAX; 210 | break; 211 | case EBPF_OP_AND_IMM: 212 | reg[inst.dst] &= inst.imm; 213 | reg[inst.dst] &= UINT32_MAX; 214 | break; 215 | case EBPF_OP_AND_REG: 216 | reg[inst.dst] &= reg[inst.src]; 217 | reg[inst.dst] &= UINT32_MAX; 218 | break; 219 | case EBPF_OP_LSH_IMM: 220 | reg[inst.dst] <<= inst.imm; 221 | reg[inst.dst] &= UINT32_MAX; 222 | break; 223 | case EBPF_OP_LSH_REG: 224 | reg[inst.dst] <<= reg[inst.src]; 225 | reg[inst.dst] &= UINT32_MAX; 226 | break; 227 | case EBPF_OP_RSH_IMM: 228 | reg[inst.dst] = u32(reg[inst.dst]) >> inst.imm; 229 | reg[inst.dst] &= UINT32_MAX; 230 | break; 231 | case EBPF_OP_RSH_REG: 232 | reg[inst.dst] = u32(reg[inst.dst]) >> reg[inst.src]; 233 | reg[inst.dst] &= UINT32_MAX; 234 | break; 235 | case EBPF_OP_NEG: 236 | reg[inst.dst] = -(int64_t)reg[inst.dst]; 237 | reg[inst.dst] &= UINT32_MAX; 238 | break; 239 | case EBPF_OP_MOD_IMM: 240 | reg[inst.dst] = u32(reg[inst.dst]) % u32(inst.imm); 241 | reg[inst.dst] &= UINT32_MAX; 242 | break; 243 | case EBPF_OP_MOD_REG: 244 | if (reg[inst.src] == 0) { 245 | vm->error_printf(stderr, "uBPF error: division by zero at PC %u\n", cur_pc); 246 | return UINT64_MAX; 247 | } 248 | reg[inst.dst] = u32(reg[inst.dst]) % u32(reg[inst.src]); 249 | break; 250 | case EBPF_OP_XOR_IMM: 251 | reg[inst.dst] ^= inst.imm; 252 | reg[inst.dst] &= UINT32_MAX; 253 | break; 254 | case EBPF_OP_XOR_REG: 255 | reg[inst.dst] ^= reg[inst.src]; 256 | reg[inst.dst] &= UINT32_MAX; 257 | break; 258 | case EBPF_OP_MOV_IMM: 259 | reg[inst.dst] = inst.imm; 260 | reg[inst.dst] &= UINT32_MAX; 261 | break; 262 | case EBPF_OP_MOV_REG: 263 | reg[inst.dst] = reg[inst.src]; 264 | reg[inst.dst] &= UINT32_MAX; 265 | break; 266 | case EBPF_OP_ARSH_IMM: 267 | reg[inst.dst] = (int32_t)reg[inst.dst] >> inst.imm; 268 | reg[inst.dst] &= UINT32_MAX; 269 | break; 270 | case EBPF_OP_ARSH_REG: 271 | reg[inst.dst] = (int32_t)reg[inst.dst] >> u32(reg[inst.src]); 272 | reg[inst.dst] &= UINT32_MAX; 273 | break; 274 | 275 | case EBPF_OP_LE: 276 | if (inst.imm == 16) { 277 | reg[inst.dst] = htole16(reg[inst.dst]); 278 | } else if (inst.imm == 32) { 279 | reg[inst.dst] = htole32(reg[inst.dst]); 280 | } else if (inst.imm == 64) { 281 | reg[inst.dst] = htole64(reg[inst.dst]); 282 | } 283 | break; 284 | case EBPF_OP_BE: 285 | if (inst.imm == 16) { 286 | reg[inst.dst] = htobe16(reg[inst.dst]); 287 | } else if (inst.imm == 32) { 288 | reg[inst.dst] = htobe32(reg[inst.dst]); 289 | } else if (inst.imm == 64) { 290 | reg[inst.dst] = htobe64(reg[inst.dst]); 291 | } 292 | break; 293 | 294 | 295 | case EBPF_OP_ADD64_IMM: 296 | reg[inst.dst] += inst.imm; 297 | break; 298 | case EBPF_OP_ADD64_REG: 299 | reg[inst.dst] += reg[inst.src]; 300 | break; 301 | case EBPF_OP_SUB64_IMM: 302 | reg[inst.dst] -= inst.imm; 303 | break; 304 | case EBPF_OP_SUB64_REG: 305 | reg[inst.dst] -= reg[inst.src]; 306 | break; 307 | case EBPF_OP_MUL64_IMM: 308 | reg[inst.dst] *= inst.imm; 309 | break; 310 | case EBPF_OP_MUL64_REG: 311 | reg[inst.dst] *= reg[inst.src]; 312 | break; 313 | case EBPF_OP_DIV64_IMM: 314 | reg[inst.dst] /= inst.imm; 315 | break; 316 | case EBPF_OP_DIV64_REG: 317 | if (reg[inst.src] == 0) { 318 | vm->error_printf(stderr, "uBPF error: division by zero at PC %u\n", cur_pc); 319 | return UINT64_MAX; 320 | } 321 | reg[inst.dst] /= reg[inst.src]; 322 | break; 323 | case EBPF_OP_OR64_IMM: 324 | reg[inst.dst] |= inst.imm; 325 | break; 326 | case EBPF_OP_OR64_REG: 327 | reg[inst.dst] |= reg[inst.src]; 328 | break; 329 | case EBPF_OP_AND64_IMM: 330 | reg[inst.dst] &= inst.imm; 331 | break; 332 | case EBPF_OP_AND64_REG: 333 | reg[inst.dst] &= reg[inst.src]; 334 | break; 335 | case EBPF_OP_LSH64_IMM: 336 | reg[inst.dst] <<= inst.imm; 337 | break; 338 | case EBPF_OP_LSH64_REG: 339 | reg[inst.dst] <<= reg[inst.src]; 340 | break; 341 | case EBPF_OP_RSH64_IMM: 342 | reg[inst.dst] >>= inst.imm; 343 | break; 344 | case EBPF_OP_RSH64_REG: 345 | reg[inst.dst] >>= reg[inst.src]; 346 | break; 347 | case EBPF_OP_NEG64: 348 | reg[inst.dst] = -reg[inst.dst]; 349 | break; 350 | case EBPF_OP_MOD64_IMM: 351 | reg[inst.dst] %= inst.imm; 352 | break; 353 | case EBPF_OP_MOD64_REG: 354 | if (reg[inst.src] == 0) { 355 | vm->error_printf(stderr, "uBPF error: division by zero at PC %u\n", cur_pc); 356 | return UINT64_MAX; 357 | } 358 | reg[inst.dst] %= reg[inst.src]; 359 | break; 360 | case EBPF_OP_XOR64_IMM: 361 | reg[inst.dst] ^= inst.imm; 362 | break; 363 | case EBPF_OP_XOR64_REG: 364 | reg[inst.dst] ^= reg[inst.src]; 365 | break; 366 | case EBPF_OP_MOV64_IMM: 367 | reg[inst.dst] = inst.imm; 368 | break; 369 | case EBPF_OP_MOV64_REG: 370 | reg[inst.dst] = reg[inst.src]; 371 | break; 372 | case EBPF_OP_ARSH64_IMM: 373 | reg[inst.dst] = (int64_t)reg[inst.dst] >> inst.imm; 374 | break; 375 | case EBPF_OP_ARSH64_REG: 376 | reg[inst.dst] = (int64_t)reg[inst.dst] >> reg[inst.src]; 377 | break; 378 | 379 | /* 380 | * HACK runtime bounds check 381 | * 382 | * Needed since we don't have a verifier yet. 383 | */ 384 | #define BOUNDS_CHECK_LOAD(size) \ 385 | do { \ 386 | if (!bounds_check(vm, (char *)reg[inst.src] + inst.offset, size, "load", cur_pc, mem, mem_len, stack)) { \ 387 | return UINT64_MAX; \ 388 | } \ 389 | } while (0) 390 | #define BOUNDS_CHECK_STORE(size) \ 391 | do { \ 392 | if (!bounds_check(vm, (char *)reg[inst.dst] + inst.offset, size, "store", cur_pc, mem, mem_len, stack)) { \ 393 | return UINT64_MAX; \ 394 | } \ 395 | } while (0) 396 | 397 | case EBPF_OP_LDXW: 398 | BOUNDS_CHECK_LOAD(4); 399 | reg[inst.dst] = *(uint32_t *)(uintptr_t)(reg[inst.src] + inst.offset); 400 | break; 401 | case EBPF_OP_LDXH: 402 | BOUNDS_CHECK_LOAD(2); 403 | reg[inst.dst] = *(uint16_t *)(uintptr_t)(reg[inst.src] + inst.offset); 404 | break; 405 | case EBPF_OP_LDXB: 406 | BOUNDS_CHECK_LOAD(1); 407 | reg[inst.dst] = *(uint8_t *)(uintptr_t)(reg[inst.src] + inst.offset); 408 | break; 409 | case EBPF_OP_LDXDW: 410 | BOUNDS_CHECK_LOAD(8); 411 | reg[inst.dst] = *(uint64_t *)(uintptr_t)(reg[inst.src] + inst.offset); 412 | break; 413 | 414 | case EBPF_OP_STW: 415 | BOUNDS_CHECK_STORE(4); 416 | *(uint32_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = inst.imm; 417 | break; 418 | case EBPF_OP_STH: 419 | BOUNDS_CHECK_STORE(2); 420 | *(uint16_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = inst.imm; 421 | break; 422 | case EBPF_OP_STB: 423 | BOUNDS_CHECK_STORE(1); 424 | *(uint8_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = inst.imm; 425 | break; 426 | case EBPF_OP_STDW: 427 | BOUNDS_CHECK_STORE(8); 428 | *(uint64_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = inst.imm; 429 | break; 430 | 431 | case EBPF_OP_STXW: 432 | BOUNDS_CHECK_STORE(4); 433 | *(uint32_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = reg[inst.src]; 434 | break; 435 | case EBPF_OP_STXH: 436 | BOUNDS_CHECK_STORE(2); 437 | *(uint16_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = reg[inst.src]; 438 | break; 439 | case EBPF_OP_STXB: 440 | BOUNDS_CHECK_STORE(1); 441 | *(uint8_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = reg[inst.src]; 442 | break; 443 | case EBPF_OP_STXDW: 444 | BOUNDS_CHECK_STORE(8); 445 | *(uint64_t *)(uintptr_t)(reg[inst.dst] + inst.offset) = reg[inst.src]; 446 | break; 447 | 448 | case EBPF_OP_LDDW: 449 | reg[inst.dst] = (uint32_t)inst.imm | ((uint64_t)insts[pc++].imm << 32); 450 | break; 451 | 452 | case EBPF_OP_JA: 453 | pc += inst.offset; 454 | break; 455 | case EBPF_OP_JEQ_IMM: 456 | if (reg[inst.dst] == inst.imm) { 457 | pc += inst.offset; 458 | } 459 | break; 460 | case EBPF_OP_JEQ_REG: 461 | if (reg[inst.dst] == reg[inst.src]) { 462 | pc += inst.offset; 463 | } 464 | break; 465 | case EBPF_OP_JGT_IMM: 466 | if (reg[inst.dst] > (uint32_t)inst.imm) { 467 | pc += inst.offset; 468 | } 469 | break; 470 | case EBPF_OP_JGT_REG: 471 | if (reg[inst.dst] > reg[inst.src]) { 472 | pc += inst.offset; 473 | } 474 | break; 475 | case EBPF_OP_JGE_IMM: 476 | if (reg[inst.dst] >= (uint32_t)inst.imm) { 477 | pc += inst.offset; 478 | } 479 | break; 480 | case EBPF_OP_JGE_REG: 481 | if (reg[inst.dst] >= reg[inst.src]) { 482 | pc += inst.offset; 483 | } 484 | break; 485 | case EBPF_OP_JLT_IMM: 486 | if (reg[inst.dst] < (uint32_t)inst.imm) { 487 | pc += inst.offset; 488 | } 489 | break; 490 | case EBPF_OP_JLT_REG: 491 | if (reg[inst.dst] < reg[inst.src]) { 492 | pc += inst.offset; 493 | } 494 | break; 495 | case EBPF_OP_JLE_IMM: 496 | if (reg[inst.dst] <= (uint32_t)inst.imm) { 497 | pc += inst.offset; 498 | } 499 | break; 500 | case EBPF_OP_JLE_REG: 501 | if (reg[inst.dst] <= reg[inst.src]) { 502 | pc += inst.offset; 503 | } 504 | break; 505 | case EBPF_OP_JSET_IMM: 506 | if (reg[inst.dst] & inst.imm) { 507 | pc += inst.offset; 508 | } 509 | break; 510 | case EBPF_OP_JSET_REG: 511 | if (reg[inst.dst] & reg[inst.src]) { 512 | pc += inst.offset; 513 | } 514 | break; 515 | case EBPF_OP_JNE_IMM: 516 | if (reg[inst.dst] != inst.imm) { 517 | pc += inst.offset; 518 | } 519 | break; 520 | case EBPF_OP_JNE_REG: 521 | if (reg[inst.dst] != reg[inst.src]) { 522 | pc += inst.offset; 523 | } 524 | break; 525 | case EBPF_OP_JSGT_IMM: 526 | if ((int64_t)reg[inst.dst] > inst.imm) { 527 | pc += inst.offset; 528 | } 529 | break; 530 | case EBPF_OP_JSGT_REG: 531 | if ((int64_t)reg[inst.dst] > (int64_t)reg[inst.src]) { 532 | pc += inst.offset; 533 | } 534 | break; 535 | case EBPF_OP_JSGE_IMM: 536 | if ((int64_t)reg[inst.dst] >= inst.imm) { 537 | pc += inst.offset; 538 | } 539 | break; 540 | case EBPF_OP_JSGE_REG: 541 | if ((int64_t)reg[inst.dst] >= (int64_t)reg[inst.src]) { 542 | pc += inst.offset; 543 | } 544 | break; 545 | case EBPF_OP_JSLT_IMM: 546 | if ((int64_t)reg[inst.dst] < inst.imm) { 547 | pc += inst.offset; 548 | } 549 | break; 550 | case EBPF_OP_JSLT_REG: 551 | if ((int64_t)reg[inst.dst] < (int64_t)reg[inst.src]) { 552 | pc += inst.offset; 553 | } 554 | break; 555 | case EBPF_OP_JSLE_IMM: 556 | if ((int64_t)reg[inst.dst] <= inst.imm) { 557 | pc += inst.offset; 558 | } 559 | break; 560 | case EBPF_OP_JSLE_REG: 561 | if ((int64_t)reg[inst.dst] <= (int64_t)reg[inst.src]) { 562 | pc += inst.offset; 563 | } 564 | break; 565 | case EBPF_OP_EXIT: 566 | return reg[0]; 567 | case EBPF_OP_CALL: 568 | reg[0] = vm->ext_funcs[inst.imm](reg[1], reg[2], reg[3], reg[4], reg[5]); 569 | break; 570 | } 571 | } 572 | } 573 | 574 | static bool 575 | validate(const struct ubpf_vm *vm, const struct ebpf_inst *insts, uint32_t num_insts, char **errmsg) 576 | { 577 | if (num_insts >= UBPF_MAX_INSTS) { 578 | *errmsg = ubpf_error("too many instructions (max %u)", UBPF_MAX_INSTS); 579 | return false; 580 | } 581 | 582 | int i; 583 | for (i = 0; i < num_insts; i++) { 584 | struct ebpf_inst inst = insts[i]; 585 | bool store = false; 586 | 587 | switch (inst.opcode) { 588 | case EBPF_OP_ADD_IMM: 589 | case EBPF_OP_ADD_REG: 590 | case EBPF_OP_SUB_IMM: 591 | case EBPF_OP_SUB_REG: 592 | case EBPF_OP_MUL_IMM: 593 | case EBPF_OP_MUL_REG: 594 | case EBPF_OP_DIV_REG: 595 | case EBPF_OP_OR_IMM: 596 | case EBPF_OP_OR_REG: 597 | case EBPF_OP_AND_IMM: 598 | case EBPF_OP_AND_REG: 599 | case EBPF_OP_LSH_IMM: 600 | case EBPF_OP_LSH_REG: 601 | case EBPF_OP_RSH_IMM: 602 | case EBPF_OP_RSH_REG: 603 | case EBPF_OP_NEG: 604 | case EBPF_OP_MOD_REG: 605 | case EBPF_OP_XOR_IMM: 606 | case EBPF_OP_XOR_REG: 607 | case EBPF_OP_MOV_IMM: 608 | case EBPF_OP_MOV_REG: 609 | case EBPF_OP_ARSH_IMM: 610 | case EBPF_OP_ARSH_REG: 611 | break; 612 | 613 | case EBPF_OP_LE: 614 | case EBPF_OP_BE: 615 | if (inst.imm != 16 && inst.imm != 32 && inst.imm != 64) { 616 | *errmsg = ubpf_error("invalid endian immediate at PC %d", i); 617 | return false; 618 | } 619 | break; 620 | 621 | case EBPF_OP_ADD64_IMM: 622 | case EBPF_OP_ADD64_REG: 623 | case EBPF_OP_SUB64_IMM: 624 | case EBPF_OP_SUB64_REG: 625 | case EBPF_OP_MUL64_IMM: 626 | case EBPF_OP_MUL64_REG: 627 | case EBPF_OP_DIV64_REG: 628 | case EBPF_OP_OR64_IMM: 629 | case EBPF_OP_OR64_REG: 630 | case EBPF_OP_AND64_IMM: 631 | case EBPF_OP_AND64_REG: 632 | case EBPF_OP_LSH64_IMM: 633 | case EBPF_OP_LSH64_REG: 634 | case EBPF_OP_RSH64_IMM: 635 | case EBPF_OP_RSH64_REG: 636 | case EBPF_OP_NEG64: 637 | case EBPF_OP_MOD64_REG: 638 | case EBPF_OP_XOR64_IMM: 639 | case EBPF_OP_XOR64_REG: 640 | case EBPF_OP_MOV64_IMM: 641 | case EBPF_OP_MOV64_REG: 642 | case EBPF_OP_ARSH64_IMM: 643 | case EBPF_OP_ARSH64_REG: 644 | break; 645 | 646 | case EBPF_OP_LDXW: 647 | case EBPF_OP_LDXH: 648 | case EBPF_OP_LDXB: 649 | case EBPF_OP_LDXDW: 650 | break; 651 | 652 | case EBPF_OP_STW: 653 | case EBPF_OP_STH: 654 | case EBPF_OP_STB: 655 | case EBPF_OP_STDW: 656 | case EBPF_OP_STXW: 657 | case EBPF_OP_STXH: 658 | case EBPF_OP_STXB: 659 | case EBPF_OP_STXDW: 660 | store = true; 661 | break; 662 | 663 | case EBPF_OP_LDDW: 664 | if (i + 1 >= num_insts || insts[i+1].opcode != 0) { 665 | *errmsg = ubpf_error("incomplete lddw at PC %d", i); 666 | return false; 667 | } 668 | i++; /* Skip next instruction */ 669 | break; 670 | 671 | case EBPF_OP_JA: 672 | case EBPF_OP_JEQ_REG: 673 | case EBPF_OP_JEQ_IMM: 674 | case EBPF_OP_JGT_REG: 675 | case EBPF_OP_JGT_IMM: 676 | case EBPF_OP_JGE_REG: 677 | case EBPF_OP_JGE_IMM: 678 | case EBPF_OP_JLT_REG: 679 | case EBPF_OP_JLT_IMM: 680 | case EBPF_OP_JLE_REG: 681 | case EBPF_OP_JLE_IMM: 682 | case EBPF_OP_JSET_REG: 683 | case EBPF_OP_JSET_IMM: 684 | case EBPF_OP_JNE_REG: 685 | case EBPF_OP_JNE_IMM: 686 | case EBPF_OP_JSGT_IMM: 687 | case EBPF_OP_JSGT_REG: 688 | case EBPF_OP_JSGE_IMM: 689 | case EBPF_OP_JSGE_REG: 690 | case EBPF_OP_JSLT_IMM: 691 | case EBPF_OP_JSLT_REG: 692 | case EBPF_OP_JSLE_IMM: 693 | case EBPF_OP_JSLE_REG: 694 | if (inst.offset == -1) { 695 | *errmsg = ubpf_error("infinite loop at PC %d", i); 696 | return false; 697 | } 698 | int new_pc = i + 1 + inst.offset; 699 | if (new_pc < 0 || new_pc >= num_insts) { 700 | *errmsg = ubpf_error("jump out of bounds at PC %d", i); 701 | return false; 702 | } else if (insts[new_pc].opcode == 0) { 703 | *errmsg = ubpf_error("jump to middle of lddw at PC %d", i); 704 | return false; 705 | } 706 | break; 707 | 708 | case EBPF_OP_CALL: 709 | if (inst.imm < 0 || inst.imm >= MAX_EXT_FUNCS) { 710 | *errmsg = ubpf_error("invalid call immediate at PC %d", i); 711 | return false; 712 | } 713 | if (!vm->ext_funcs[inst.imm]) { 714 | *errmsg = ubpf_error("call to nonexistent function %u at PC %d", inst.imm, i); 715 | return false; 716 | } 717 | break; 718 | 719 | case EBPF_OP_EXIT: 720 | break; 721 | 722 | case EBPF_OP_DIV_IMM: 723 | case EBPF_OP_MOD_IMM: 724 | case EBPF_OP_DIV64_IMM: 725 | case EBPF_OP_MOD64_IMM: 726 | if (inst.imm == 0) { 727 | *errmsg = ubpf_error("division by zero at PC %d", i); 728 | return false; 729 | } 730 | break; 731 | 732 | default: 733 | *errmsg = ubpf_error("unknown opcode 0x%02x at PC %d", inst.opcode, i); 734 | return false; 735 | } 736 | 737 | if (inst.src > 10) { 738 | *errmsg = ubpf_error("invalid source register at PC %d", i); 739 | return false; 740 | } 741 | 742 | if (inst.dst > 9 && !(store && inst.dst == 10)) { 743 | *errmsg = ubpf_error("invalid destination register at PC %d", i); 744 | return false; 745 | } 746 | } 747 | 748 | return true; 749 | } 750 | 751 | static bool 752 | bounds_check(const struct ubpf_vm *vm, void *addr, int size, const char *type, uint16_t cur_pc, void *mem, size_t mem_len, void *stack) 753 | { 754 | if (!vm->bounds_check_enabled) 755 | return true; 756 | if (mem && (addr >= mem && ((char*)addr + size) <= ((char*)mem + mem_len))) { 757 | /* Context access */ 758 | return true; 759 | } else if (addr >= stack && ((char*)addr + size) <= ((char*)stack + UBPF_STACK_SIZE)) { 760 | /* Stack access */ 761 | return true; 762 | } else { 763 | vm->error_printf(stderr, "uBPF error: out of bounds memory %s at PC %u, addr %p, size %d\nmem %p/%zd stack %p/%d\n", type, cur_pc, addr, size, mem, mem_len, stack, UBPF_STACK_SIZE); 764 | return false; 765 | } 766 | } 767 | 768 | char * 769 | ubpf_error(const char *fmt, ...) 770 | { 771 | char *msg; 772 | va_list ap; 773 | va_start(ap, fmt); 774 | if (vasprintf(&msg, fmt, ap) < 0) { 775 | msg = NULL; 776 | } 777 | va_end(ap); 778 | return msg; 779 | } 780 | --------------------------------------------------------------------------------