Bladeren bron

src/lib.rs: Add support for 32-bit jumps in interpreter and verifier

Add and test support for 32-bit jump instructions in rbpf's interpreter.
To run programs with these instructions we also need to declare them in
the verifier, and to make sure that the jumps are not out-of-bounds. Now
that we support 32-bit jumps, add tests to make sure the implementation
works as expected.

The tests are copied (nearly to the identical) from
https://github.com/Alan-Jowett/bpf_conformance, where I undersand they
derive from existing uBPF tests.

Signed-off-by: Quentin Monnet <quentin@isovalent.com>
Quentin Monnet 2 jaren geleden
bovenliggende
commit
7e8875e036
3 gewijzigde bestanden met toevoegingen van 473 en 0 verwijderingen
  1. 25 0
      src/lib.rs
  2. 25 0
      src/verifier.rs
  3. 423 0
      tests/ubpf_vm.rs

+ 25 - 0
src/lib.rs

@@ -549,6 +549,31 @@ impl<'a> EbpfVmMbuff<'a> {
                 ebpf::JSLT_REG   => if (reg[_dst] as i64) <  reg[_src] as i64 { do_jump(); },
                 ebpf::JSLE_IMM   => if  reg[_dst] as i64  <= insn.imm  as i64 { do_jump(); },
                 ebpf::JSLE_REG   => if  reg[_dst] as i64  <= reg[_src] as i64 { do_jump(); },
+
+                // BPF_JMP32 class
+                ebpf::JEQ_IMM32  => if  reg[_dst] as u32  == insn.imm  as u32      { do_jump(); },
+                ebpf::JEQ_REG32  => if  reg[_dst] as u32  == reg[_src] as u32      { do_jump(); },
+                ebpf::JGT_IMM32  => if  reg[_dst] as u32  >  insn.imm  as u32      { do_jump(); },
+                ebpf::JGT_REG32  => if  reg[_dst] as u32  >  reg[_src] as u32      { do_jump(); },
+                ebpf::JGE_IMM32  => if  reg[_dst] as u32  >= insn.imm  as u32      { do_jump(); },
+                ebpf::JGE_REG32  => if  reg[_dst] as u32  >= reg[_src] as u32      { do_jump(); },
+                ebpf::JLT_IMM32  => if (reg[_dst] as u32) <  insn.imm  as u32      { do_jump(); },
+                ebpf::JLT_REG32  => if (reg[_dst] as u32) <  reg[_src] as u32      { do_jump(); },
+                ebpf::JLE_IMM32  => if  reg[_dst] as u32  <= insn.imm  as u32      { do_jump(); },
+                ebpf::JLE_REG32  => if  reg[_dst] as u32  <= reg[_src] as u32      { do_jump(); },
+                ebpf::JSET_IMM32 => if  reg[_dst] as u32  &  insn.imm  as u32 != 0 { do_jump(); },
+                ebpf::JSET_REG32 => if  reg[_dst] as u32  &  reg[_src] as u32 != 0 { do_jump(); },
+                ebpf::JNE_IMM32  => if  reg[_dst] as u32  != insn.imm  as u32      { do_jump(); },
+                ebpf::JNE_REG32  => if  reg[_dst] as u32  != reg[_src] as u32      { do_jump(); },
+                ebpf::JSGT_IMM32 => if  reg[_dst] as i32  >  insn.imm  as i32      { do_jump(); },
+                ebpf::JSGT_REG32 => if  reg[_dst] as i32  >  reg[_src] as i32      { do_jump(); },
+                ebpf::JSGE_IMM32 => if  reg[_dst] as i32  >= insn.imm  as i32      { do_jump(); },
+                ebpf::JSGE_REG32 => if  reg[_dst] as i32  >= reg[_src] as i32      { do_jump(); },
+                ebpf::JSLT_IMM32 => if (reg[_dst] as i32) <  insn.imm  as i32      { do_jump(); },
+                ebpf::JSLT_REG32 => if (reg[_dst] as i32) <  reg[_src] as i32      { do_jump(); },
+                ebpf::JSLE_IMM32 => if  reg[_dst] as i32  <= insn.imm  as i32      { do_jump(); },
+                ebpf::JSLE_REG32 => if  reg[_dst] as i32  <= reg[_src] as i32      { do_jump(); },
+
                 // 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)) {

+ 25 - 0
src/verifier.rs

@@ -223,6 +223,31 @@ pub fn check(prog: &[u8]) -> Result<(), Error> {
             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)?; },
+
+            // BPF_JMP32 class
+            ebpf::JEQ_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JEQ_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JGT_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JGT_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JGE_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JGE_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JLT_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JLT_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JLE_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JLE_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSET_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSET_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JNE_IMM32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JNE_REG32  => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSGT_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSGT_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSGE_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSGE_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSLT_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSLT_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSLE_IMM32 => { check_jmp_offset(prog, insn_ptr)?; },
+            ebpf::JSLE_REG32 => { check_jmp_offset(prog, insn_ptr)?; },
+
             ebpf::CALL       => {},
             ebpf::TAIL_CALL  => { unimplemented!() },
             ebpf::EXIT       => {},

+ 423 - 0
tests/ubpf_vm.rs

@@ -851,6 +851,429 @@ fn test_vm_jslt_reg() {
     assert_eq!(vm.execute_program().unwrap(), 0x1);
 }
 
+#[test]
+fn test_vm_jeq32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0x0
+        mov32 r1, 0xa
+        jeq32 r1, 0xb, +5
+        mov32 r0, 1
+        mov r1, 0xb
+        or r1, r9
+        jeq32 r1, 0xb, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jeq32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 0xa
+        mov32 r2, 0xb
+        jeq32 r1, r2, +5
+        mov32 r0, 1
+        mov32 r1, 0xb
+        or r1, r9
+        jeq32 r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jge32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 0xa
+        jge32 r1, 0xb, +5
+        mov32 r0, 1
+        or r1, r9
+        mov32 r1, 0xc
+        jge32 r1, 0xb, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jge32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 0xa
+        mov32 r2, 0xb
+        jge32 r1, r2, +5
+        mov32 r0, 1
+        or r1, r9
+        mov32 r1, 0xc
+        jge32 r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jgt32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 5
+        or r1, r9
+        jgt32 r1, 6, +4
+        jgt32 r1, 5, +3
+        jgt32 r1, 4, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jgt32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov r0, 0
+        mov r1, 5
+        mov32 r1, 5
+        or r1, r9
+        mov r2, 6
+        mov r3, 4
+        jgt32 r1, r2, +4
+        jgt32 r1, r1, +3
+        jgt32 r1, r3, +1
+        exit
+        mov r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jle32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 5
+        or r1, r9
+        jle32 r1, 4, +5
+        jle32 r1, 6, +1
+        exit
+        jle32 r1, 5, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jle32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov r0, 0
+        mov r1, 5
+        mov r2, 4
+        mov r3, 6
+        or r1, r9
+        jle32 r1, r2, +5
+        jle32 r1, r1, +1
+        exit
+        jle32 r1, r3, +1
+        exit
+        mov r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jlt32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 5
+        or r1, r9
+        jlt32 r1, 4, +4
+        jlt32 r1, 5, +3
+        jlt32 r1, 6, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jlt32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov r0, 0
+        mov r1, 5
+        mov r2, 4
+        mov r3, 6
+        or r1, r9
+        jlt32 r1, r2, +4
+        jlt32 r1, r1, +3
+        jlt32 r1, r3, +1
+        exit
+        mov r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jne32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 0xb
+        or r1, r9
+        jne32 r1, 0xb, +4
+        mov32 r0, 1
+        mov32 r1, 0xa
+        or r1, r9
+        jne32 r1, 0xb, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jne32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 0xb
+        or r1, r9
+        mov32 r2, 0xb
+        jne32 r1, r2, +4
+        mov32 r0, 1
+        mov32 r1, 0xa
+        or r1, r9
+        jne32 r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jset32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 0x7
+        or r1, r9
+        jset32 r1, 0x8, +4
+        mov32 r0, 1
+        mov32 r1, 0x9
+        jset32 r1, 0x8, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jset32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, 0x7
+        or r1, r9
+        mov32 r2, 0x8
+        jset32 r1, r2, +4
+        mov32 r0, 1
+        mov32 r1, 0x9
+        jset32 r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jsge32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        jsge32 r1, -1, +5
+        jsge32 r1, 0, +4
+        mov32 r0, 1
+        mov r1, -1
+        jsge32 r1, -1, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jsge32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        mov r2, -1
+        mov32 r3, 0
+        jsge32 r1, r2, +5
+        jsge32 r1, r3, +4
+        mov32 r0, 1
+        mov r1, r2
+        jsge32 r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jsgt32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        jsgt32 r1, -1, +4
+        mov32 r0, 1
+        mov32 r1, 0
+        jsgt32 r1, -1, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jsgt32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        mov r2, -1
+        jsgt32 r1, r2, +4
+        mov32 r0, 1
+        mov32 r1, 0
+        jsgt32 r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jsle32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        jsle32 r1, -3, +5
+        jsle32 r1, -1, +1
+        exit
+        mov32 r0, 1
+        jsle32 r1, -2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jsle32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        mov r2, -3
+        mov32 r3, 0
+        jsle32 r1, r2, +6
+        jsle32 r1, r3, +1
+        exit
+        mov32 r0, 1
+        mov r1, r2
+        jsle32 r1, r2, +1
+        mov32 r0, 2
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jslt32_imm() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        jslt32 r1, -3, +4
+        jslt32 r1, -2, +3
+        jslt32 r1, -1, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
+#[test]
+fn test_vm_jslt32_reg() {
+    let prog = assemble("
+        mov r9, 1
+        lsh r9, 32
+        mov32 r0, 0
+        mov32 r1, -2
+        or r1, r9
+        mov r2, -3
+        mov r3, -1
+        jslt32 r1, r1, +4
+        jslt32 r1, r2, +3
+        jslt32 r1, r3, +1
+        exit
+        mov32 r0, 1
+        exit").unwrap();
+    let vm = rbpf::EbpfVmNoData::new(Some(&prog)).unwrap();
+    assert_eq!(vm.execute_program().unwrap(), 0x1);
+}
+
 #[test]
 fn test_vm_lddw() {
     let prog = assemble("lddw r0, 0x1122334455667788