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;