Repository: qmonnet/rbpf (Rust)

Issue:
Attempt to negate with overflow in ld_st_imm_str of disassembler
The disassembler would panic in `ld_st_imm_str` when it tries to negate the 0x8000i16 value in

https://github.com/qmonnet/rbpf/blob/4812c52fe1009ee1e3c2307662d175bdd07b5c71/src/disassembler.rs#L29-L33

The PoC program to reproduce:
```
disassembler::disassemble(&[98, 1, 0, 128, 0, 0, 31, 145])
```

The would panic the disassembler:
```
thread '<unnamed>' panicked at 'attempt to negate with overflow', /rbpf-0.2.0/src/disassembler.rs:33:56
```

To enhance the robustness of the disassembler, the negation logic of i16 could be restructured in `ld_st_imm_str` function.


Discussion:

Patch:
diff --git a/src/disassembler.rs b/src/disassembler.rs
index 1ae625b13..41fd09e86 100644
--- a/src/disassembler.rs
+++ b/src/disassembler.rs
@@ -30,7 +30,7 @@ fn ld_st_imm_str(name: &str, insn: &ebpf::Insn) -> String {
     if insn.off >= 0 {
         format!("{name} [r{}+{:#x}], {:#x}", insn.dst, insn.off, insn.imm)
     } else {
-        format!("{name} [r{}-{:#x}], {:#x}", insn.dst, -insn.off, insn.imm)
+        format!("{name} [r{}-{:#x}], {:#x}", insn.dst, -(insn.off as isize), insn.imm)
     }
 }
 
@@ -39,7 +39,7 @@ fn ld_reg_str(name: &str, insn: &ebpf::Insn) -> String {
     if insn.off >= 0 {
         format!("{name} r{}, [r{}+{:#x}]", insn.dst, insn.src, insn.off)
     } else {
-        format!("{name} r{}, [r{}-{:#x}]", insn.dst, insn.src, -insn.off)
+        format!("{name} r{}, [r{}-{:#x}]", insn.dst, insn.src, -(insn.off as isize))
     }
 }
 
@@ -48,7 +48,7 @@ fn st_reg_str(name: &str, insn: &ebpf::Insn) -> String {
     if insn.off >= 0 {
         format!("{name} [r{}+{:#x}], r{}", insn.dst, insn.off, insn.src)
     } else {
-        format!("{name} [r{}-{:#x}], r{}", insn.dst, -insn.off, insn.src)
+        format!("{name} [r{}-{:#x}], r{}", insn.dst, -(insn.off as isize), insn.src)
     }
 }
 
@@ -67,7 +67,7 @@ fn jmp_imm_str(name: &str, insn: &ebpf::Insn) -> String {
     if insn.off >= 0 {
         format!("{name} r{}, {:#x}, +{:#x}", insn.dst, insn.imm, insn.off)
     } else {
-        format!("{name} r{}, {:#x}, -{:#x}", insn.dst, insn.imm, -insn.off)
+        format!("{name} r{}, {:#x}, -{:#x}", insn.dst, insn.imm, -(insn.off as isize))
     }
 }
 
@@ -76,7 +76,7 @@ fn jmp_reg_str(name: &str, insn: &ebpf::Insn) -> String {
     if insn.off >= 0 {
         format!("{name} r{}, r{}, +{:#x}", insn.dst, insn.src, insn.off)
     } else {
-        format!("{name} r{}, r{}, -{:#x}", insn.dst, insn.src, -insn.off)
+        format!("{name} r{}, r{}, -{:#x}", insn.dst, insn.src, -(insn.off as isize))
     }
 }
 


Test patch:
diff --git a/tests/disassembler.rs b/tests/disassembler.rs
index e42592827..3d1503b69 100644
--- a/tests/disassembler.rs
+++ b/tests/disassembler.rs
@@ -303,3 +303,40 @@ fn test_large_immediate() {
     disasm!("add64 r1, 0x7fffffff");
     disasm!("add64 r1, 0x7fffffff");
 }
+
+// Non-regression tests for overflow when trying to negate offset 0x8000i16.
+#[test]
+fn test_offset_overflow() {
+    let insns = [
+        0x62, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stw
+        0x6a, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // sth
+        0x72, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stb
+        0x7a, 0x01, 0x00, 0x80, 0x01, 0x00, 0x00, 0x00, // stdw
+        0x61, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxw
+        0x69, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxh
+        0x71, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxb
+        0x79, 0x01, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // ldxdw
+        0x15, 0x01, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, // jeq (imm)
+        0x1d, 0x21, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // jeq (reg)
+        0x16, 0x01, 0x00, 0x80, 0x02, 0x00, 0x00, 0x00, // jeq32 (imm)
+        0x1e, 0x21, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, // jeq32 (reg)
+    ];
+
+    let expected_output = "stw [r1-0x8000], 0x1
+sth [r1-0x8000], 0x1
+stb [r1-0x8000], 0x1
+stdw [r1-0x8000], 0x1
+ldxw r1, [r0-0x8000]
+ldxh r1, [r0-0x8000]
+ldxb r1, [r0-0x8000]
+ldxdw r1, [r0-0x8000]
+jeq r1, 0x2, -0x8000
+jeq r1, r2, -0x8000
+jeq32 r1, 0x2, -0x8000
+jeq32 r1, r2, -0x8000";
+
+    let prog = to_insn_vec(&insns);
+    let asm = prog.into_iter().map(|ins| ins.desc).collect::<Vec<_>>().join("\n");
+
+    assert_eq!(asm, expected_output);
+}