Skip to content

Commit

Permalink
Add BPF_J{LT,LE,SLT,SLE} instructions
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
qmonnet committed Nov 10, 2017
1 parent b8c87d2 commit a0a78eb
Show file tree
Hide file tree
Showing 14 changed files with 198 additions and 1 deletion.
12 changes: 12 additions & 0 deletions 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
14 changes: 14 additions & 0 deletions 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
11 changes: 11 additions & 0 deletions 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
13 changes: 13 additions & 0 deletions 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
12 changes: 12 additions & 0 deletions 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
15 changes: 15 additions & 0 deletions 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
11 changes: 11 additions & 0 deletions 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
13 changes: 13 additions & 0 deletions 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
2 changes: 1 addition & 1 deletion ubpf/asm_parser.py
Expand Up @@ -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) | \
Expand Down
4 changes: 4 additions & 0 deletions ubpf/assembler.py
Expand Up @@ -54,6 +54,10 @@
'jne': 5,
'jsgt': 6,
'jsge': 7,
'jlt': 10,
'jle': 11,
'jslt': 12,
'jsle': 13,
}

JMP_MISC_OPS = {
Expand Down
4 changes: 4 additions & 0 deletions ubpf/disassembler.py
Expand Up @@ -41,6 +41,10 @@
7: 'jsge',
8: 'call',
9: 'exit',
10: 'jlt',
11: 'jle',
12: 'jslt',
13: 'jsle',
}

MODES = {
Expand Down
8 changes: 8 additions & 0 deletions vm/ebpf.h
Expand Up @@ -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
32 changes: 32 additions & 0 deletions vm/ubpf_jit_x86_64.c
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
48 changes: 48 additions & 0 deletions vm/ubpf_vm.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand All @@ -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;
Expand Down

0 comments on commit a0a78eb

Please sign in to comment.