diff --git a/src/assembler.rs b/src/assembler.rs index 46a88e65c..cc4c721af 100644 --- a/src/assembler.rs +++ b/src/assembler.rs @@ -122,7 +122,11 @@ fn make_instruction_map(sbpf_version: SBPFVersion) -> HashMap( 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 94a7695cb..b500b0a56 100644 --- a/src/disassembler.rs +++ b/src/disassembler.rs @@ -265,14 +265,7 @@ pub fn disassemble_instruction( 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( }, 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 6608452b4..57d572909 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 638cc275d..f94007620 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 8d1dee5c6..849e960c6 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 78fd1a027..097c18a8e 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 d94c21553..66df864d9 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 0740787e7..e77d274fe 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() {