Browse Source

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 rbpf so as to remain as compatible as possible with Linux.

This commit adds the new instructions for the interpreter, the JIT, the
verifier, the assembler, the disassembler and the program builder. Unit
tests are also added for these instructions.

Fixes issue #18.
Quentin Monnet 7 years ago
parent
commit
53b0fce923
11 changed files with 477 additions and 23 deletions
  1. 5 1
      src/assembler.rs
  2. 8 0
      src/disassembler.rs
  3. 24 0
      src/ebpf.rs
  4. 73 1
      src/insn_builder.rs
  5. 32 0
      src/jit.rs
  6. 23 15
      src/lib.rs
  7. 8 0
      src/verifier.rs
  8. 20 4
      tests/assembler.rs
  9. 10 2
      tests/disassembler.rs
  10. 141 0
      tests/ubpf_jit_x86_64.rs
  11. 133 0
      tests/ubpf_vm.rs

+ 5 - 1
src/assembler.rs

@@ -54,10 +54,14 @@ fn make_instruction_map() -> HashMap<String, (InstructionType, u8)> {
     let jump_conditions = [("jeq", ebpf::BPF_JEQ),
                            ("jgt", ebpf::BPF_JGT),
                            ("jge", ebpf::BPF_JGE),
+                           ("jlt", ebpf::BPF_JLT),
+                           ("jle", ebpf::BPF_JLE),
                            ("jset", ebpf::BPF_JSET),
                            ("jne", ebpf::BPF_JNE),
                            ("jsgt", ebpf::BPF_JSGT),
-                           ("jsge", ebpf::BPF_JSGE)];
+                           ("jsge", ebpf::BPF_JSGE),
+                           ("jslt", ebpf::BPF_JSLT),
+                           ("jsle", ebpf::BPF_JSLE)];
 
     {
         let mut entry = |name: &str, inst_type: InstructionType, opc: u8| {

+ 8 - 0
src/disassembler.rs

@@ -270,6 +270,10 @@ pub fn to_insn_vec(prog: &[u8]) -> Vec<HLInsn> {
             ebpf::JGT_REG    => { name = "jgt";  desc = jmp_reg_str(name, &insn); },
             ebpf::JGE_IMM    => { name = "jge";  desc = jmp_imm_str(name, &insn); },
             ebpf::JGE_REG    => { name = "jge";  desc = jmp_reg_str(name, &insn); },
+            ebpf::JLT_IMM    => { name = "jlt";  desc = jmp_imm_str(name, &insn); },
+            ebpf::JLT_REG    => { name = "jlt";  desc = jmp_reg_str(name, &insn); },
+            ebpf::JLE_IMM    => { name = "jle";  desc = jmp_imm_str(name, &insn); },
+            ebpf::JLE_REG    => { name = "jle";  desc = jmp_reg_str(name, &insn); },
             ebpf::JSET_IMM   => { name = "jset"; desc = jmp_imm_str(name, &insn); },
             ebpf::JSET_REG   => { name = "jset"; desc = jmp_reg_str(name, &insn); },
             ebpf::JNE_IMM    => { name = "jne";  desc = jmp_imm_str(name, &insn); },
@@ -278,6 +282,10 @@ pub fn to_insn_vec(prog: &[u8]) -> Vec<HLInsn> {
             ebpf::JSGT_REG   => { name = "jsgt"; desc = jmp_reg_str(name, &insn); },
             ebpf::JSGE_IMM   => { name = "jsge"; desc = jmp_imm_str(name, &insn); },
             ebpf::JSGE_REG   => { name = "jsge"; desc = jmp_reg_str(name, &insn); },
+            ebpf::JSLT_IMM   => { name = "jslt"; desc = jmp_imm_str(name, &insn); },
+            ebpf::JSLT_REG   => { name = "jslt"; desc = jmp_reg_str(name, &insn); },
+            ebpf::JSLE_IMM   => { name = "jsle"; desc = jmp_imm_str(name, &insn); },
+            ebpf::JSLE_REG   => { name = "jsle"; desc = jmp_reg_str(name, &insn); },
             ebpf::CALL       => { name = "call"; desc = format!("{} {:#x}", name, insn.imm); },
             ebpf::TAIL_CALL  => { name = "tail_call"; desc = name.to_string(); },
             ebpf::EXIT       => { name = "exit";      desc = name.to_string(); },

+ 24 - 0
src/ebpf.rs

@@ -142,6 +142,14 @@ pub const BPF_JSGE  : u8 = 0x70;
 pub const BPF_CALL  : u8 = 0x80;
 /// BPF JMP operation code: return from program.
 pub const BPF_EXIT  : u8 = 0x90;
+/// BPF JMP operation code: jump if lower than.
+pub const BPF_JLT   : u8 = 0xa0;
+/// BPF JMP operation code: jump if lower or equal.
+pub const BPF_JLE   : u8 = 0xb0;
+/// BPF JMP operation code: jump if lower than (signed).
+pub const BPF_JSLT  : u8 = 0xc0;
+/// BPF JMP operation code: jump if lower or equal (signed).
+pub const BPF_JSLE  : u8 = 0xd0;
 
 // Op codes
 // (Following operation names are not “official”, but may be proper to rbpf; Linux kernel only
@@ -327,6 +335,14 @@ pub const JGT_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JGT;
 pub const JGE_IMM    : u8 = BPF_JMP   | BPF_K   | BPF_JGE;
 /// BPF opcode: `jge dst, src, +off` /// `PC += off if dst >= src`.
 pub const JGE_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JGE;
+/// BPF opcode: `jlt dst, imm, +off` /// `PC += off if dst < imm`.
+pub const JLT_IMM    : u8 = BPF_JMP   | BPF_K   | BPF_JLT;
+/// BPF opcode: `jlt dst, src, +off` /// `PC += off if dst < src`.
+pub const JLT_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JLT;
+/// BPF opcode: `jle dst, imm, +off` /// `PC += off if dst <= imm`.
+pub const JLE_IMM    : u8 = BPF_JMP   | BPF_K   | BPF_JLE;
+/// BPF opcode: `jle dst, src, +off` /// `PC += off if dst <= src`.
+pub const JLE_REG    : u8 = BPF_JMP   | BPF_X   | BPF_JLE;
 /// BPF opcode: `jset dst, imm, +off` /// `PC += off if dst & imm`.
 pub const JSET_IMM   : u8 = BPF_JMP   | BPF_K   | BPF_JSET;
 /// BPF opcode: `jset dst, src, +off` /// `PC += off if dst & src`.
@@ -343,6 +359,14 @@ pub const JSGT_REG   : u8 = BPF_JMP   | BPF_X   | BPF_JSGT;
 pub const JSGE_IMM   : u8 = BPF_JMP   | BPF_K   | BPF_JSGE;
 /// BPF opcode: `jsge dst, src, +off` /// `PC += off if dst >= src (signed)`.
 pub const JSGE_REG   : u8 = BPF_JMP   | BPF_X   | BPF_JSGE;
+/// BPF opcode: `jslt dst, imm, +off` /// `PC += off if dst < imm (signed)`.
+pub const JSLT_IMM   : u8 = BPF_JMP   | BPF_K   | BPF_JSLT;
+/// BPF opcode: `jslt dst, src, +off` /// `PC += off if dst < src (signed)`.
+pub const JSLT_REG   : u8 = BPF_JMP   | BPF_X   | BPF_JSLT;
+/// BPF opcode: `jsle dst, imm, +off` /// `PC += off if dst <= imm (signed)`.
+pub const JSLE_IMM   : u8 = BPF_JMP   | BPF_K   | BPF_JSLE;
+/// BPF opcode: `jsle dst, src, +off` /// `PC += off if dst <= src (signed)`.
+pub const JSLE_REG   : u8 = BPF_JMP   | BPF_X   | BPF_JSLE;
 
 /// BPF opcode: `call imm` /// helper function call to helper with key `imm`.
 pub const CALL       : u8 = BPF_JMP   | BPF_CALL;

+ 73 - 1
src/insn_builder.rs

@@ -567,6 +567,10 @@ pub enum Cond {
     Greater = BPF_JGT as isize,
     /// Jump if `>=`
     GreaterEquals = BPF_JGE as isize,
+    /// Jump if `<`
+    Lower = BPF_JLT as isize,
+    /// Jump if `<=`
+    LowerEquals = BPF_JLE as isize,
     /// Jump if `src` & `dst`
     BitAnd = BPF_JSET as isize,
     /// Jump if `!=`
@@ -574,7 +578,11 @@ pub enum Cond {
     /// Jump if `>` (signed)
     GreaterSigned = BPF_JSGT as isize,
     /// Jump if `>=` (signed)
-    GreaterEqualsSigned = BPF_JSGE as isize
+    GreaterEqualsSigned = BPF_JSGE as isize,
+    /// Jump if `<` (signed)
+    LowerSigned = BPF_JSLT as isize,
+    /// Jump if `<=` (signed)
+    LowerEqualsSigned = BPF_JSLE as isize
 }
 
 /// struct representation of CALL instruction
@@ -688,6 +696,22 @@ mod tests {
                 assert_eq!(program.into_bytes(), &[0x3d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
             }
 
+            #[test]
+            fn jump_on_dst_lower_than_src() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::Lower, Source::Reg).set_dst(0x03).set_src(0x02).push();
+
+                assert_eq!(program.into_bytes(), &[0xad, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+            }
+
+            #[test]
+            fn jump_on_dst_lower_or_equals_to_src() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::LowerEquals, Source::Reg).set_dst(0x04).set_src(0x01).push();
+
+                assert_eq!(program.into_bytes(), &[0xbd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+            }
+
             #[test]
             fn jump_on_dst_bit_and_with_src_not_equal_zero() {
                 let mut program = BpfCode::new();
@@ -719,6 +743,22 @@ mod tests {
 
                 assert_eq!(program.into_bytes(), &[0x7d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
             }
+
+            #[test]
+            fn jump_on_dst_lower_than_src_signed() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::LowerSigned, Source::Reg).set_dst(0x04).set_src(0x01).push();
+
+                assert_eq!(program.into_bytes(), &[0xcd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+            }
+
+            #[test]
+            fn jump_on_dst_lower_or_equals_src_signed() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::LowerEqualsSigned, Source::Reg).set_dst(0x01).set_src(0x03).push();
+
+                assert_eq!(program.into_bytes(), &[0xdd, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+            }
         }
 
         #[cfg(test)]
@@ -757,6 +797,22 @@ mod tests {
                 assert_eq!(program.into_bytes(), &[0x35, 0x04, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00]);
             }
 
+            #[test]
+            fn jump_on_dst_lower_than_const() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::Lower, Source::Imm).set_dst(0x02).set_imm(0x00_11_00_11).push();
+
+                assert_eq!(program.into_bytes(), &[0xa5, 0x02, 0x00, 0x00, 0x11, 0x00, 0x11, 0x00]);
+            }
+
+            #[test]
+            fn jump_on_dst_lower_or_equals_to_const() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::LowerEquals, Source::Imm).set_dst(0x04).set_imm(0x00_22_11_00).push();
+
+                assert_eq!(program.into_bytes(), &[0xb5, 0x04, 0x00, 0x00, 0x00, 0x11, 0x22, 0x00]);
+            }
+
             #[test]
             fn jump_on_dst_bit_and_with_const_not_equal_zero() {
                 let mut program = BpfCode::new();
@@ -788,6 +844,22 @@ mod tests {
 
                 assert_eq!(program.into_bytes(), &[0x75, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
             }
+
+            #[test]
+            fn jump_on_dst_lower_than_const_signed() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::LowerSigned, Source::Imm).set_dst(0x04).push();
+
+                assert_eq!(program.into_bytes(), &[0xc5, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+            }
+
+            #[test]
+            fn jump_on_dst_lower_or_equals_src_signed() {
+                let mut program = BpfCode::new();
+                program.jump_conditional(Cond::LowerEqualsSigned, Source::Imm).set_dst(0x01).push();
+
+                assert_eq!(program.into_bytes(), &[0xd5, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
+            }
         }
     }
 

+ 32 - 0
src/jit.rs

@@ -703,6 +703,22 @@ impl<'a> JitMemory<'a> {
                     emit_cmp(self, src, dst);
                     emit_jcc(self, 0x83, target_pc);
                 },
+                ebpf::JLT_IMM    => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x82, target_pc);
+                },
+                ebpf::JLT_REG    => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x82, target_pc);
+                },
+                ebpf::JLE_IMM    => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x86, target_pc);
+                },
+                ebpf::JLE_REG    => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x86, target_pc);
+                },
                 ebpf::JSET_IMM   => {
                     emit_alu64_imm32(self, 0xf7, 0, dst, insn.imm);
                     emit_jcc(self, 0x85, target_pc);
@@ -735,6 +751,22 @@ impl<'a> JitMemory<'a> {
                     emit_cmp(self, src, dst);
                     emit_jcc(self, 0x8d, target_pc);
                 },
+                ebpf::JSLT_IMM   => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x8c, target_pc);
+                },
+                ebpf::JSLT_REG   => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x8c, target_pc);
+                },
+                ebpf::JSLE_IMM   => {
+                    emit_cmp_imm32(self, dst, insn.imm);
+                    emit_jcc(self, 0x8e, target_pc);
+                },
+                ebpf::JSLE_REG   => {
+                    emit_cmp(self, src, dst);
+                    emit_jcc(self, 0x8e, target_pc);
+                },
                 ebpf::CALL       => {
                     // For JIT, helpers in use MUST be registered at compile time. They can be
                     // updated later, but not created after compiling (we need the address of the

+ 23 - 15
src/lib.rs

@@ -477,21 +477,29 @@ impl<'a> EbpfVmMbuff<'a> {
 
                 // BPF_JMP class
                 // TODO: check this actually works as expected for signed / unsigned ops
-                ebpf::JA         =>                                           insn_ptr = (insn_ptr as i16 + insn.off) as usize,
-                ebpf::JEQ_IMM    => if reg[_dst] == insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JEQ_REG    => if reg[_dst] == reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JGT_IMM    => if reg[_dst] >  insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JGT_REG    => if reg[_dst] >  reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JGE_IMM    => if reg[_dst] >= insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JGE_REG    => if reg[_dst] >= reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JSET_IMM   => if reg[_dst] &  insn.imm as u64 != 0    { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JSET_REG   => if reg[_dst] &  reg[_src]       != 0    { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JNE_IMM    => if reg[_dst] != insn.imm as u64         { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JNE_REG    => if reg[_dst] != reg[_src]               { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JSGT_IMM   => if reg[_dst] as i64 >  insn.imm  as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JSGT_REG   => if reg[_dst] as i64 >  reg[_src] as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JSGE_IMM   => if reg[_dst] as i64 >= insn.imm  as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
-                ebpf::JSGE_REG   => if reg[_dst] as i64 >= reg[_src] as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JA         =>                                             insn_ptr = (insn_ptr as i16 + insn.off) as usize,
+                ebpf::JEQ_IMM    => if  reg[_dst] == insn.imm as u64          { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JEQ_REG    => if  reg[_dst] == reg[_src]                { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGT_IMM    => if  reg[_dst] >  insn.imm as u64          { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGT_REG    => if  reg[_dst] >  reg[_src]                { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGE_IMM    => if  reg[_dst] >= insn.imm as u64          { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JGE_REG    => if  reg[_dst] >= reg[_src]                { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JLT_IMM    => if  reg[_dst] <  insn.imm as u64          { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JLT_REG    => if  reg[_dst] <  reg[_src]                { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JLE_IMM    => if  reg[_dst] <= insn.imm as u64          { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JLE_REG    => if  reg[_dst] <= reg[_src]                { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSET_IMM   => if  reg[_dst] &  insn.imm as u64 != 0     { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSET_REG   => if  reg[_dst] &  reg[_src]       != 0     { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JNE_IMM    => if  reg[_dst] != insn.imm as u64          { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JNE_REG    => if  reg[_dst] != reg[_src]                { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGT_IMM   => if  reg[_dst] as i64 >  insn.imm  as i64  { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGT_REG   => if  reg[_dst] as i64 >  reg[_src] as i64  { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGE_IMM   => if  reg[_dst] as i64 >= insn.imm  as i64  { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSGE_REG   => if  reg[_dst] as i64 >= reg[_src] as i64  { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSLT_IMM   => if (reg[_dst] as i64) <  insn.imm  as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSLT_REG   => if (reg[_dst] as i64) <  reg[_src] as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSLE_IMM   => if (reg[_dst] as i64) <= insn.imm  as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
+                ebpf::JSLE_REG   => if (reg[_dst] as i64) <= reg[_src] as i64 { insn_ptr = (insn_ptr as i16 + insn.off) as usize; },
                 // Do not delegate the check to the verifier, since registered functions can be
                 // changed after the program has been verified.
                 ebpf::CALL       => if let Some(function) = self.helpers.get(&(insn.imm as u32)) {

+ 8 - 0
src/verifier.rs

@@ -210,6 +210,10 @@ pub fn check(prog: &[u8]) -> bool {
             ebpf::JGT_REG    => { check_jmp_offset(prog, insn_ptr); },
             ebpf::JGE_IMM    => { check_jmp_offset(prog, insn_ptr); },
             ebpf::JGE_REG    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JLT_IMM    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JLT_REG    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JLE_IMM    => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JLE_REG    => { check_jmp_offset(prog, insn_ptr); },
             ebpf::JSET_IMM   => { check_jmp_offset(prog, insn_ptr); },
             ebpf::JSET_REG   => { check_jmp_offset(prog, insn_ptr); },
             ebpf::JNE_IMM    => { check_jmp_offset(prog, insn_ptr); },
@@ -218,6 +222,10 @@ pub fn check(prog: &[u8]) -> bool {
             ebpf::JSGT_REG   => { check_jmp_offset(prog, insn_ptr); },
             ebpf::JSGE_IMM   => { check_jmp_offset(prog, insn_ptr); },
             ebpf::JSGE_REG   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSLT_IMM   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSLT_REG   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSLE_IMM   => { check_jmp_offset(prog, insn_ptr); },
+            ebpf::JSLE_REG   => { check_jmp_offset(prog, insn_ptr); },
             ebpf::CALL       => {},
             ebpf::TAIL_CALL  => { unimplemented!() },
             ebpf::EXIT       => {},

+ 20 - 4
tests/assembler.rs

@@ -382,32 +382,48 @@ fn test_jump_conditional() {
     assert_eq!(asm("jeq r1, r2, +3
                     jgt r1, r2, +3
                     jge r1, r2, +3
+                    jlt r1, r2, +3
+                    jle r1, r2, +3
                     jset r1, r2, +3
                     jne r1, r2, +3
                     jsgt r1, r2, +3
-                    jsge r1, r2, +3"),
+                    jsge r1, r2, +3
+                    jslt r1, r2, +3
+                    jsle r1, r2, +3"),
                Ok(vec![insn(ebpf::JEQ_REG, 1, 2, 3, 0),
                        insn(ebpf::JGT_REG, 1, 2, 3, 0),
                        insn(ebpf::JGE_REG, 1, 2, 3, 0),
+                       insn(ebpf::JLT_REG, 1, 2, 3, 0),
+                       insn(ebpf::JLE_REG, 1, 2, 3, 0),
                        insn(ebpf::JSET_REG, 1, 2, 3, 0),
                        insn(ebpf::JNE_REG, 1, 2, 3, 0),
                        insn(ebpf::JSGT_REG, 1, 2, 3, 0),
-                       insn(ebpf::JSGE_REG, 1, 2, 3, 0)]));
+                       insn(ebpf::JSGE_REG, 1, 2, 3, 0),
+                       insn(ebpf::JSLT_REG, 1, 2, 3, 0),
+                       insn(ebpf::JSLE_REG, 1, 2, 3, 0)]));
 
     assert_eq!(asm("jeq r1, 2, +3
                     jgt r1, 2, +3
                     jge r1, 2, +3
+                    jlt r1, 2, +3
+                    jle r1, 2, +3
                     jset r1, 2, +3
                     jne r1, 2, +3
                     jsgt r1, 2, +3
-                    jsge r1, 2, +3"),
+                    jsge r1, 2, +3
+                    jslt r1, 2, +3
+                    jsle r1, 2, +3"),
                Ok(vec![insn(ebpf::JEQ_IMM, 1, 0, 3, 2),
                        insn(ebpf::JGT_IMM, 1, 0, 3, 2),
                        insn(ebpf::JGE_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JLT_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JLE_IMM, 1, 0, 3, 2),
                        insn(ebpf::JSET_IMM, 1, 0, 3, 2),
                        insn(ebpf::JNE_IMM, 1, 0, 3, 2),
                        insn(ebpf::JSGT_IMM, 1, 0, 3, 2),
-                       insn(ebpf::JSGE_IMM, 1, 0, 3, 2)]));
+                       insn(ebpf::JSGE_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JSLT_IMM, 1, 0, 3, 2),
+                       insn(ebpf::JSLE_IMM, 1, 0, 3, 2)]));
 }
 
 // Test all supported Endian mnemonics.

+ 10 - 2
tests/disassembler.rs

@@ -245,18 +245,26 @@ fn test_jump_conditional() {
     disasm!("jeq r1, r2, +0x3
 jgt r1, r2, +0x3
 jge r1, r2, +0x3
+jlt r1, r2, +0x3
+jle r1, r2, +0x3
 jset r1, r2, +0x3
 jne r1, r2, +0x3
 jsgt r1, r2, +0x3
-jsge r1, r2, +0x3");
+jsge r1, r2, +0x3
+jslt r1, r2, +0x3
+jsle r1, r2, +0x3");
 
     disasm!("jeq r1, 0x2, +0x3
 jgt r1, 0x2, +0x3
 jge r1, 0x2, +0x3
+jlt r1, 0x2, +0x3
+jle r1, 0x2, +0x3
 jset r1, 0x2, +0x3
 jne r1, 0x2, +0x3
 jsgt r1, 0x2, +0x3
-jsge r1, 0x2, +0x3");
+jsge r1, 0x2, +0x3
+jslt r1, 0x2, +0x3
+jsle r1, 0x2, +0x3");
 }
 
 // Test all supported Endian mnemonics.

+ 141 - 0
tests/ubpf_jit_x86_64.rs

@@ -570,6 +570,42 @@ fn test_jit_jge_imm() {
     unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
 }
 
+#[test]
+fn test_jit_jle_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov32 r1, 5
+        jle r1, 4, +1
+        jle r1, 6, +1
+        exit
+        jle r1, 5, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
+#[test]
+fn test_jit_jle_reg() {
+    let prog = assemble("
+        mov r0, 0
+        mov r1, 5
+        mov r2, 4
+        mov r3, 6
+        jle r1, r2, +2
+        jle r1, r1, +1
+        exit
+        jle r1, r3, +1
+        exit
+        mov r0, 1
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
 #[test]
 fn test_jit_jgt_imm() {
     let prog = assemble("
@@ -604,6 +640,40 @@ fn test_jit_jgt_reg() {
     unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
 }
 
+#[test]
+fn test_jit_jlt_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov32 r1, 5
+        jlt r1, 4, +2
+        jlt r1, 5, +1
+        jlt r1, 6, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
+#[test]
+fn test_jit_jlt_reg() {
+    let prog = assemble("
+        mov r0, 0
+        mov r1, 5
+        mov r2, 4
+        mov r3, 6
+        jlt r1, r2, +2
+        jlt r1, r1, +1
+        jlt r1, r3, +1
+        exit
+        mov r0, 1
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
 #[test]
 fn test_jit_jit_bounce() {
     let prog = assemble("
@@ -705,6 +775,43 @@ fn test_jit_jsge_reg() {
     unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
 }
 
+#[test]
+fn test_jit_jsle_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -2
+        jsle r1, -3, +1
+        jsle r1, -1, +1
+        exit
+        mov32 r0, 1
+        jsle r1, -2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
+#[test]
+fn test_jit_jsle_reg() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -1
+        mov r2, -2
+        mov32 r3, 0
+        jsle r1, r2, +1
+        jsle r1, r3, +1
+        exit
+        mov32 r0, 1
+        mov r1, r2
+        jsle r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
 #[test]
 fn test_jit_jsgt_imm() {
     let prog = assemble("
@@ -738,6 +845,40 @@ fn test_jit_jsgt_reg() {
     unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
 }
 
+#[test]
+fn test_jit_jslt_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -2
+        jslt r1, -3, +2
+        jslt r1, -2, +1
+        jslt r1, -1, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
+#[test]
+fn test_jit_jslt_reg() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -2
+        mov r2, -3
+        mov r3, -1
+        jslt r1, r1, +2
+        jslt r1, r2, +1
+        jslt r1, r3, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let mut vm = rbpf::EbpfVmNoData::new(&prog);
+    vm.jit_compile();
+    unsafe { assert_eq!(vm.prog_exec_jit(), 0x1); }
+}
+
 #[test]
 fn test_jit_lddw() {
     let prog = assemble("

+ 133 - 0
tests/ubpf_vm.rs

@@ -528,6 +528,40 @@ fn test_vm_jge_imm() {
     assert_eq!(vm.prog_exec(), 0x1);
 }
 
+#[test]
+fn test_vm_jle_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov32 r1, 5
+        jle r1, 4, +1
+        jle r1, 6, +1
+        exit
+        jle r1, 5, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jle_reg() {
+    let prog = assemble("
+        mov r0, 0
+        mov r1, 5
+        mov r2, 4
+        mov r3, 6
+        jle r1, r2, +2
+        jle r1, r1, +1
+        exit
+        jle r1, r3, +1
+        exit
+        mov r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
 #[test]
 fn test_vm_jgt_imm() {
     let prog = assemble("
@@ -560,6 +594,38 @@ fn test_vm_jgt_reg() {
     assert_eq!(vm.prog_exec(), 0x1);
 }
 
+#[test]
+fn test_vm_jlt_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov32 r1, 5
+        jlt r1, 4, +2
+        jlt r1, 5, +1
+        jlt r1, 6, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jlt_reg() {
+    let prog = assemble("
+        mov r0, 0
+        mov r1, 5
+        mov r2, 4
+        mov r3, 6
+        jlt r1, r2, +2
+        jlt r1, r1, +1
+        jlt r1, r3, +1
+        exit
+        mov r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
 #[test]
 fn test_vm_jit_bounce() {
     let prog = assemble("
@@ -655,6 +721,41 @@ fn test_vm_jsge_reg() {
     assert_eq!(vm.prog_exec(), 0x1);
 }
 
+#[test]
+fn test_vm_jsle_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -2
+        jsle r1, -3, +1
+        jsle r1, -1, +1
+        exit
+        mov32 r0, 1
+        jsle r1, -2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jsle_reg() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -1
+        mov r2, -2
+        mov32 r3, 0
+        jsle r1, r2, +1
+        jsle r1, r3, +1
+        exit
+        mov32 r0, 1
+        mov r1, r2
+        jsle r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
 #[test]
 fn test_vm_jsgt_imm() {
     let prog = assemble("
@@ -686,6 +787,38 @@ fn test_vm_jsgt_reg() {
     assert_eq!(vm.prog_exec(), 0x1);
 }
 
+#[test]
+fn test_vm_jslt_imm() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -2
+        jslt r1, -3, +2
+        jslt r1, -2, +1
+        jslt r1, -1, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
+#[test]
+fn test_vm_jslt_reg() {
+    let prog = assemble("
+        mov32 r0, 0
+        mov r1, -2
+        mov r2, -3
+        mov r3, -1
+        jslt r1, r1, +2
+        jslt r1, r2, +1
+        jslt r1, r3, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(&prog);
+    assert_eq!(vm.prog_exec(), 0x1);
+}
+
 #[test]
 fn test_vm_lddw() {
     let prog = assemble("lddw r0, 0x1122334455667788