From a0a78ebc7112363fbe32faf1c7291fd4f2bb9927 Mon Sep 17 00:00:00 2001 From: Quentin Monnet Date: Fri, 10 Nov 2017 16:26:58 +0000 Subject: [PATCH] Add BPF_J{LT,LE,SLT,SLE} instructions See kernel patches merged in commit 078295fb9af5. Those new instructions were added for performance reason to the kernel. Add them to uBPF. This commit adds the new instructions for the interpreter, the JIT, the assembler and the disassembler. Unit tests are also added for these instructions. As rbpf took its unit tests from uBPF, the unit tests in this commit are also consistent with the associated tests for the new instructions for rbpf. --- tests/jle-imm.data | 12 +++++++++++ tests/jle-reg.data | 14 +++++++++++++ tests/jlt-imm.data | 11 ++++++++++ tests/jlt-reg.data | 13 ++++++++++++ tests/jsle-imm.data | 12 +++++++++++ tests/jsle-reg.data | 15 ++++++++++++++ tests/jslt-imm.data | 11 ++++++++++ tests/jslt-reg.data | 13 ++++++++++++ ubpf/asm_parser.py | 2 +- ubpf/assembler.py | 4 ++++ ubpf/disassembler.py | 4 ++++ vm/ebpf.h | 8 ++++++++ vm/ubpf_jit_x86_64.c | 32 +++++++++++++++++++++++++++++ vm/ubpf_vm.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 14 files changed, 198 insertions(+), 1 deletion(-) create mode 100644 tests/jle-imm.data create mode 100644 tests/jle-reg.data create mode 100644 tests/jlt-imm.data create mode 100644 tests/jlt-reg.data create mode 100644 tests/jsle-imm.data create mode 100644 tests/jsle-reg.data create mode 100644 tests/jslt-imm.data create mode 100644 tests/jslt-reg.data diff --git a/tests/jle-imm.data b/tests/jle-imm.data new file mode 100644 index 00000000..7a1ae778 --- /dev/null +++ b/tests/jle-imm.data @@ -0,0 +1,12 @@ +-- asm +mov32 r0, 0 +mov32 r1, 5 +jle r1, 4, +1 # Not taken +jle r1, 6, +1 # Taken +exit +jle r1, 5, +1 # Taken +exit +mov32 r0, 1 +exit +-- result +0x1 diff --git a/tests/jle-reg.data b/tests/jle-reg.data new file mode 100644 index 00000000..a66a2e42 --- /dev/null +++ b/tests/jle-reg.data @@ -0,0 +1,14 @@ +-- asm +mov r0, 0 +mov r1, 5 +mov r2, 4 +mov r3, 6 +jle r1, r2, +2 # Not taken +jle r1, r1, +1 # Taken +exit +jle r1, r3, +1 # Taken +exit +mov r0, 1 +exit +-- result +0x1 diff --git a/tests/jlt-imm.data b/tests/jlt-imm.data new file mode 100644 index 00000000..96806cfe --- /dev/null +++ b/tests/jlt-imm.data @@ -0,0 +1,11 @@ +-- asm +mov32 r0, 0 +mov32 r1, 5 +jlt r1, 4, +2 # Not taken +jlt r1, 5, +1 # Not taken +jlt r1, 6, +1 # Taken +exit +mov32 r0, 1 +exit +-- result +0x1 diff --git a/tests/jlt-reg.data b/tests/jlt-reg.data new file mode 100644 index 00000000..72c5a999 --- /dev/null +++ b/tests/jlt-reg.data @@ -0,0 +1,13 @@ +-- asm +mov r0, 0 +mov r1, 5 +mov r2, 4 +mov r3, 6 +jlt r1, r2, +2 # Not taken +jlt r1, r1, +1 # Not taken +jlt r1, r3, +1 # Taken +exit +mov r0, 1 +exit +-- result +0x1 diff --git a/tests/jsle-imm.data b/tests/jsle-imm.data new file mode 100644 index 00000000..d21509b2 --- /dev/null +++ b/tests/jsle-imm.data @@ -0,0 +1,12 @@ +-- asm +mov32 r0, 0 +mov r1, 0xfffffffe +jsle r1, 0xfffffffd, +1 # Not taken +jsle r1, 0xffffffff, +1 # Taken +exit +mov32 r0, 1 +jsle r1, 0xfffffffe, +1 # Taken +mov32 r0, 2 +exit +-- result +0x1 diff --git a/tests/jsle-reg.data b/tests/jsle-reg.data new file mode 100644 index 00000000..39aa036c --- /dev/null +++ b/tests/jsle-reg.data @@ -0,0 +1,15 @@ +-- asm +mov32 r0, 0 +mov r1, 0xffffffff +mov r2, 0xfffffffe +mov32 r3, 0 +jsle r1, r2, +1 # Not taken +jsle r1, r3, +1 # Taken +exit +mov32 r0, 1 +mov r1, r2 +jsle r1, r2, +1 # Taken +mov32 r0, 2 +exit +-- result +0x1 diff --git a/tests/jslt-imm.data b/tests/jslt-imm.data new file mode 100644 index 00000000..d2f6924f --- /dev/null +++ b/tests/jslt-imm.data @@ -0,0 +1,11 @@ +-- asm +mov32 r0, 0 +mov r1, 0xfffffffe +jslt r1, 0xfffffffd, +2 # Not taken +jslt r1, 0xfffffffe, +1 # Not taken +jslt r1, 0xffffffff, +1 # Taken +exit +mov32 r0, 1 +exit +-- result +0x1 diff --git a/tests/jslt-reg.data b/tests/jslt-reg.data new file mode 100644 index 00000000..b95337a5 --- /dev/null +++ b/tests/jslt-reg.data @@ -0,0 +1,13 @@ +-- asm +mov32 r0, 0 +mov r1, 0xfffffffe +mov r2, 0xfffffffd +mov r3, 0xffffffff +jslt r1, r1, +2 # Not taken +jslt r1, r2, +1 # Not taken +jslt r1, r3, +1 # Taken +exit +mov32 r0, 1 +exit +-- result +0x1 diff --git a/ubpf/asm_parser.py b/ubpf/asm_parser.py index a55205a8..f5881b94 100644 --- a/ubpf/asm_parser.py +++ b/ubpf/asm_parser.py @@ -39,7 +39,7 @@ def keywords(vs): (keywords(mem_load_ops) + reg + "," + memref) | \ (keywords(["lddw"]) + reg + "," + imm) -jmp_cmp_ops = ['jeq', 'jgt', 'jge', 'jset', 'jne', 'jsgt', 'jsge'] +jmp_cmp_ops = ['jeq', 'jgt', 'jge', 'jlt', 'jle', 'jset', 'jne', 'jsgt', 'jsge', 'jslt', 'jsle'] jmp_instruction = \ (keywords(jmp_cmp_ops) + reg + "," + (reg | imm) + "," + offset) | \ (keywords(['ja']) + offset) | \ diff --git a/ubpf/assembler.py b/ubpf/assembler.py index df0689f2..d6b24445 100644 --- a/ubpf/assembler.py +++ b/ubpf/assembler.py @@ -54,6 +54,10 @@ 'jne': 5, 'jsgt': 6, 'jsge': 7, + 'jlt': 10, + 'jle': 11, + 'jslt': 12, + 'jsle': 13, } JMP_MISC_OPS = { diff --git a/ubpf/disassembler.py b/ubpf/disassembler.py index 86a2c359..c432d945 100644 --- a/ubpf/disassembler.py +++ b/ubpf/disassembler.py @@ -41,6 +41,10 @@ 7: 'jsge', 8: 'call', 9: 'exit', + 10: 'jlt', + 11: 'jle', + 12: 'jslt', + 13: 'jsle', } MODES = { diff --git a/vm/ebpf.h b/vm/ebpf.h index fed1a2c5..f1fe1674 100644 --- a/vm/ebpf.h +++ b/vm/ebpf.h @@ -137,5 +137,13 @@ struct ebpf_inst { #define EBPF_OP_JSGE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0x70) #define EBPF_OP_CALL (EBPF_CLS_JMP|0x80) #define EBPF_OP_EXIT (EBPF_CLS_JMP|0x90) +#define EBPF_OP_JLT_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xa0) +#define EBPF_OP_JLT_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xa0) +#define EBPF_OP_JLE_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xb0) +#define EBPF_OP_JLE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xb0) +#define EBPF_OP_JSLT_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xc0) +#define EBPF_OP_JSLT_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xc0) +#define EBPF_OP_JSLE_IMM (EBPF_CLS_JMP|EBPF_SRC_IMM|0xd0) +#define EBPF_OP_JSLE_REG (EBPF_CLS_JMP|EBPF_SRC_REG|0xd0) #endif diff --git a/vm/ubpf_jit_x86_64.c b/vm/ubpf_jit_x86_64.c index 2531875e..2f86130a 100644 --- a/vm/ubpf_jit_x86_64.c +++ b/vm/ubpf_jit_x86_64.c @@ -293,6 +293,22 @@ translate(struct ubpf_vm *vm, struct jit_state *state, char **errmsg) emit_cmp(state, src, dst); emit_jcc(state, 0x83, target_pc); break; + case EBPF_OP_JLT_IMM: + emit_cmp_imm32(state, dst, inst.imm); + emit_jcc(state, 0x82, target_pc); + break; + case EBPF_OP_JLT_REG: + emit_cmp(state, src, dst); + emit_jcc(state, 0x82, target_pc); + break; + case EBPF_OP_JLE_IMM: + emit_cmp_imm32(state, dst, inst.imm); + emit_jcc(state, 0x86, target_pc); + break; + case EBPF_OP_JLE_REG: + emit_cmp(state, src, dst); + emit_jcc(state, 0x86, target_pc); + break; case EBPF_OP_JSET_IMM: emit_alu64_imm32(state, 0xf7, 0, dst, inst.imm); emit_jcc(state, 0x85, target_pc); @@ -325,6 +341,22 @@ translate(struct ubpf_vm *vm, struct jit_state *state, char **errmsg) emit_cmp(state, src, dst); emit_jcc(state, 0x8d, target_pc); break; + case EBPF_OP_JSLT_IMM: + emit_cmp_imm32(state, dst, inst.imm); + emit_jcc(state, 0x8c, target_pc); + break; + case EBPF_OP_JSLT_REG: + emit_cmp(state, src, dst); + emit_jcc(state, 0x8c, target_pc); + break; + case EBPF_OP_JSLE_IMM: + emit_cmp_imm32(state, dst, inst.imm); + emit_jcc(state, 0x8e, target_pc); + break; + case EBPF_OP_JSLE_REG: + emit_cmp(state, src, dst); + emit_jcc(state, 0x8e, target_pc); + break; case EBPF_OP_CALL: /* We reserve RCX for shifts */ emit_mov(state, R9, RCX); diff --git a/vm/ubpf_vm.c b/vm/ubpf_vm.c index fab2083b..1eddb944 100644 --- a/vm/ubpf_vm.c +++ b/vm/ubpf_vm.c @@ -465,6 +465,26 @@ ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len) pc += inst.offset; } break; + case EBPF_OP_JLT_IMM: + if (reg[inst.dst] < (uint32_t)inst.imm) { + pc += inst.offset; + } + break; + case EBPF_OP_JLT_REG: + if (reg[inst.dst] < reg[inst.src]) { + pc += inst.offset; + } + break; + case EBPF_OP_JLE_IMM: + if (reg[inst.dst] <= (uint32_t)inst.imm) { + pc += inst.offset; + } + break; + case EBPF_OP_JLE_REG: + if (reg[inst.dst] <= reg[inst.src]) { + pc += inst.offset; + } + break; case EBPF_OP_JSET_IMM: if (reg[inst.dst] & inst.imm) { pc += inst.offset; @@ -505,6 +525,26 @@ ubpf_exec(const struct ubpf_vm *vm, void *mem, size_t mem_len) pc += inst.offset; } break; + case EBPF_OP_JSLT_IMM: + if ((int64_t)reg[inst.dst] < inst.imm) { + pc += inst.offset; + } + break; + case EBPF_OP_JSLT_REG: + if ((int64_t)reg[inst.dst] < (int64_t)reg[inst.src]) { + pc += inst.offset; + } + break; + case EBPF_OP_JSLE_IMM: + if ((int64_t)reg[inst.dst] <= inst.imm) { + pc += inst.offset; + } + break; + case EBPF_OP_JSLE_REG: + if ((int64_t)reg[inst.dst] <= (int64_t)reg[inst.src]) { + pc += inst.offset; + } + break; case EBPF_OP_EXIT: return reg[0]; case EBPF_OP_CALL: @@ -623,6 +663,10 @@ validate(const struct ubpf_vm *vm, const struct ebpf_inst *insts, uint32_t num_i case EBPF_OP_JGT_IMM: case EBPF_OP_JGE_REG: case EBPF_OP_JGE_IMM: + case EBPF_OP_JLT_REG: + case EBPF_OP_JLT_IMM: + case EBPF_OP_JLE_REG: + case EBPF_OP_JLE_IMM: case EBPF_OP_JSET_REG: case EBPF_OP_JSET_IMM: case EBPF_OP_JNE_REG: @@ -631,6 +675,10 @@ validate(const struct ubpf_vm *vm, const struct ebpf_inst *insts, uint32_t num_i case EBPF_OP_JSGT_REG: case EBPF_OP_JSGE_IMM: case EBPF_OP_JSGE_REG: + case EBPF_OP_JSLT_IMM: + case EBPF_OP_JSLT_REG: + case EBPF_OP_JSLE_IMM: + case EBPF_OP_JSLE_REG: if (inst.offset == -1) { *errmsg = ubpf_error("infinite loop at PC %d", i); return false;