From 3edd35eabbaec9d791f6c86f708f207f96d12f63 Mon Sep 17 00:00:00 2001
From: Lucas <lucas.tnagel@gmail.com>
Date: Thu, 10 Oct 2024 12:08:42 -0300
Subject: [PATCH 1/3] Modify assembler and disassembler

---
 src/assembler.rs      |  7 ++++++-
 src/disassembler.rs   | 15 ++++-----------
 src/ebpf.rs           | 10 ++++++----
 src/insn_builder.rs   |  2 +-
 src/interpreter.rs    |  3 ++-
 src/jit.rs            |  3 ++-
 tests/assembler.rs    | 13 +++++++++++++
 tests/disassembler.rs |  9 +++++++++
 8 files changed, 43 insertions(+), 19 deletions(-)

diff --git a/src/assembler.rs b/src/assembler.rs
index 46a88e65..cc4c721a 100644
--- a/src/assembler.rs
+++ b/src/assembler.rs
@@ -122,7 +122,11 @@ fn make_instruction_map(sbpf_version: SBPFVersion) -> HashMap<String, (Instructi
 
         // Miscellaneous.
         entry("ja", JumpUnconditional, ebpf::JA);
-        entry("syscall", Syscall, ebpf::CALL_IMM);
+        if sbpf_version == SBPFVersion::V1 {
+            entry("syscall", Syscall, ebpf::CALL_IMM);
+        } else {
+            entry("syscall", Syscall, ebpf::SYSCALL);
+        }
         entry("call", CallImm, ebpf::CALL_IMM);
         entry("callx", CallReg, ebpf::CALL_REG);
         entry("lddw", LoadDwImm, ebpf::LD_DW_IMM);
@@ -450,6 +454,7 @@ pub fn assemble<C: ContextObject>(
                                 0,
                                 ebpf::hash_symbol_name(label.as_bytes()) as i32 as i64,
                             ),
+                            (Syscall, [Integer(imm)]) => insn(opc, 0, 0, 0, *imm),
                             (CallImm, [Label(label)]) => {
                                 let label: &str = label;
                                 let target_pc = *labels
diff --git a/src/disassembler.rs b/src/disassembler.rs
index 94a7695c..b500b0a5 100644
--- a/src/disassembler.rs
+++ b/src/disassembler.rs
@@ -265,14 +265,7 @@ pub fn disassemble_instruction<C: ContextObject>(
         ebpf::JSLE_IMM   => { name = "jsle"; desc = jmp_imm_str(name, insn, cfg_nodes); },
         ebpf::JSLE_REG   => { name = "jsle"; desc = jmp_reg_str(name, insn, cfg_nodes); },
         ebpf::CALL_IMM   => {
-            let mut function_name = None;
-            if sbpf_version.static_syscalls() {
-                if insn.src != 0 {
-                    function_name = Some(resolve_label(cfg_nodes, insn.imm as usize).to_string());
-                }
-            } else {
-                function_name = function_registry.lookup_by_key(insn.imm as u32).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string());
-            }
+            let function_name = function_registry.lookup_by_key(insn.imm as u32).map(|(function_name, _)| String::from_utf8_lossy(function_name).to_string());
             let function_name = if let Some(function_name) = function_name {
                 name = "call";
                 function_name
@@ -284,9 +277,9 @@ pub fn disassemble_instruction<C: ContextObject>(
         },
         ebpf::CALL_REG   => { name = "callx"; desc = format!("{} r{}", name, if sbpf_version.callx_uses_src_reg() { insn.src } else { insn.imm as u8 }); },
         ebpf::EXIT
-        | ebpf::RETURN   if !sbpf_version.static_syscalls() => { name = "exit"; desc = name.to_string(); },
-        ebpf::EXIT
-        | ebpf::RETURN   if sbpf_version.static_syscalls() =>  { name = "return"; desc = name.to_string(); },
+        | ebpf::RETURN if !sbpf_version.static_syscalls() => { name = "exit"; desc = name.to_string(); },
+        ebpf::RETURN   if sbpf_version.static_syscalls() =>  { name = "return"; desc = name.to_string(); },
+        ebpf::SYSCALL  if sbpf_version.static_syscalls() =>  { desc = format!("syscall {}", insn.imm); },
 
         _                => { name = "unknown"; desc = format!("{} opcode={:#x}", name, insn.opc); },
     };
diff --git a/src/ebpf.rs b/src/ebpf.rs
index 6608452b..57d57290 100644
--- a/src/ebpf.rs
+++ b/src/ebpf.rs
@@ -195,8 +195,8 @@ pub const BPF_JSGT: u8 = 0x60;
 pub const BPF_JSGE: u8 = 0x70;
 /// BPF JMP operation code: syscall function call.
 pub const BPF_CALL: u8 = 0x80;
-/// BPF JMP operation code: return from program.
-pub const BPF_EXIT: u8 = 0x90;
+/// BPF JMP operation code: return from program (V1) or syscall (V2).
+pub const BPF_EXIT_SYSCALL: 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.
@@ -481,9 +481,11 @@ pub const CALL_IMM: u8 = BPF_JMP | BPF_CALL;
 /// BPF opcode: tail call.
 pub const CALL_REG: u8 = BPF_JMP | BPF_X | BPF_CALL;
 /// BPF opcode: `exit` /// `return r0`. /// Valid only for SBPFv1
-pub const EXIT: u8 = BPF_JMP | BPF_EXIT;
+pub const EXIT: u8 = BPF_JMP | BPF_EXIT_SYSCALL;
 /// BPF opcode: `return` /// `return r0`. /// Valid only for SBPFv2
-pub const RETURN: u8 = BPF_JMP | BPF_X | BPF_EXIT;
+pub const RETURN: u8 = BPF_JMP | BPF_X | BPF_EXIT_SYSCALL;
+/// BPF opcode: `syscall` /// `syscall imm`. /// Valid only for SBPFv2
+pub const SYSCALL: u8 = BPF_JMP | BPF_EXIT_SYSCALL;
 
 // Used in JIT
 /// Mask to extract the operation class from an operation code.
diff --git a/src/insn_builder.rs b/src/insn_builder.rs
index 638cc275..f9400762 100644
--- a/src/insn_builder.rs
+++ b/src/insn_builder.rs
@@ -616,7 +616,7 @@ impl<'i> Exit<'i> {
 
 impl Instruction for Exit<'_> {
     fn opt_code_byte(&self) -> u8 {
-        BPF_EXIT | BPF_JMP
+        BPF_EXIT_SYSCALL | BPF_JMP
     }
 
     fn get_insn_mut(&mut self) -> &mut Insn {
diff --git a/src/interpreter.rs b/src/interpreter.rs
index adc2ab79..34ea6dd8 100644
--- a/src/interpreter.rs
+++ b/src/interpreter.rs
@@ -526,7 +526,8 @@ impl<'a, 'b, C: ContextObject> Interpreter<'a, 'b, C> {
 
             // Do not delegate the check to the verifier, since self.registered functions can be
             // changed after the program has been verified.
-            ebpf::CALL_IMM   => {
+            ebpf::CALL_IMM
+            | ebpf::SYSCALL if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => {
                 let mut resolved = false;
                 let (external, internal) = if self.executable.get_sbpf_version().static_syscalls() {
                     (insn.src == 0, insn.src != 0)
diff --git a/src/jit.rs b/src/jit.rs
index cf1fd63e..d17abd19 100644
--- a/src/jit.rs
+++ b/src/jit.rs
@@ -705,7 +705,8 @@ impl<'a, C: ContextObject> JitCompiler<'a, C> {
                 ebpf::JSLT_REG   => self.emit_conditional_branch_reg(0x8c, false, src, dst, target_pc),
                 ebpf::JSLE_IMM   => self.emit_conditional_branch_imm(0x8e, false, insn.imm, dst, target_pc),
                 ebpf::JSLE_REG   => self.emit_conditional_branch_reg(0x8e, false, src, dst, target_pc),
-                ebpf::CALL_IMM   => {
+                ebpf::CALL_IMM | ebpf::SYSCALL
+                if insn.opc == ebpf::CALL_IMM || self.executable.get_sbpf_version().static_syscalls() => {
                     // For JIT, external functions MUST be registered at compile time.
 
                     let mut resolved = false;
diff --git a/tests/assembler.rs b/tests/assembler.rs
index d94c2155..66df864d 100644
--- a/tests/assembler.rs
+++ b/tests/assembler.rs
@@ -71,6 +71,19 @@ fn test_exit() {
     );
 }
 
+#[test]
+fn test_static_syscall() {
+    let config = Config {
+        enabled_sbpf_versions: SBPFVersion::V2..=SBPFVersion::V2,
+        ..Config::default()
+    };
+
+    assert_eq!(
+        asm_with_config("syscall 3", config),
+        Ok(vec![insn(0, ebpf::SYSCALL, 0, 0, 0, 3)])
+    );
+}
+
 #[test]
 fn test_return() {
     let config = Config {
diff --git a/tests/disassembler.rs b/tests/disassembler.rs
index 0740787e..e77d274f 100644
--- a/tests/disassembler.rs
+++ b/tests/disassembler.rs
@@ -60,6 +60,15 @@ fn test_return() {
     disasm!("entrypoint:\n    return\n", config);
 }
 
+#[test]
+fn test_static_syscall() {
+    let config = Config {
+        enabled_sbpf_versions: SBPFVersion::V2..=SBPFVersion::V2,
+        ..Config::default()
+    };
+    disasm!("entrypoint:\n    syscall 5\n", config);
+}
+
 // Example for InstructionType::AluBinary.
 #[test]
 fn test_add64() {

From 9596526305aec899a5c85e24852715fa5487d59e Mon Sep 17 00:00:00 2001
From: Lucas <lucas.tnagel@gmail.com>
Date: Fri, 11 Oct 2024 16:26:56 -0300
Subject: [PATCH 2/3] Rebase onto main

---
 src/verifier.rs   | 1 +
 tests/verifier.rs | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/verifier.rs b/src/verifier.rs
index e9a28024..6f46a0a3 100644
--- a/src/verifier.rs
+++ b/src/verifier.rs
@@ -392,6 +392,7 @@ impl Verifier for RequisiteVerifier {
                 ebpf::CALL_REG   => { check_callx_register(&insn, insn_ptr, sbpf_version)?; },
                 ebpf::EXIT       if !sbpf_version.static_syscalls()   => {},
                 ebpf::RETURN     if sbpf_version.static_syscalls()    => {},
+                ebpf::SYSCALL    if sbpf_version.static_syscalls()    => {},
 
                 _                => {
                     return Err(VerifierError::UnknownOpCode(insn.opc, insn_ptr));
diff --git a/tests/verifier.rs b/tests/verifier.rs
index a7cf9421..5fa54218 100644
--- a/tests/verifier.rs
+++ b/tests/verifier.rs
@@ -431,7 +431,7 @@ fn return_instr() {
     for sbpf_version in [SBPFVersion::V1, SBPFVersion::V2] {
         let prog = &[
             0xbf, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // mov64 r0, 2
-            0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit
+            0x95, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // exit (v1), syscall (v2)
             0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // return
         ];
 
@@ -444,7 +444,7 @@ fn return_instr() {
         .unwrap();
         let result = executable.verify::<RequisiteVerifier>();
         if sbpf_version == SBPFVersion::V2 {
-            assert_error!(result, "VerifierError(UnknownOpCode(149, 1))");
+            assert!(result.is_ok());
         } else {
             assert_error!(result, "VerifierError(UnknownOpCode(157, 2))");
         }

From 64c8ca5d8bd215bfd896f54977487916d8ff75e3 Mon Sep 17 00:00:00 2001
From: Lucas <lucas.tnagel@gmail.com>
Date: Fri, 11 Oct 2024 15:49:18 -0300
Subject: [PATCH 3/3] Fix naming and assembler

---
 src/assembler.rs    | 14 +++++++++-----
 src/ebpf.rs         | 12 +++++++-----
 src/insn_builder.rs |  2 +-
 3 files changed, 17 insertions(+), 11 deletions(-)

diff --git a/src/assembler.rs b/src/assembler.rs
index cc4c721a..ffdb3508 100644
--- a/src/assembler.rs
+++ b/src/assembler.rs
@@ -122,11 +122,15 @@ fn make_instruction_map(sbpf_version: SBPFVersion) -> HashMap<String, (Instructi
 
         // Miscellaneous.
         entry("ja", JumpUnconditional, ebpf::JA);
-        if sbpf_version == SBPFVersion::V1 {
-            entry("syscall", Syscall, ebpf::CALL_IMM);
-        } else {
-            entry("syscall", Syscall, ebpf::SYSCALL);
-        }
+        entry(
+            "syscall",
+            Syscall,
+            if sbpf_version == SBPFVersion::V1 {
+                ebpf::CALL_IMM
+            } else {
+                ebpf::SYSCALL
+            },
+        );
         entry("call", CallImm, ebpf::CALL_IMM);
         entry("callx", CallReg, ebpf::CALL_REG);
         entry("lddw", LoadDwImm, ebpf::LD_DW_IMM);
diff --git a/src/ebpf.rs b/src/ebpf.rs
index 57d57290..9c443092 100644
--- a/src/ebpf.rs
+++ b/src/ebpf.rs
@@ -195,8 +195,10 @@ pub const BPF_JSGT: u8 = 0x60;
 pub const BPF_JSGE: u8 = 0x70;
 /// BPF JMP operation code: syscall function call.
 pub const BPF_CALL: u8 = 0x80;
-/// BPF JMP operation code: return from program (V1) or syscall (V2).
-pub const BPF_EXIT_SYSCALL: u8 = 0x90;
+/// BPF JMP operation code: return from program (V1).
+pub const BPF_EXIT: u8 = 0x90;
+/// BPF JMP operation code: static syscall (V2).
+pub const BPF_SYSCALL: 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.
@@ -481,11 +483,11 @@ pub const CALL_IMM: u8 = BPF_JMP | BPF_CALL;
 /// BPF opcode: tail call.
 pub const CALL_REG: u8 = BPF_JMP | BPF_X | BPF_CALL;
 /// BPF opcode: `exit` /// `return r0`. /// Valid only for SBPFv1
-pub const EXIT: u8 = BPF_JMP | BPF_EXIT_SYSCALL;
+pub const EXIT: u8 = BPF_JMP | BPF_EXIT;
 /// BPF opcode: `return` /// `return r0`. /// Valid only for SBPFv2
-pub const RETURN: u8 = BPF_JMP | BPF_X | BPF_EXIT_SYSCALL;
+pub const RETURN: u8 = BPF_JMP | BPF_X | BPF_EXIT;
 /// BPF opcode: `syscall` /// `syscall imm`. /// Valid only for SBPFv2
-pub const SYSCALL: u8 = BPF_JMP | BPF_EXIT_SYSCALL;
+pub const SYSCALL: u8 = BPF_JMP | BPF_SYSCALL;
 
 // Used in JIT
 /// Mask to extract the operation class from an operation code.
diff --git a/src/insn_builder.rs b/src/insn_builder.rs
index f9400762..638cc275 100644
--- a/src/insn_builder.rs
+++ b/src/insn_builder.rs
@@ -616,7 +616,7 @@ impl<'i> Exit<'i> {
 
 impl Instruction for Exit<'_> {
     fn opt_code_byte(&self) -> u8 {
-        BPF_EXIT_SYSCALL | BPF_JMP
+        BPF_EXIT | BPF_JMP
     }
 
     fn get_insn_mut(&mut self) -> &mut Insn {